From da668aa15b99150a8595c491aee00d5d2426aaf9 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 10 Mar 2021 07:33:14 +0100 Subject: tests: Move unit tests into a separate directory The main tests directory still looks very crowded, and it's not clear which files are part of a unit tests and which belong to a different test subsystem. Let's clean up the mess and move the unit tests to a separate directory. Message-Id: <20210310063314.1049838-1-thuth@redhat.com> Acked-by: Paolo Bonzini Signed-off-by: Thomas Huth --- tests/check-block-qdict.c | 712 --------- tests/check-qdict.c | 379 ----- tests/check-qjson.c | 1518 ------------------ tests/check-qlist.c | 103 -- tests/check-qlit.c | 101 -- tests/check-qnull.c | 78 - tests/check-qnum.c | 175 --- tests/check-qobject.c | 334 ---- tests/check-qom-interface.c | 103 -- tests/check-qom-proplist.c | 638 -------- tests/check-qstring.c | 82 - tests/crypto-tls-psk-helpers.c | 50 - tests/crypto-tls-psk-helpers.h | 33 - tests/crypto-tls-x509-helpers.c | 508 ------ tests/crypto-tls-x509-helpers.h | 132 -- tests/io-channel-helpers.c | 163 -- tests/io-channel-helpers.h | 41 - tests/iothread.c | 127 -- tests/iothread.h | 25 - tests/meson.build | 182 +-- tests/pkix_asn1_tab.c | 1108 -------------- tests/ptimer-test-stubs.c | 124 -- tests/ptimer-test.c | 892 ----------- tests/ptimer-test.h | 22 - tests/rcutorture.c | 483 ------ tests/socket-helpers.c | 157 -- tests/socket-helpers.h | 35 - tests/test-aio-multithread.c | 460 ------ tests/test-aio.c | 921 ----------- tests/test-authz-list.c | 160 -- tests/test-authz-listfile.c | 196 --- tests/test-authz-pam.c | 133 -- tests/test-authz-simple.c | 51 - tests/test-base64.c | 108 -- tests/test-bdrv-drain.c | 2230 --------------------------- tests/test-bdrv-graph-mod.c | 200 --- tests/test-bitcnt.c | 140 -- tests/test-bitmap.c | 138 -- tests/test-bitops.c | 146 -- tests/test-block-backend.c | 85 -- tests/test-block-iothread.c | 774 ---------- tests/test-blockjob-txn.c | 262 ---- tests/test-blockjob.c | 393 ----- tests/test-bufferiszero.c | 78 - tests/test-char.c | 1560 ------------------- tests/test-clone-visitor.c | 199 --- tests/test-coroutine.c | 512 ------- tests/test-crypto-afsplit.c | 194 --- tests/test-crypto-block.c | 368 ----- tests/test-crypto-cipher.c | 801 ---------- tests/test-crypto-hash.c | 255 ---- tests/test-crypto-hmac.c | 266 ---- tests/test-crypto-ivgen.c | 174 --- tests/test-crypto-pbkdf.c | 446 ------ tests/test-crypto-secret.c | 614 -------- tests/test-crypto-tlscredsx509.c | 718 --------- tests/test-crypto-tlssession.c | 660 -------- tests/test-crypto-xts.c | 529 ------- tests/test-cutils.c | 2460 ------------------------------ tests/test-fdmon-epoll.c | 73 - tests/test-hbitmap.c | 1119 -------------- tests/test-image-locking.c | 158 -- tests/test-int128.c | 223 --- tests/test-io-channel-buffer.c | 52 - tests/test-io-channel-command.c | 130 -- tests/test-io-channel-file.c | 155 -- tests/test-io-channel-socket.c | 603 -------- tests/test-io-channel-tls.c | 346 ----- tests/test-io-task.c | 268 ---- tests/test-iov.c | 581 ------- tests/test-keyval.c | 750 --------- tests/test-logging.c | 218 --- tests/test-mul64.c | 68 - tests/test-opts-visitor.c | 371 ----- tests/test-qapi-util.c | 78 - tests/test-qdev-global-props.c | 319 ---- tests/test-qdist.c | 389 ----- tests/test-qemu-opts.c | 1046 ------------- tests/test-qga.c | 1019 ------------- tests/test-qgraph.c | 434 ------ tests/test-qht.c | 260 ---- tests/test-qmp-cmds.c | 359 ----- tests/test-qmp-event.c | 154 -- tests/test-qobject-input-visitor.c | 1379 ----------------- tests/test-qobject-output-visitor.c | 807 ---------- tests/test-rcu-list.c | 381 ----- tests/test-rcu-simpleq.c | 2 - tests/test-rcu-slist.c | 2 - tests/test-rcu-tailq.c | 2 - tests/test-replication.c | 623 -------- tests/test-shift128.c | 139 -- tests/test-string-input-visitor.c | 501 ------ tests/test-string-output-visitor.c | 248 --- tests/test-thread-pool.c | 250 --- tests/test-throttle.c | 770 ---------- tests/test-timed-average.c | 89 -- tests/test-util-filemonitor.c | 720 --------- tests/test-util-sockets.c | 379 ----- tests/test-uuid.c | 184 --- tests/test-visitor-serialization.c | 1116 -------------- tests/test-vmstate.c | 1529 ------------------- tests/test-write-threshold.c | 123 -- tests/test-x86-cpuid.c | 138 -- tests/test-xbzrle.c | 191 --- tests/unit/check-block-qdict.c | 712 +++++++++ tests/unit/check-qdict.c | 379 +++++ tests/unit/check-qjson.c | 1518 ++++++++++++++++++ tests/unit/check-qlist.c | 103 ++ tests/unit/check-qlit.c | 101 ++ tests/unit/check-qnull.c | 78 + tests/unit/check-qnum.c | 175 +++ tests/unit/check-qobject.c | 334 ++++ tests/unit/check-qom-interface.c | 103 ++ tests/unit/check-qom-proplist.c | 638 ++++++++ tests/unit/check-qstring.c | 82 + tests/unit/crypto-tls-psk-helpers.c | 50 + tests/unit/crypto-tls-psk-helpers.h | 33 + tests/unit/crypto-tls-x509-helpers.c | 508 ++++++ tests/unit/crypto-tls-x509-helpers.h | 132 ++ tests/unit/io-channel-helpers.c | 163 ++ tests/unit/io-channel-helpers.h | 41 + tests/unit/iothread.c | 127 ++ tests/unit/iothread.h | 25 + tests/unit/meson.build | 184 +++ tests/unit/pkix_asn1_tab.c | 1108 ++++++++++++++ tests/unit/ptimer-test-stubs.c | 124 ++ tests/unit/ptimer-test.c | 892 +++++++++++ tests/unit/ptimer-test.h | 22 + tests/unit/rcutorture.c | 483 ++++++ tests/unit/socket-helpers.c | 157 ++ tests/unit/socket-helpers.h | 35 + tests/unit/test-aio-multithread.c | 460 ++++++ tests/unit/test-aio.c | 921 +++++++++++ tests/unit/test-authz-list.c | 160 ++ tests/unit/test-authz-listfile.c | 196 +++ tests/unit/test-authz-pam.c | 133 ++ tests/unit/test-authz-simple.c | 51 + tests/unit/test-base64.c | 108 ++ tests/unit/test-bdrv-drain.c | 2230 +++++++++++++++++++++++++++ tests/unit/test-bdrv-graph-mod.c | 200 +++ tests/unit/test-bitcnt.c | 140 ++ tests/unit/test-bitmap.c | 138 ++ tests/unit/test-bitops.c | 146 ++ tests/unit/test-block-backend.c | 85 ++ tests/unit/test-block-iothread.c | 774 ++++++++++ tests/unit/test-blockjob-txn.c | 262 ++++ tests/unit/test-blockjob.c | 393 +++++ tests/unit/test-bufferiszero.c | 78 + tests/unit/test-char.c | 1560 +++++++++++++++++++ tests/unit/test-clone-visitor.c | 199 +++ tests/unit/test-coroutine.c | 512 +++++++ tests/unit/test-crypto-afsplit.c | 194 +++ tests/unit/test-crypto-block.c | 368 +++++ tests/unit/test-crypto-cipher.c | 801 ++++++++++ tests/unit/test-crypto-hash.c | 255 ++++ tests/unit/test-crypto-hmac.c | 266 ++++ tests/unit/test-crypto-ivgen.c | 174 +++ tests/unit/test-crypto-pbkdf.c | 446 ++++++ tests/unit/test-crypto-secret.c | 614 ++++++++ tests/unit/test-crypto-tlscredsx509.c | 718 +++++++++ tests/unit/test-crypto-tlssession.c | 660 ++++++++ tests/unit/test-crypto-xts.c | 529 +++++++ tests/unit/test-cutils.c | 2460 ++++++++++++++++++++++++++++++ tests/unit/test-fdmon-epoll.c | 73 + tests/unit/test-hbitmap.c | 1119 ++++++++++++++ tests/unit/test-image-locking.c | 158 ++ tests/unit/test-int128.c | 223 +++ tests/unit/test-io-channel-buffer.c | 52 + tests/unit/test-io-channel-command.c | 130 ++ tests/unit/test-io-channel-file.c | 155 ++ tests/unit/test-io-channel-socket.c | 603 ++++++++ tests/unit/test-io-channel-tls.c | 346 +++++ tests/unit/test-io-task.c | 268 ++++ tests/unit/test-iov.c | 581 +++++++ tests/unit/test-keyval.c | 750 +++++++++ tests/unit/test-logging.c | 218 +++ tests/unit/test-mul64.c | 68 + tests/unit/test-opts-visitor.c | 371 +++++ tests/unit/test-qapi-util.c | 78 + tests/unit/test-qdev-global-props.c | 319 ++++ tests/unit/test-qdist.c | 389 +++++ tests/unit/test-qemu-opts.c | 1046 +++++++++++++ tests/unit/test-qga.c | 1019 +++++++++++++ tests/unit/test-qgraph.c | 434 ++++++ tests/unit/test-qht.c | 260 ++++ tests/unit/test-qmp-cmds.c | 359 +++++ tests/unit/test-qmp-event.c | 154 ++ tests/unit/test-qobject-input-visitor.c | 1379 +++++++++++++++++ tests/unit/test-qobject-output-visitor.c | 807 ++++++++++ tests/unit/test-rcu-list.c | 381 +++++ tests/unit/test-rcu-simpleq.c | 2 + tests/unit/test-rcu-slist.c | 2 + tests/unit/test-rcu-tailq.c | 2 + tests/unit/test-replication.c | 623 ++++++++ tests/unit/test-shift128.c | 139 ++ tests/unit/test-string-input-visitor.c | 501 ++++++ tests/unit/test-string-output-visitor.c | 248 +++ tests/unit/test-thread-pool.c | 250 +++ tests/unit/test-throttle.c | 770 ++++++++++ tests/unit/test-timed-average.c | 89 ++ tests/unit/test-util-filemonitor.c | 720 +++++++++ tests/unit/test-util-sockets.c | 379 +++++ tests/unit/test-uuid.c | 184 +++ tests/unit/test-visitor-serialization.c | 1116 ++++++++++++++ tests/unit/test-vmstate.c | 1529 +++++++++++++++++++ tests/unit/test-write-threshold.c | 123 ++ tests/unit/test-x86-cpuid.c | 138 ++ tests/unit/test-xbzrle.c | 191 +++ 208 files changed, 43285 insertions(+), 43281 deletions(-) delete mode 100644 tests/check-block-qdict.c delete mode 100644 tests/check-qdict.c delete mode 100644 tests/check-qjson.c delete mode 100644 tests/check-qlist.c delete mode 100644 tests/check-qlit.c delete mode 100644 tests/check-qnull.c delete mode 100644 tests/check-qnum.c delete mode 100644 tests/check-qobject.c delete mode 100644 tests/check-qom-interface.c delete mode 100644 tests/check-qom-proplist.c delete mode 100644 tests/check-qstring.c delete mode 100644 tests/crypto-tls-psk-helpers.c delete mode 100644 tests/crypto-tls-psk-helpers.h delete mode 100644 tests/crypto-tls-x509-helpers.c delete mode 100644 tests/crypto-tls-x509-helpers.h delete mode 100644 tests/io-channel-helpers.c delete mode 100644 tests/io-channel-helpers.h delete mode 100644 tests/iothread.c delete mode 100644 tests/iothread.h delete mode 100644 tests/pkix_asn1_tab.c delete mode 100644 tests/ptimer-test-stubs.c delete mode 100644 tests/ptimer-test.c delete mode 100644 tests/ptimer-test.h delete mode 100644 tests/rcutorture.c delete mode 100644 tests/socket-helpers.c delete mode 100644 tests/socket-helpers.h delete mode 100644 tests/test-aio-multithread.c delete mode 100644 tests/test-aio.c delete mode 100644 tests/test-authz-list.c delete mode 100644 tests/test-authz-listfile.c delete mode 100644 tests/test-authz-pam.c delete mode 100644 tests/test-authz-simple.c delete mode 100644 tests/test-base64.c delete mode 100644 tests/test-bdrv-drain.c delete mode 100644 tests/test-bdrv-graph-mod.c delete mode 100644 tests/test-bitcnt.c delete mode 100644 tests/test-bitmap.c delete mode 100644 tests/test-bitops.c delete mode 100644 tests/test-block-backend.c delete mode 100644 tests/test-block-iothread.c delete mode 100644 tests/test-blockjob-txn.c delete mode 100644 tests/test-blockjob.c delete mode 100644 tests/test-bufferiszero.c delete mode 100644 tests/test-char.c delete mode 100644 tests/test-clone-visitor.c delete mode 100644 tests/test-coroutine.c delete mode 100644 tests/test-crypto-afsplit.c delete mode 100644 tests/test-crypto-block.c delete mode 100644 tests/test-crypto-cipher.c delete mode 100644 tests/test-crypto-hash.c delete mode 100644 tests/test-crypto-hmac.c delete mode 100644 tests/test-crypto-ivgen.c delete mode 100644 tests/test-crypto-pbkdf.c delete mode 100644 tests/test-crypto-secret.c delete mode 100644 tests/test-crypto-tlscredsx509.c delete mode 100644 tests/test-crypto-tlssession.c delete mode 100644 tests/test-crypto-xts.c delete mode 100644 tests/test-cutils.c delete mode 100644 tests/test-fdmon-epoll.c delete mode 100644 tests/test-hbitmap.c delete mode 100644 tests/test-image-locking.c delete mode 100644 tests/test-int128.c delete mode 100644 tests/test-io-channel-buffer.c delete mode 100644 tests/test-io-channel-command.c delete mode 100644 tests/test-io-channel-file.c delete mode 100644 tests/test-io-channel-socket.c delete mode 100644 tests/test-io-channel-tls.c delete mode 100644 tests/test-io-task.c delete mode 100644 tests/test-iov.c delete mode 100644 tests/test-keyval.c delete mode 100644 tests/test-logging.c delete mode 100644 tests/test-mul64.c delete mode 100644 tests/test-opts-visitor.c delete mode 100644 tests/test-qapi-util.c delete mode 100644 tests/test-qdev-global-props.c delete mode 100644 tests/test-qdist.c delete mode 100644 tests/test-qemu-opts.c delete mode 100644 tests/test-qga.c delete mode 100644 tests/test-qgraph.c delete mode 100644 tests/test-qht.c delete mode 100644 tests/test-qmp-cmds.c delete mode 100644 tests/test-qmp-event.c delete mode 100644 tests/test-qobject-input-visitor.c delete mode 100644 tests/test-qobject-output-visitor.c delete mode 100644 tests/test-rcu-list.c delete mode 100644 tests/test-rcu-simpleq.c delete mode 100644 tests/test-rcu-slist.c delete mode 100644 tests/test-rcu-tailq.c delete mode 100644 tests/test-replication.c delete mode 100644 tests/test-shift128.c delete mode 100644 tests/test-string-input-visitor.c delete mode 100644 tests/test-string-output-visitor.c delete mode 100644 tests/test-thread-pool.c delete mode 100644 tests/test-throttle.c delete mode 100644 tests/test-timed-average.c delete mode 100644 tests/test-util-filemonitor.c delete mode 100644 tests/test-util-sockets.c delete mode 100644 tests/test-uuid.c delete mode 100644 tests/test-visitor-serialization.c delete mode 100644 tests/test-vmstate.c delete mode 100644 tests/test-write-threshold.c delete mode 100644 tests/test-x86-cpuid.c delete mode 100644 tests/test-xbzrle.c create mode 100644 tests/unit/check-block-qdict.c create mode 100644 tests/unit/check-qdict.c create mode 100644 tests/unit/check-qjson.c create mode 100644 tests/unit/check-qlist.c create mode 100644 tests/unit/check-qlit.c create mode 100644 tests/unit/check-qnull.c create mode 100644 tests/unit/check-qnum.c create mode 100644 tests/unit/check-qobject.c create mode 100644 tests/unit/check-qom-interface.c create mode 100644 tests/unit/check-qom-proplist.c create mode 100644 tests/unit/check-qstring.c create mode 100644 tests/unit/crypto-tls-psk-helpers.c create mode 100644 tests/unit/crypto-tls-psk-helpers.h create mode 100644 tests/unit/crypto-tls-x509-helpers.c create mode 100644 tests/unit/crypto-tls-x509-helpers.h create mode 100644 tests/unit/io-channel-helpers.c create mode 100644 tests/unit/io-channel-helpers.h create mode 100644 tests/unit/iothread.c create mode 100644 tests/unit/iothread.h create mode 100644 tests/unit/meson.build create mode 100644 tests/unit/pkix_asn1_tab.c create mode 100644 tests/unit/ptimer-test-stubs.c create mode 100644 tests/unit/ptimer-test.c create mode 100644 tests/unit/ptimer-test.h create mode 100644 tests/unit/rcutorture.c create mode 100644 tests/unit/socket-helpers.c create mode 100644 tests/unit/socket-helpers.h create mode 100644 tests/unit/test-aio-multithread.c create mode 100644 tests/unit/test-aio.c create mode 100644 tests/unit/test-authz-list.c create mode 100644 tests/unit/test-authz-listfile.c create mode 100644 tests/unit/test-authz-pam.c create mode 100644 tests/unit/test-authz-simple.c create mode 100644 tests/unit/test-base64.c create mode 100644 tests/unit/test-bdrv-drain.c create mode 100644 tests/unit/test-bdrv-graph-mod.c create mode 100644 tests/unit/test-bitcnt.c create mode 100644 tests/unit/test-bitmap.c create mode 100644 tests/unit/test-bitops.c create mode 100644 tests/unit/test-block-backend.c create mode 100644 tests/unit/test-block-iothread.c create mode 100644 tests/unit/test-blockjob-txn.c create mode 100644 tests/unit/test-blockjob.c create mode 100644 tests/unit/test-bufferiszero.c create mode 100644 tests/unit/test-char.c create mode 100644 tests/unit/test-clone-visitor.c create mode 100644 tests/unit/test-coroutine.c create mode 100644 tests/unit/test-crypto-afsplit.c create mode 100644 tests/unit/test-crypto-block.c create mode 100644 tests/unit/test-crypto-cipher.c create mode 100644 tests/unit/test-crypto-hash.c create mode 100644 tests/unit/test-crypto-hmac.c create mode 100644 tests/unit/test-crypto-ivgen.c create mode 100644 tests/unit/test-crypto-pbkdf.c create mode 100644 tests/unit/test-crypto-secret.c create mode 100644 tests/unit/test-crypto-tlscredsx509.c create mode 100644 tests/unit/test-crypto-tlssession.c create mode 100644 tests/unit/test-crypto-xts.c create mode 100644 tests/unit/test-cutils.c create mode 100644 tests/unit/test-fdmon-epoll.c create mode 100644 tests/unit/test-hbitmap.c create mode 100644 tests/unit/test-image-locking.c create mode 100644 tests/unit/test-int128.c create mode 100644 tests/unit/test-io-channel-buffer.c create mode 100644 tests/unit/test-io-channel-command.c create mode 100644 tests/unit/test-io-channel-file.c create mode 100644 tests/unit/test-io-channel-socket.c create mode 100644 tests/unit/test-io-channel-tls.c create mode 100644 tests/unit/test-io-task.c create mode 100644 tests/unit/test-iov.c create mode 100644 tests/unit/test-keyval.c create mode 100644 tests/unit/test-logging.c create mode 100644 tests/unit/test-mul64.c create mode 100644 tests/unit/test-opts-visitor.c create mode 100644 tests/unit/test-qapi-util.c create mode 100644 tests/unit/test-qdev-global-props.c create mode 100644 tests/unit/test-qdist.c create mode 100644 tests/unit/test-qemu-opts.c create mode 100644 tests/unit/test-qga.c create mode 100644 tests/unit/test-qgraph.c create mode 100644 tests/unit/test-qht.c create mode 100644 tests/unit/test-qmp-cmds.c create mode 100644 tests/unit/test-qmp-event.c create mode 100644 tests/unit/test-qobject-input-visitor.c create mode 100644 tests/unit/test-qobject-output-visitor.c create mode 100644 tests/unit/test-rcu-list.c create mode 100644 tests/unit/test-rcu-simpleq.c create mode 100644 tests/unit/test-rcu-slist.c create mode 100644 tests/unit/test-rcu-tailq.c create mode 100644 tests/unit/test-replication.c create mode 100644 tests/unit/test-shift128.c create mode 100644 tests/unit/test-string-input-visitor.c create mode 100644 tests/unit/test-string-output-visitor.c create mode 100644 tests/unit/test-thread-pool.c create mode 100644 tests/unit/test-throttle.c create mode 100644 tests/unit/test-timed-average.c create mode 100644 tests/unit/test-util-filemonitor.c create mode 100644 tests/unit/test-util-sockets.c create mode 100644 tests/unit/test-uuid.c create mode 100644 tests/unit/test-visitor-serialization.c create mode 100644 tests/unit/test-vmstate.c create mode 100644 tests/unit/test-write-threshold.c create mode 100644 tests/unit/test-x86-cpuid.c create mode 100644 tests/unit/test-xbzrle.c (limited to 'tests') diff --git a/tests/check-block-qdict.c b/tests/check-block-qdict.c deleted file mode 100644 index 5a25825093..0000000000 --- a/tests/check-block-qdict.c +++ /dev/null @@ -1,712 +0,0 @@ -/* - * Unit-tests for Block layer QDict extras - * - * Copyright (c) 2013-2018 Red Hat, Inc. - * - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "block/qdict.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qnum.h" -#include "qapi/error.h" - -static void qdict_defaults_test(void) -{ - QDict *dict, *copy; - - dict = qdict_new(); - copy = qdict_new(); - - qdict_set_default_str(dict, "foo", "abc"); - qdict_set_default_str(dict, "foo", "def"); - g_assert_cmpstr(qdict_get_str(dict, "foo"), ==, "abc"); - qdict_set_default_str(dict, "bar", "ghi"); - - qdict_copy_default(copy, dict, "foo"); - g_assert_cmpstr(qdict_get_str(copy, "foo"), ==, "abc"); - qdict_set_default_str(copy, "bar", "xyz"); - qdict_copy_default(copy, dict, "bar"); - g_assert_cmpstr(qdict_get_str(copy, "bar"), ==, "xyz"); - - qobject_unref(copy); - qobject_unref(dict); -} - -static void qdict_flatten_test(void) -{ - QList *e_1 = qlist_new(); - QList *e = qlist_new(); - QDict *e_1_2 = qdict_new(); - QDict *f = qdict_new(); - QList *y = qlist_new(); - QDict *z = qdict_new(); - QDict *root = qdict_new(); - - /* - * Test the flattening of - * - * { - * "e": [ - * 42, - * [ - * 23, - * 66, - * { - * "a": 0, - * "b": 1 - * } - * ] - * ], - * "f": { - * "c": 2, - * "d": 3, - * }, - * "g": 4, - * "y": [{}], - * "z": {"a": []} - * } - * - * to - * - * { - * "e.0": 42, - * "e.1.0": 23, - * "e.1.1": 66, - * "e.1.2.a": 0, - * "e.1.2.b": 1, - * "f.c": 2, - * "f.d": 3, - * "g": 4, - * "y.0": {}, - * "z.a": [] - * } - */ - - qdict_put_int(e_1_2, "a", 0); - qdict_put_int(e_1_2, "b", 1); - - qlist_append_int(e_1, 23); - qlist_append_int(e_1, 66); - qlist_append(e_1, e_1_2); - qlist_append_int(e, 42); - qlist_append(e, e_1); - - qdict_put_int(f, "c", 2); - qdict_put_int(f, "d", 3); - - qlist_append(y, qdict_new()); - - qdict_put(z, "a", qlist_new()); - - qdict_put(root, "e", e); - qdict_put(root, "f", f); - qdict_put_int(root, "g", 4); - qdict_put(root, "y", y); - qdict_put(root, "z", z); - - qdict_flatten(root); - - g_assert(qdict_get_int(root, "e.0") == 42); - g_assert(qdict_get_int(root, "e.1.0") == 23); - g_assert(qdict_get_int(root, "e.1.1") == 66); - g_assert(qdict_get_int(root, "e.1.2.a") == 0); - g_assert(qdict_get_int(root, "e.1.2.b") == 1); - g_assert(qdict_get_int(root, "f.c") == 2); - g_assert(qdict_get_int(root, "f.d") == 3); - g_assert(qdict_get_int(root, "g") == 4); - g_assert(!qdict_size(qdict_get_qdict(root, "y.0"))); - g_assert(qlist_empty(qdict_get_qlist(root, "z.a"))); - - g_assert(qdict_size(root) == 10); - - qobject_unref(root); -} - -static void qdict_clone_flatten_test(void) -{ - QDict *dict1 = qdict_new(); - QDict *dict2 = qdict_new(); - QDict *cloned_dict1; - - /* - * Test that we can clone and flatten - * { "a": { "b": 42 } } - * without modifying the clone. - */ - - qdict_put_int(dict2, "b", 42); - qdict_put(dict1, "a", dict2); - - cloned_dict1 = qdict_clone_shallow(dict1); - - qdict_flatten(dict1); - - g_assert(qdict_size(dict1) == 1); - g_assert(qdict_get_int(dict1, "a.b") == 42); - - g_assert(qdict_size(cloned_dict1) == 1); - g_assert(qdict_get_qdict(cloned_dict1, "a") == dict2); - - g_assert(qdict_size(dict2) == 1); - g_assert(qdict_get_int(dict2, "b") == 42); - - qobject_unref(dict1); - qobject_unref(cloned_dict1); -} - -static void qdict_array_split_test(void) -{ - QDict *test_dict = qdict_new(); - QDict *dict1, *dict2; - QNum *int1; - QList *test_list; - - /* - * Test the split of - * - * { - * "1.x": 0, - * "4.y": 1, - * "0.a": 42, - * "o.o": 7, - * "0.b": 23, - * "2": 66 - * } - * - * to - * - * [ - * { - * "a": 42, - * "b": 23 - * }, - * { - * "x": 0 - * }, - * 66 - * ] - * - * and - * - * { - * "4.y": 1, - * "o.o": 7 - * } - * - * (remaining in the old QDict) - * - * This example is given in the comment of qdict_array_split(). - */ - - qdict_put_int(test_dict, "1.x", 0); - qdict_put_int(test_dict, "4.y", 1); - qdict_put_int(test_dict, "0.a", 42); - qdict_put_int(test_dict, "o.o", 7); - qdict_put_int(test_dict, "0.b", 23); - qdict_put_int(test_dict, "2", 66); - - qdict_array_split(test_dict, &test_list); - - dict1 = qobject_to(QDict, qlist_pop(test_list)); - dict2 = qobject_to(QDict, qlist_pop(test_list)); - int1 = qobject_to(QNum, qlist_pop(test_list)); - - g_assert(dict1); - g_assert(dict2); - g_assert(int1); - g_assert(qlist_empty(test_list)); - - qobject_unref(test_list); - - g_assert(qdict_get_int(dict1, "a") == 42); - g_assert(qdict_get_int(dict1, "b") == 23); - - g_assert(qdict_size(dict1) == 2); - - qobject_unref(dict1); - - g_assert(qdict_get_int(dict2, "x") == 0); - - g_assert(qdict_size(dict2) == 1); - - qobject_unref(dict2); - - g_assert_cmpint(qnum_get_int(int1), ==, 66); - - qobject_unref(int1); - - g_assert(qdict_get_int(test_dict, "4.y") == 1); - g_assert(qdict_get_int(test_dict, "o.o") == 7); - - g_assert(qdict_size(test_dict) == 2); - - qobject_unref(test_dict); - - /* - * Test the split of - * - * { - * "0": 42, - * "1": 23, - * "1.x": 84 - * } - * - * to - * - * [ - * 42 - * ] - * - * and - * - * { - * "1": 23, - * "1.x": 84 - * } - * - * That is, test whether splitting stops if there is both an entry with key - * of "%u" and other entries with keys prefixed "%u." for the same index. - */ - - test_dict = qdict_new(); - - qdict_put_int(test_dict, "0", 42); - qdict_put_int(test_dict, "1", 23); - qdict_put_int(test_dict, "1.x", 84); - - qdict_array_split(test_dict, &test_list); - - int1 = qobject_to(QNum, qlist_pop(test_list)); - - g_assert(int1); - g_assert(qlist_empty(test_list)); - - qobject_unref(test_list); - - g_assert_cmpint(qnum_get_int(int1), ==, 42); - - qobject_unref(int1); - - g_assert(qdict_get_int(test_dict, "1") == 23); - g_assert(qdict_get_int(test_dict, "1.x") == 84); - - g_assert(qdict_size(test_dict) == 2); - - qobject_unref(test_dict); -} - -static void qdict_array_entries_test(void) -{ - QDict *dict = qdict_new(); - - g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 0); - - qdict_put_int(dict, "bar", 0); - qdict_put_int(dict, "baz.0", 0); - g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 0); - - qdict_put_int(dict, "foo.1", 0); - g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, -EINVAL); - qdict_put_int(dict, "foo.0", 0); - g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 2); - qdict_put_int(dict, "foo.bar", 0); - g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, -EINVAL); - qdict_del(dict, "foo.bar"); - - qdict_put_int(dict, "foo.2.a", 0); - qdict_put_int(dict, "foo.2.b", 0); - qdict_put_int(dict, "foo.2.c", 0); - g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 3); - g_assert_cmpint(qdict_array_entries(dict, ""), ==, -EINVAL); - - qobject_unref(dict); - - dict = qdict_new(); - qdict_put_int(dict, "1", 0); - g_assert_cmpint(qdict_array_entries(dict, ""), ==, -EINVAL); - qdict_put_int(dict, "0", 0); - g_assert_cmpint(qdict_array_entries(dict, ""), ==, 2); - qdict_put_int(dict, "bar", 0); - g_assert_cmpint(qdict_array_entries(dict, ""), ==, -EINVAL); - qdict_del(dict, "bar"); - - qdict_put_int(dict, "2.a", 0); - qdict_put_int(dict, "2.b", 0); - qdict_put_int(dict, "2.c", 0); - g_assert_cmpint(qdict_array_entries(dict, ""), ==, 3); - - qobject_unref(dict); -} - -static void qdict_join_test(void) -{ - QDict *dict1, *dict2; - bool overwrite = false; - int i; - - dict1 = qdict_new(); - dict2 = qdict_new(); - - /* Test everything once without overwrite and once with */ - do { - /* Test empty dicts */ - qdict_join(dict1, dict2, overwrite); - - g_assert(qdict_size(dict1) == 0); - g_assert(qdict_size(dict2) == 0); - - /* First iteration: Test movement */ - /* Second iteration: Test empty source and non-empty destination */ - qdict_put_int(dict2, "foo", 42); - - for (i = 0; i < 2; i++) { - qdict_join(dict1, dict2, overwrite); - - g_assert(qdict_size(dict1) == 1); - g_assert(qdict_size(dict2) == 0); - - g_assert(qdict_get_int(dict1, "foo") == 42); - } - - /* Test non-empty source and destination without conflict */ - qdict_put_int(dict2, "bar", 23); - - qdict_join(dict1, dict2, overwrite); - - g_assert(qdict_size(dict1) == 2); - g_assert(qdict_size(dict2) == 0); - - g_assert(qdict_get_int(dict1, "foo") == 42); - g_assert(qdict_get_int(dict1, "bar") == 23); - - /* Test conflict */ - qdict_put_int(dict2, "foo", 84); - - qdict_join(dict1, dict2, overwrite); - - g_assert(qdict_size(dict1) == 2); - g_assert(qdict_size(dict2) == !overwrite); - - g_assert(qdict_get_int(dict1, "foo") == (overwrite ? 84 : 42)); - g_assert(qdict_get_int(dict1, "bar") == 23); - - if (!overwrite) { - g_assert(qdict_get_int(dict2, "foo") == 84); - } - - /* Check the references */ - g_assert(qdict_get(dict1, "foo")->base.refcnt == 1); - g_assert(qdict_get(dict1, "bar")->base.refcnt == 1); - - if (!overwrite) { - g_assert(qdict_get(dict2, "foo")->base.refcnt == 1); - } - - /* Clean up */ - qdict_del(dict1, "foo"); - qdict_del(dict1, "bar"); - - if (!overwrite) { - qdict_del(dict2, "foo"); - } - } while (overwrite ^= true); - - qobject_unref(dict1); - qobject_unref(dict2); -} - -static void qdict_crumple_test_recursive(void) -{ - QDict *src, *dst, *rule, *vnc, *acl, *listen; - QDict *empty, *empty_dict, *empty_list_0; - QList *rules, *empty_list, *empty_dict_a; - - src = qdict_new(); - qdict_put_str(src, "vnc.listen.addr", "127.0.0.1"); - qdict_put_str(src, "vnc.listen.port", "5901"); - qdict_put_str(src, "vnc.acl.rules.0.match", "fred"); - qdict_put_str(src, "vnc.acl.rules.0.policy", "allow"); - qdict_put_str(src, "vnc.acl.rules.1.match", "bob"); - qdict_put_str(src, "vnc.acl.rules.1.policy", "deny"); - qdict_put_str(src, "vnc.acl.default", "deny"); - qdict_put_str(src, "vnc.acl..name", "acl0"); - qdict_put_str(src, "vnc.acl.rule..name", "acl0"); - qdict_put(src, "empty.dict.a", qlist_new()); - qdict_put(src, "empty.list.0", qdict_new()); - - dst = qobject_to(QDict, qdict_crumple(src, &error_abort)); - g_assert(dst); - g_assert_cmpint(qdict_size(dst), ==, 2); - - vnc = qdict_get_qdict(dst, "vnc"); - g_assert(vnc); - g_assert_cmpint(qdict_size(vnc), ==, 3); - - listen = qdict_get_qdict(vnc, "listen"); - g_assert(listen); - g_assert_cmpint(qdict_size(listen), ==, 2); - g_assert_cmpstr("127.0.0.1", ==, qdict_get_str(listen, "addr")); - g_assert_cmpstr("5901", ==, qdict_get_str(listen, "port")); - - acl = qdict_get_qdict(vnc, "acl"); - g_assert(acl); - g_assert_cmpint(qdict_size(acl), ==, 3); - - rules = qdict_get_qlist(acl, "rules"); - g_assert(rules); - g_assert_cmpint(qlist_size(rules), ==, 2); - - rule = qobject_to(QDict, qlist_pop(rules)); - g_assert(rule); - g_assert_cmpint(qdict_size(rule), ==, 2); - g_assert_cmpstr("fred", ==, qdict_get_str(rule, "match")); - g_assert_cmpstr("allow", ==, qdict_get_str(rule, "policy")); - qobject_unref(rule); - - rule = qobject_to(QDict, qlist_pop(rules)); - g_assert(rule); - g_assert_cmpint(qdict_size(rule), ==, 2); - g_assert_cmpstr("bob", ==, qdict_get_str(rule, "match")); - g_assert_cmpstr("deny", ==, qdict_get_str(rule, "policy")); - qobject_unref(rule); - - /* With recursive crumpling, we should see all names unescaped */ - g_assert_cmpstr("acl0", ==, qdict_get_str(vnc, "acl.name")); - g_assert_cmpstr("acl0", ==, qdict_get_str(acl, "rule.name")); - - empty = qdict_get_qdict(dst, "empty"); - g_assert(empty); - g_assert_cmpint(qdict_size(empty), ==, 2); - empty_dict = qdict_get_qdict(empty, "dict"); - g_assert(empty_dict); - g_assert_cmpint(qdict_size(empty_dict), ==, 1); - empty_dict_a = qdict_get_qlist(empty_dict, "a"); - g_assert(empty_dict_a && qlist_empty(empty_dict_a)); - empty_list = qdict_get_qlist(empty, "list"); - g_assert(empty_list); - g_assert_cmpint(qlist_size(empty_list), ==, 1); - empty_list_0 = qobject_to(QDict, qlist_pop(empty_list)); - g_assert(empty_list_0); - g_assert_cmpint(qdict_size(empty_list_0), ==, 0); - qobject_unref(empty_list_0); - - qobject_unref(src); - qobject_unref(dst); -} - -static void qdict_crumple_test_empty(void) -{ - QDict *src, *dst; - - src = qdict_new(); - - dst = qobject_to(QDict, qdict_crumple(src, &error_abort)); - - g_assert_cmpint(qdict_size(dst), ==, 0); - - qobject_unref(src); - qobject_unref(dst); -} - -static int qdict_count_entries(QDict *dict) -{ - const QDictEntry *e; - int count = 0; - - for (e = qdict_first(dict); e; e = qdict_next(dict, e)) { - count++; - } - - return count; -} - -static void qdict_rename_keys_test(void) -{ - QDict *dict = qdict_new(); - QDict *copy; - QDictRenames *renames; - Error *local_err = NULL; - - qdict_put_str(dict, "abc", "foo"); - qdict_put_str(dict, "abcdef", "bar"); - qdict_put_int(dict, "number", 42); - qdict_put_bool(dict, "flag", true); - qdict_put_null(dict, "nothing"); - - /* Empty rename list */ - renames = (QDictRenames[]) { - { NULL, "this can be anything" } - }; - copy = qdict_clone_shallow(dict); - qdict_rename_keys(copy, renames, &error_abort); - - g_assert_cmpstr(qdict_get_str(copy, "abc"), ==, "foo"); - g_assert_cmpstr(qdict_get_str(copy, "abcdef"), ==, "bar"); - g_assert_cmpint(qdict_get_int(copy, "number"), ==, 42); - g_assert_cmpint(qdict_get_bool(copy, "flag"), ==, true); - g_assert(qobject_type(qdict_get(copy, "nothing")) == QTYPE_QNULL); - g_assert_cmpint(qdict_count_entries(copy), ==, 5); - - qobject_unref(copy); - - /* Simple rename of all entries */ - renames = (QDictRenames[]) { - { "abc", "str1" }, - { "abcdef", "str2" }, - { "number", "int" }, - { "flag", "bool" }, - { "nothing", "null" }, - { NULL , NULL } - }; - copy = qdict_clone_shallow(dict); - qdict_rename_keys(copy, renames, &error_abort); - - g_assert(!qdict_haskey(copy, "abc")); - g_assert(!qdict_haskey(copy, "abcdef")); - g_assert(!qdict_haskey(copy, "number")); - g_assert(!qdict_haskey(copy, "flag")); - g_assert(!qdict_haskey(copy, "nothing")); - - g_assert_cmpstr(qdict_get_str(copy, "str1"), ==, "foo"); - g_assert_cmpstr(qdict_get_str(copy, "str2"), ==, "bar"); - g_assert_cmpint(qdict_get_int(copy, "int"), ==, 42); - g_assert_cmpint(qdict_get_bool(copy, "bool"), ==, true); - g_assert(qobject_type(qdict_get(copy, "null")) == QTYPE_QNULL); - g_assert_cmpint(qdict_count_entries(copy), ==, 5); - - qobject_unref(copy); - - /* Renames are processed top to bottom */ - renames = (QDictRenames[]) { - { "abc", "tmp" }, - { "abcdef", "abc" }, - { "number", "abcdef" }, - { "flag", "number" }, - { "nothing", "flag" }, - { "tmp", "nothing" }, - { NULL , NULL } - }; - copy = qdict_clone_shallow(dict); - qdict_rename_keys(copy, renames, &error_abort); - - g_assert_cmpstr(qdict_get_str(copy, "nothing"), ==, "foo"); - g_assert_cmpstr(qdict_get_str(copy, "abc"), ==, "bar"); - g_assert_cmpint(qdict_get_int(copy, "abcdef"), ==, 42); - g_assert_cmpint(qdict_get_bool(copy, "number"), ==, true); - g_assert(qobject_type(qdict_get(copy, "flag")) == QTYPE_QNULL); - g_assert(!qdict_haskey(copy, "tmp")); - g_assert_cmpint(qdict_count_entries(copy), ==, 5); - - qobject_unref(copy); - - /* Conflicting rename */ - renames = (QDictRenames[]) { - { "abcdef", "abc" }, - { NULL , NULL } - }; - copy = qdict_clone_shallow(dict); - qdict_rename_keys(copy, renames, &local_err); - - error_free_or_abort(&local_err); - - g_assert_cmpstr(qdict_get_str(copy, "abc"), ==, "foo"); - g_assert_cmpstr(qdict_get_str(copy, "abcdef"), ==, "bar"); - g_assert_cmpint(qdict_get_int(copy, "number"), ==, 42); - g_assert_cmpint(qdict_get_bool(copy, "flag"), ==, true); - g_assert(qobject_type(qdict_get(copy, "nothing")) == QTYPE_QNULL); - g_assert_cmpint(qdict_count_entries(copy), ==, 5); - - qobject_unref(copy); - - /* Renames in an empty dict */ - renames = (QDictRenames[]) { - { "abcdef", "abc" }, - { NULL , NULL } - }; - - qobject_unref(dict); - dict = qdict_new(); - - qdict_rename_keys(dict, renames, &error_abort); - g_assert(qdict_first(dict) == NULL); - - qobject_unref(dict); -} - -static void qdict_crumple_test_bad_inputs(void) -{ - QDict *src, *nested; - Error *error = NULL; - - src = qdict_new(); - /* rule.0 can't be both a string and a dict */ - qdict_put_str(src, "rule.0", "fred"); - qdict_put_str(src, "rule.0.policy", "allow"); - - g_assert(qdict_crumple(src, &error) == NULL); - error_free_or_abort(&error); - qobject_unref(src); - - src = qdict_new(); - /* rule can't be both a list and a dict */ - qdict_put_str(src, "rule.0", "fred"); - qdict_put_str(src, "rule.a", "allow"); - - g_assert(qdict_crumple(src, &error) == NULL); - error_free_or_abort(&error); - qobject_unref(src); - - src = qdict_new(); - /* The input should be flat, ie no dicts or lists */ - nested = qdict_new(); - qdict_put(nested, "x", qdict_new()); - qdict_put(src, "rule.a", nested); - qdict_put_str(src, "rule.b", "allow"); - - g_assert(qdict_crumple(src, &error) == NULL); - error_free_or_abort(&error); - qobject_unref(src); - - src = qdict_new(); - /* List indexes must not have gaps */ - qdict_put_str(src, "rule.0", "deny"); - qdict_put_str(src, "rule.3", "allow"); - - g_assert(qdict_crumple(src, &error) == NULL); - error_free_or_abort(&error); - qobject_unref(src); - - src = qdict_new(); - /* List indexes must be in %zu format */ - qdict_put_str(src, "rule.0", "deny"); - qdict_put_str(src, "rule.+1", "allow"); - - g_assert(qdict_crumple(src, &error) == NULL); - error_free_or_abort(&error); - qobject_unref(src); -} - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - - g_test_add_func("/public/defaults", qdict_defaults_test); - g_test_add_func("/public/flatten", qdict_flatten_test); - g_test_add_func("/public/clone_flatten", qdict_clone_flatten_test); - g_test_add_func("/public/array_split", qdict_array_split_test); - g_test_add_func("/public/array_entries", qdict_array_entries_test); - g_test_add_func("/public/join", qdict_join_test); - g_test_add_func("/public/crumple/recursive", - qdict_crumple_test_recursive); - g_test_add_func("/public/crumple/empty", - qdict_crumple_test_empty); - g_test_add_func("/public/crumple/bad_inputs", - qdict_crumple_test_bad_inputs); - - g_test_add_func("/public/rename_keys", qdict_rename_keys_test); - - return g_test_run(); -} diff --git a/tests/check-qdict.c b/tests/check-qdict.c deleted file mode 100644 index b5efa859b0..0000000000 --- a/tests/check-qdict.c +++ /dev/null @@ -1,379 +0,0 @@ -/* - * QDict unit-tests. - * - * Copyright (C) 2009 Red Hat Inc. - * - * Authors: - * Luiz Capitulino - * - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qstring.h" - -/* - * Public Interface test-cases - * - * (with some violations to access 'private' data) - */ - -static void qdict_new_test(void) -{ - QDict *qdict; - - qdict = qdict_new(); - g_assert(qdict != NULL); - g_assert(qdict_size(qdict) == 0); - g_assert(qdict->base.refcnt == 1); - g_assert(qobject_type(QOBJECT(qdict)) == QTYPE_QDICT); - - qobject_unref(qdict); -} - -static void qdict_put_obj_test(void) -{ - QNum *qn; - QDict *qdict; - QDictEntry *ent; - const int num = 42; - - qdict = qdict_new(); - - // key "" will have tdb hash 12345 - qdict_put_int(qdict, "", num); - - g_assert(qdict_size(qdict) == 1); - ent = QLIST_FIRST(&qdict->table[12345 % QDICT_BUCKET_MAX]); - qn = qobject_to(QNum, ent->value); - g_assert_cmpint(qnum_get_int(qn), ==, num); - - qobject_unref(qdict); -} - -static void qdict_destroy_simple_test(void) -{ - QDict *qdict; - - qdict = qdict_new(); - qdict_put_int(qdict, "num", 0); - qdict_put_str(qdict, "str", "foo"); - - qobject_unref(qdict); -} - -static void qdict_get_test(void) -{ - QNum *qn; - QObject *obj; - const int value = -42; - const char *key = "test"; - QDict *tests_dict = qdict_new(); - - qdict_put_int(tests_dict, key, value); - - obj = qdict_get(tests_dict, key); - g_assert(obj != NULL); - - qn = qobject_to(QNum, obj); - g_assert_cmpint(qnum_get_int(qn), ==, value); - - qobject_unref(tests_dict); -} - -static void qdict_get_int_test(void) -{ - int ret; - const int value = 100; - const char *key = "int"; - QDict *tests_dict = qdict_new(); - - qdict_put_int(tests_dict, key, value); - - ret = qdict_get_int(tests_dict, key); - g_assert(ret == value); - - qobject_unref(tests_dict); -} - -static void qdict_get_try_int_test(void) -{ - int ret; - const int value = 100; - const char *key = "int"; - QDict *tests_dict = qdict_new(); - - qdict_put_int(tests_dict, key, value); - qdict_put_str(tests_dict, "string", "test"); - - ret = qdict_get_try_int(tests_dict, key, 0); - g_assert(ret == value); - - ret = qdict_get_try_int(tests_dict, "missing", -42); - g_assert_cmpuint(ret, ==, -42); - - ret = qdict_get_try_int(tests_dict, "string", -42); - g_assert_cmpuint(ret, ==, -42); - - qobject_unref(tests_dict); -} - -static void qdict_get_str_test(void) -{ - const char *p; - const char *key = "key"; - const char *str = "string"; - QDict *tests_dict = qdict_new(); - - qdict_put_str(tests_dict, key, str); - - p = qdict_get_str(tests_dict, key); - g_assert(p != NULL); - g_assert(strcmp(p, str) == 0); - - qobject_unref(tests_dict); -} - -static void qdict_get_try_str_test(void) -{ - const char *p; - const char *key = "key"; - const char *str = "string"; - QDict *tests_dict = qdict_new(); - - qdict_put_str(tests_dict, key, str); - - p = qdict_get_try_str(tests_dict, key); - g_assert(p != NULL); - g_assert(strcmp(p, str) == 0); - - qobject_unref(tests_dict); -} - -static void qdict_haskey_not_test(void) -{ - QDict *tests_dict = qdict_new(); - g_assert(qdict_haskey(tests_dict, "test") == 0); - - qobject_unref(tests_dict); -} - -static void qdict_haskey_test(void) -{ - const char *key = "test"; - QDict *tests_dict = qdict_new(); - - qdict_put_int(tests_dict, key, 0); - g_assert(qdict_haskey(tests_dict, key) == 1); - - qobject_unref(tests_dict); -} - -static void qdict_del_test(void) -{ - const char *key = "key test"; - QDict *tests_dict = qdict_new(); - - qdict_put_str(tests_dict, key, "foo"); - g_assert(qdict_size(tests_dict) == 1); - - qdict_del(tests_dict, key); - - g_assert(qdict_size(tests_dict) == 0); - g_assert(qdict_haskey(tests_dict, key) == 0); - - qobject_unref(tests_dict); -} - -static void qobject_to_qdict_test(void) -{ - QDict *tests_dict = qdict_new(); - g_assert(qobject_to(QDict, QOBJECT(tests_dict)) == tests_dict); - - qobject_unref(tests_dict); -} - -static void qdict_iterapi_test(void) -{ - int count; - const QDictEntry *ent; - QDict *tests_dict = qdict_new(); - - g_assert(qdict_first(tests_dict) == NULL); - - qdict_put_int(tests_dict, "key1", 1); - qdict_put_int(tests_dict, "key2", 2); - qdict_put_int(tests_dict, "key3", 3); - - count = 0; - for (ent = qdict_first(tests_dict); ent; ent = qdict_next(tests_dict, ent)){ - g_assert(qdict_haskey(tests_dict, qdict_entry_key(ent)) == 1); - count++; - } - - g_assert(count == qdict_size(tests_dict)); - - /* Do it again to test restarting */ - count = 0; - for (ent = qdict_first(tests_dict); ent; ent = qdict_next(tests_dict, ent)){ - g_assert(qdict_haskey(tests_dict, qdict_entry_key(ent)) == 1); - count++; - } - - g_assert(count == qdict_size(tests_dict)); - - qobject_unref(tests_dict); -} - -/* - * Errors test-cases - */ - -static void qdict_put_exists_test(void) -{ - int value; - const char *key = "exists"; - QDict *tests_dict = qdict_new(); - - qdict_put_int(tests_dict, key, 1); - qdict_put_int(tests_dict, key, 2); - - value = qdict_get_int(tests_dict, key); - g_assert(value == 2); - - g_assert(qdict_size(tests_dict) == 1); - - qobject_unref(tests_dict); -} - -static void qdict_get_not_exists_test(void) -{ - QDict *tests_dict = qdict_new(); - g_assert(qdict_get(tests_dict, "foo") == NULL); - - qobject_unref(tests_dict); -} - -/* - * Stress test-case - * - * This is a lot big for a unit-test, but there is no other place - * to have it. - */ - -static void remove_dots(char *string) -{ - char *p = strchr(string, ':'); - if (p) - *p = '\0'; -} - -static QString *read_line(FILE *file, char *key) -{ - char value[128]; - - if (fscanf(file, "%127s%127s", key, value) == EOF) { - return NULL; - } - remove_dots(key); - return qstring_from_str(value); -} - -#define reset_file(file) fseek(file, 0L, SEEK_SET) - -static void qdict_stress_test(void) -{ - size_t lines; - char key[128]; - FILE *test_file; - QDict *qdict; - QString *value; - const char *test_file_path = "tests/data/qobject/qdict.txt"; - - test_file = fopen(test_file_path, "r"); - g_assert(test_file != NULL); - - // Create the dict - qdict = qdict_new(); - g_assert(qdict != NULL); - - // Add everything from the test file - for (lines = 0;; lines++) { - value = read_line(test_file, key); - if (!value) - break; - - qdict_put(qdict, key, value); - } - g_assert(qdict_size(qdict) == lines); - - // Check if everything is really in there - reset_file(test_file); - for (;;) { - const char *str1, *str2; - - value = read_line(test_file, key); - if (!value) - break; - - str1 = qstring_get_str(value); - - str2 = qdict_get_str(qdict, key); - g_assert(str2 != NULL); - - g_assert(strcmp(str1, str2) == 0); - - qobject_unref(value); - } - - // Delete everything - reset_file(test_file); - for (;;) { - value = read_line(test_file, key); - if (!value) - break; - - qdict_del(qdict, key); - qobject_unref(value); - - g_assert(qdict_haskey(qdict, key) == 0); - } - fclose(test_file); - - g_assert(qdict_size(qdict) == 0); - qobject_unref(qdict); -} - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - - g_test_add_func("/public/new", qdict_new_test); - g_test_add_func("/public/put_obj", qdict_put_obj_test); - g_test_add_func("/public/destroy_simple", qdict_destroy_simple_test); - - /* Continue, but now with fixtures */ - g_test_add_func("/public/get", qdict_get_test); - g_test_add_func("/public/get_int", qdict_get_int_test); - g_test_add_func("/public/get_try_int", qdict_get_try_int_test); - g_test_add_func("/public/get_str", qdict_get_str_test); - g_test_add_func("/public/get_try_str", qdict_get_try_str_test); - g_test_add_func("/public/haskey_not", qdict_haskey_not_test); - g_test_add_func("/public/haskey", qdict_haskey_test); - g_test_add_func("/public/del", qdict_del_test); - g_test_add_func("/public/to_qdict", qobject_to_qdict_test); - g_test_add_func("/public/iterapi", qdict_iterapi_test); - - g_test_add_func("/errors/put_exists", qdict_put_exists_test); - g_test_add_func("/errors/get_not_exists", qdict_get_not_exists_test); - - /* The Big one */ - if (g_test_slow()) { - g_test_add_func("/stress/test", qdict_stress_test); - } - - return g_test_run(); -} diff --git a/tests/check-qjson.c b/tests/check-qjson.c deleted file mode 100644 index c845f91d43..0000000000 --- a/tests/check-qjson.c +++ /dev/null @@ -1,1518 +0,0 @@ -/* - * Copyright IBM, Corp. 2009 - * Copyright (c) 2013, 2015 Red Hat Inc. - * - * Authors: - * Anthony Liguori - * Markus Armbruster - * - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - * - */ - -#include "qemu/osdep.h" - -#include "qapi/error.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qjson.h" -#include "qapi/qmp/qlit.h" -#include "qapi/qmp/qnull.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qstring.h" -#include "qemu/unicode.h" -#include "qemu-common.h" - -static QString *from_json_str(const char *jstr, bool single, Error **errp) -{ - char quote = single ? '\'' : '"'; - char *qjstr = g_strdup_printf("%c%s%c", quote, jstr, quote); - QString *ret = qobject_to(QString, qobject_from_json(qjstr, errp)); - - g_free(qjstr); - return ret; -} - -static char *to_json_str(QString *str) -{ - GString *json = qobject_to_json(QOBJECT(str)); - - if (!json) { - return NULL; - } - /* peel off double quotes */ - g_string_truncate(json, json->len - 1); - g_string_erase(json, 0, 1); - return g_string_free(json, false); -} - -static void escaped_string(void) -{ - struct { - /* Content of JSON string to parse with qobject_from_json() */ - const char *json_in; - /* Expected parse output; to unparse with qobject_to_json() */ - const char *utf8_out; - int skip; - } test_cases[] = { - { "\\b\\f\\n\\r\\t\\\\\\\"", "\b\f\n\r\t\\\"" }, - { "\\/\\'", "/'", .skip = 1 }, - { "single byte utf-8 \\u0020", "single byte utf-8 ", .skip = 1 }, - { "double byte utf-8 \\u00A2", "double byte utf-8 \xc2\xa2" }, - { "triple byte utf-8 \\u20AC", "triple byte utf-8 \xe2\x82\xac" }, - { "quadruple byte utf-8 \\uD834\\uDD1E", /* U+1D11E */ - "quadruple byte utf-8 \xF0\x9D\x84\x9E" }, - { "\\", NULL }, - { "\\z", NULL }, - { "\\ux", NULL }, - { "\\u1x", NULL }, - { "\\u12x", NULL }, - { "\\u123x", NULL }, - { "\\u12345", "\341\210\2645" }, - { "\\u0000x", "\xC0\x80x" }, - { "unpaired leading surrogate \\uD800", NULL }, - { "unpaired leading surrogate \\uD800\\uCAFE", NULL }, - { "unpaired leading surrogate \\uD800\\uD801\\uDC02", NULL }, - { "unpaired trailing surrogate \\uDC00", NULL }, - { "backward surrogate pair \\uDC00\\uD800", NULL }, - { "noncharacter U+FDD0 \\uFDD0", NULL }, - { "noncharacter U+FDEF \\uFDEF", NULL }, - { "noncharacter U+1FFFE \\uD87F\\uDFFE", NULL }, - { "noncharacter U+10FFFF \\uDC3F\\uDFFF", NULL }, - {} - }; - int i, j; - QString *cstr; - char *jstr; - - for (i = 0; test_cases[i].json_in; i++) { - for (j = 0; j < 2; j++) { - if (test_cases[i].utf8_out) { - cstr = from_json_str(test_cases[i].json_in, j, &error_abort); - g_assert_cmpstr(qstring_get_str(cstr), - ==, test_cases[i].utf8_out); - if (!test_cases[i].skip) { - jstr = to_json_str(cstr); - g_assert_cmpstr(jstr, ==, test_cases[i].json_in); - g_free(jstr); - } - qobject_unref(cstr); - } else { - cstr = from_json_str(test_cases[i].json_in, j, NULL); - g_assert(!cstr); - } - } - } -} - -static void string_with_quotes(void) -{ - const char *test_cases[] = { - "\"the bee's knees\"", - "'double quote \"'", - NULL - }; - int i; - QString *str; - char *cstr; - - for (i = 0; test_cases[i]; i++) { - str = qobject_to(QString, - qobject_from_json(test_cases[i], &error_abort)); - g_assert(str); - cstr = g_strndup(test_cases[i] + 1, strlen(test_cases[i]) - 2); - g_assert_cmpstr(qstring_get_str(str), ==, cstr); - g_free(cstr); - qobject_unref(str); - } -} - -static void utf8_string(void) -{ - /* - * Most test cases are scraped from Markus Kuhn's UTF-8 decoder - * capability and stress test at - * http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt - */ - static const struct { - /* Content of JSON string to parse with qobject_from_json() */ - const char *json_in; - /* Expected parse output */ - const char *utf8_out; - /* Expected unparse output, defaults to @json_in */ - const char *json_out; - } test_cases[] = { - /* 0 Control characters */ - { - /* - * Note: \x00 is impossible, other representations of - * U+0000 are covered under 4.3 - */ - "\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" - "\x10\x11\x12\x13\x14\x15\x16\x17" - "\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F", - NULL, - "\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007" - "\\b\\t\\n\\u000B\\f\\r\\u000E\\u000F" - "\\u0010\\u0011\\u0012\\u0013\\u0014\\u0015\\u0016\\u0017" - "\\u0018\\u0019\\u001A\\u001B\\u001C\\u001D\\u001E\\u001F", - }, - /* 1 Some correct UTF-8 text */ - { - /* a bit of German */ - "Falsches \xC3\x9C" "ben von Xylophonmusik qu\xC3\xA4lt" - " jeden gr\xC3\xB6\xC3\x9F" "eren Zwerg.", - "Falsches \xC3\x9C" "ben von Xylophonmusik qu\xC3\xA4lt" - " jeden gr\xC3\xB6\xC3\x9F" "eren Zwerg.", - "Falsches \\u00DCben von Xylophonmusik qu\\u00E4lt" - " jeden gr\\u00F6\\u00DFeren Zwerg.", - }, - { - /* a bit of Greek */ - "\xCE\xBA\xE1\xBD\xB9\xCF\x83\xCE\xBC\xCE\xB5", - "\xCE\xBA\xE1\xBD\xB9\xCF\x83\xCE\xBC\xCE\xB5", - "\\u03BA\\u1F79\\u03C3\\u03BC\\u03B5", - }, - /* '%' character when not interpolating */ - { - "100%", - "100%", - }, - /* 2 Boundary condition test cases */ - /* 2.1 First possible sequence of a certain length */ - /* - * 2.1.1 1 byte U+0020 - * Control characters are already covered by their own test - * case under 0. Test the first 1 byte non-control character - * here. - */ - { - " ", - " ", - }, - /* 2.1.2 2 bytes U+0080 */ - { - "\xC2\x80", - "\xC2\x80", - "\\u0080", - }, - /* 2.1.3 3 bytes U+0800 */ - { - "\xE0\xA0\x80", - "\xE0\xA0\x80", - "\\u0800", - }, - /* 2.1.4 4 bytes U+10000 */ - { - "\xF0\x90\x80\x80", - "\xF0\x90\x80\x80", - "\\uD800\\uDC00", - }, - /* 2.1.5 5 bytes U+200000 */ - { - "\xF8\x88\x80\x80\x80", - NULL, - "\\uFFFD", - }, - /* 2.1.6 6 bytes U+4000000 */ - { - "\xFC\x84\x80\x80\x80\x80", - NULL, - "\\uFFFD", - }, - /* 2.2 Last possible sequence of a certain length */ - /* 2.2.1 1 byte U+007F */ - { - "\x7F", - "\x7F", - "\\u007F", - }, - /* 2.2.2 2 bytes U+07FF */ - { - "\xDF\xBF", - "\xDF\xBF", - "\\u07FF", - }, - /* - * 2.2.3 3 bytes U+FFFC - * The last possible sequence is actually U+FFFF. But that's - * a noncharacter, and already covered by its own test case - * under 5.3. Same for U+FFFE. U+FFFD is the last character - * in the BMP, and covered under 2.3. Because of U+FFFD's - * special role as replacement character, it's worth testing - * U+FFFC here. - */ - { - "\xEF\xBF\xBC", - "\xEF\xBF\xBC", - "\\uFFFC", - }, - /* 2.2.4 4 bytes U+1FFFFF */ - { - "\xF7\xBF\xBF\xBF", - NULL, - "\\uFFFD", - }, - /* 2.2.5 5 bytes U+3FFFFFF */ - { - "\xFB\xBF\xBF\xBF\xBF", - NULL, - "\\uFFFD", - }, - /* 2.2.6 6 bytes U+7FFFFFFF */ - { - "\xFD\xBF\xBF\xBF\xBF\xBF", - NULL, - "\\uFFFD", - }, - /* 2.3 Other boundary conditions */ - { - /* last one before surrogate range: U+D7FF */ - "\xED\x9F\xBF", - "\xED\x9F\xBF", - "\\uD7FF", - }, - { - /* first one after surrogate range: U+E000 */ - "\xEE\x80\x80", - "\xEE\x80\x80", - "\\uE000", - }, - { - /* last one in BMP: U+FFFD */ - "\xEF\xBF\xBD", - "\xEF\xBF\xBD", - "\\uFFFD", - }, - { - /* last one in last plane: U+10FFFD */ - "\xF4\x8F\xBF\xBD", - "\xF4\x8F\xBF\xBD", - "\\uDBFF\\uDFFD" - }, - { - /* first one beyond Unicode range: U+110000 */ - "\xF4\x90\x80\x80", - NULL, - "\\uFFFD", - }, - /* 3 Malformed sequences */ - /* 3.1 Unexpected continuation bytes */ - /* 3.1.1 First continuation byte */ - { - "\x80", - NULL, - "\\uFFFD", - }, - /* 3.1.2 Last continuation byte */ - { - "\xBF", - NULL, - "\\uFFFD", - }, - /* 3.1.3 2 continuation bytes */ - { - "\x80\xBF", - NULL, - "\\uFFFD\\uFFFD", - }, - /* 3.1.4 3 continuation bytes */ - { - "\x80\xBF\x80", - NULL, - "\\uFFFD\\uFFFD\\uFFFD", - }, - /* 3.1.5 4 continuation bytes */ - { - "\x80\xBF\x80\xBF", - NULL, - "\\uFFFD\\uFFFD\\uFFFD\\uFFFD", - }, - /* 3.1.6 5 continuation bytes */ - { - "\x80\xBF\x80\xBF\x80", - NULL, - "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD", - }, - /* 3.1.7 6 continuation bytes */ - { - "\x80\xBF\x80\xBF\x80\xBF", - NULL, - "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD", - }, - /* 3.1.8 7 continuation bytes */ - { - "\x80\xBF\x80\xBF\x80\xBF\x80", - NULL, - "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD", - }, - /* 3.1.9 Sequence of all 64 possible continuation bytes */ - { - "\x80\x81\x82\x83\x84\x85\x86\x87" - "\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F" - "\x90\x91\x92\x93\x94\x95\x96\x97" - "\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F" - "\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7" - "\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF" - "\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7" - "\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF", - NULL, - "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD" - "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD" - "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD" - "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD" - "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD" - "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD" - "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD" - "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD", - }, - /* 3.2 Lonely start characters */ - /* 3.2.1 All 32 first bytes of 2-byte sequences, followed by space */ - { - "\xC0 \xC1 \xC2 \xC3 \xC4 \xC5 \xC6 \xC7 " - "\xC8 \xC9 \xCA \xCB \xCC \xCD \xCE \xCF " - "\xD0 \xD1 \xD2 \xD3 \xD4 \xD5 \xD6 \xD7 " - "\xD8 \xD9 \xDA \xDB \xDC \xDD \xDE \xDF ", - NULL, - "\\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD " - "\\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD " - "\\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD " - "\\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD ", - }, - /* 3.2.2 All 16 first bytes of 3-byte sequences, followed by space */ - { - "\xE0 \xE1 \xE2 \xE3 \xE4 \xE5 \xE6 \xE7 " - "\xE8 \xE9 \xEA \xEB \xEC \xED \xEE \xEF ", - NULL, - "\\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD " - "\\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD ", - }, - /* 3.2.3 All 8 first bytes of 4-byte sequences, followed by space */ - { - "\xF0 \xF1 \xF2 \xF3 \xF4 \xF5 \xF6 \xF7 ", - NULL, - "\\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD ", - }, - /* 3.2.4 All 4 first bytes of 5-byte sequences, followed by space */ - { - "\xF8 \xF9 \xFA \xFB ", - NULL, - "\\uFFFD \\uFFFD \\uFFFD \\uFFFD ", - }, - /* 3.2.5 All 2 first bytes of 6-byte sequences, followed by space */ - { - "\xFC \xFD ", - NULL, - "\\uFFFD \\uFFFD ", - }, - /* 3.3 Sequences with last continuation byte missing */ - /* 3.3.1 2-byte sequence with last byte missing (U+0000) */ - { - "\xC0", - NULL, - "\\uFFFD", - }, - /* 3.3.2 3-byte sequence with last byte missing (U+0000) */ - { - "\xE0\x80", - NULL, - "\\uFFFD", - }, - /* 3.3.3 4-byte sequence with last byte missing (U+0000) */ - { - "\xF0\x80\x80", - NULL, - "\\uFFFD", - }, - /* 3.3.4 5-byte sequence with last byte missing (U+0000) */ - { - "\xF8\x80\x80\x80", - NULL, - "\\uFFFD", - }, - /* 3.3.5 6-byte sequence with last byte missing (U+0000) */ - { - "\xFC\x80\x80\x80\x80", - NULL, - "\\uFFFD", - }, - /* 3.3.6 2-byte sequence with last byte missing (U+07FF) */ - { - "\xDF", - NULL, - "\\uFFFD", - }, - /* 3.3.7 3-byte sequence with last byte missing (U+FFFF) */ - { - "\xEF\xBF", - NULL, - "\\uFFFD", - }, - /* 3.3.8 4-byte sequence with last byte missing (U+1FFFFF) */ - { - "\xF7\xBF\xBF", - NULL, - "\\uFFFD", - }, - /* 3.3.9 5-byte sequence with last byte missing (U+3FFFFFF) */ - { - "\xFB\xBF\xBF\xBF", - NULL, - "\\uFFFD", - }, - /* 3.3.10 6-byte sequence with last byte missing (U+7FFFFFFF) */ - { - "\xFD\xBF\xBF\xBF\xBF", - NULL, - "\\uFFFD", - }, - /* 3.4 Concatenation of incomplete sequences */ - { - "\xC0\xE0\x80\xF0\x80\x80\xF8\x80\x80\x80\xFC\x80\x80\x80\x80" - "\xDF\xEF\xBF\xF7\xBF\xBF\xFB\xBF\xBF\xBF\xFD\xBF\xBF\xBF\xBF", - NULL, - "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD" - "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD", - }, - /* 3.5 Impossible bytes */ - { - "\xFE", - NULL, - "\\uFFFD", - }, - { - "\xFF", - NULL, - "\\uFFFD", - }, - { - "\xFE\xFE\xFF\xFF", - NULL, - "\\uFFFD\\uFFFD\\uFFFD\\uFFFD", - }, - /* 4 Overlong sequences */ - /* 4.1 Overlong '/' */ - { - "\xC0\xAF", - NULL, - "\\uFFFD", - }, - { - "\xE0\x80\xAF", - NULL, - "\\uFFFD", - }, - { - "\xF0\x80\x80\xAF", - NULL, - "\\uFFFD", - }, - { - "\xF8\x80\x80\x80\xAF", - NULL, - "\\uFFFD", - }, - { - "\xFC\x80\x80\x80\x80\xAF", - NULL, - "\\uFFFD", - }, - /* - * 4.2 Maximum overlong sequences - * Highest Unicode value that is still resulting in an - * overlong sequence if represented with the given number of - * bytes. This is a boundary test for safe UTF-8 decoders. - */ - { - /* \U+007F */ - "\xC1\xBF", - NULL, - "\\uFFFD", - }, - { - /* \U+07FF */ - "\xE0\x9F\xBF", - NULL, - "\\uFFFD", - }, - { - /* - * \U+FFFC - * The actual maximum would be U+FFFF, but that's a - * noncharacter. Testing U+FFFC seems more useful. See - * also 2.2.3 - */ - "\xF0\x8F\xBF\xBC", - NULL, - "\\uFFFD", - }, - { - /* \U+1FFFFF */ - "\xF8\x87\xBF\xBF\xBF", - NULL, - "\\uFFFD", - }, - { - /* \U+3FFFFFF */ - "\xFC\x83\xBF\xBF\xBF\xBF", - NULL, - "\\uFFFD", - }, - /* 4.3 Overlong representation of the NUL character */ - { - /* \U+0000 */ - "\xC0\x80", - "\xC0\x80", - "\\u0000", - }, - { - /* \U+0000 */ - "\xE0\x80\x80", - NULL, - "\\uFFFD", - }, - { - /* \U+0000 */ - "\xF0\x80\x80\x80", - NULL, - "\\uFFFD", - }, - { - /* \U+0000 */ - "\xF8\x80\x80\x80\x80", - NULL, - "\\uFFFD", - }, - { - /* \U+0000 */ - "\xFC\x80\x80\x80\x80\x80", - NULL, - "\\uFFFD", - }, - /* 5 Illegal code positions */ - /* 5.1 Single UTF-16 surrogates */ - { - /* \U+D800 */ - "\xED\xA0\x80", - NULL, - "\\uFFFD", - }, - { - /* \U+DB7F */ - "\xED\xAD\xBF", - NULL, - "\\uFFFD", - }, - { - /* \U+DB80 */ - "\xED\xAE\x80", - NULL, - "\\uFFFD", - }, - { - /* \U+DBFF */ - "\xED\xAF\xBF", - NULL, - "\\uFFFD", - }, - { - /* \U+DC00 */ - "\xED\xB0\x80", - NULL, - "\\uFFFD", - }, - { - /* \U+DF80 */ - "\xED\xBE\x80", - NULL, - "\\uFFFD", - }, - { - /* \U+DFFF */ - "\xED\xBF\xBF", - NULL, - "\\uFFFD", - }, - /* 5.2 Paired UTF-16 surrogates */ - { - /* \U+D800\U+DC00 */ - "\xED\xA0\x80\xED\xB0\x80", - NULL, - "\\uFFFD\\uFFFD", - }, - { - /* \U+D800\U+DFFF */ - "\xED\xA0\x80\xED\xBF\xBF", - NULL, - "\\uFFFD\\uFFFD", - }, - { - /* \U+DB7F\U+DC00 */ - "\xED\xAD\xBF\xED\xB0\x80", - NULL, - "\\uFFFD\\uFFFD", - }, - { - /* \U+DB7F\U+DFFF */ - "\xED\xAD\xBF\xED\xBF\xBF", - NULL, - "\\uFFFD\\uFFFD", - }, - { - /* \U+DB80\U+DC00 */ - "\xED\xAE\x80\xED\xB0\x80", - NULL, - "\\uFFFD\\uFFFD", - }, - { - /* \U+DB80\U+DFFF */ - "\xED\xAE\x80\xED\xBF\xBF", - NULL, - "\\uFFFD\\uFFFD", - }, - { - /* \U+DBFF\U+DC00 */ - "\xED\xAF\xBF\xED\xB0\x80", - NULL, - "\\uFFFD\\uFFFD", - }, - { - /* \U+DBFF\U+DFFF */ - "\xED\xAF\xBF\xED\xBF\xBF", - NULL, - "\\uFFFD\\uFFFD", - }, - /* 5.3 Other illegal code positions */ - /* BMP noncharacters */ - { - /* \U+FFFE */ - "\xEF\xBF\xBE", - NULL, - "\\uFFFD", - }, - { - /* \U+FFFF */ - "\xEF\xBF\xBF", - NULL, - "\\uFFFD", - }, - { - /* U+FDD0 */ - "\xEF\xB7\x90", - NULL, - "\\uFFFD", - }, - { - /* U+FDEF */ - "\xEF\xB7\xAF", - NULL, - "\\uFFFD", - }, - /* Plane 1 .. 16 noncharacters */ - { - /* U+1FFFE U+1FFFF U+2FFFE U+2FFFF ... U+10FFFE U+10FFFF */ - "\xF0\x9F\xBF\xBE\xF0\x9F\xBF\xBF" - "\xF0\xAF\xBF\xBE\xF0\xAF\xBF\xBF" - "\xF0\xBF\xBF\xBE\xF0\xBF\xBF\xBF" - "\xF1\x8F\xBF\xBE\xF1\x8F\xBF\xBF" - "\xF1\x9F\xBF\xBE\xF1\x9F\xBF\xBF" - "\xF1\xAF\xBF\xBE\xF1\xAF\xBF\xBF" - "\xF1\xBF\xBF\xBE\xF1\xBF\xBF\xBF" - "\xF2\x8F\xBF\xBE\xF2\x8F\xBF\xBF" - "\xF2\x9F\xBF\xBE\xF2\x9F\xBF\xBF" - "\xF2\xAF\xBF\xBE\xF2\xAF\xBF\xBF" - "\xF2\xBF\xBF\xBE\xF2\xBF\xBF\xBF" - "\xF3\x8F\xBF\xBE\xF3\x8F\xBF\xBF" - "\xF3\x9F\xBF\xBE\xF3\x9F\xBF\xBF" - "\xF3\xAF\xBF\xBE\xF3\xAF\xBF\xBF" - "\xF3\xBF\xBF\xBE\xF3\xBF\xBF\xBF" - "\xF4\x8F\xBF\xBE\xF4\x8F\xBF\xBF", - NULL, - "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD" - "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD" - "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD" - "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD", - }, - {} - }; - int i, j; - QString *str; - const char *json_in, *utf8_out, *utf8_in, *json_out, *tail; - char *end, *in, *jstr; - - for (i = 0; test_cases[i].json_in; i++) { - for (j = 0; j < 2; j++) { - json_in = test_cases[i].json_in; - utf8_out = test_cases[i].utf8_out; - utf8_in = test_cases[i].utf8_out ?: test_cases[i].json_in; - json_out = test_cases[i].json_out ?: test_cases[i].json_in; - - /* Parse @json_in, expect @utf8_out */ - if (utf8_out) { - str = from_json_str(json_in, j, &error_abort); - g_assert_cmpstr(qstring_get_str(str), ==, utf8_out); - qobject_unref(str); - } else { - str = from_json_str(json_in, j, NULL); - g_assert(!str); - /* - * Failure may be due to any sequence, but *all* sequences - * are expected to fail. Test each one in isolation. - */ - for (tail = json_in; *tail; tail = end) { - mod_utf8_codepoint(tail, 6, &end); - if (*end == ' ') { - end++; - } - in = g_strndup(tail, end - tail); - str = from_json_str(in, j, NULL); - g_assert(!str); - g_free(in); - } - } - - /* Unparse @utf8_in, expect @json_out */ - str = qstring_from_str(utf8_in); - jstr = to_json_str(str); - g_assert_cmpstr(jstr, ==, json_out); - qobject_unref(str); - g_free(jstr); - - /* Parse @json_out right back, unless it has replacements */ - if (!strstr(json_out, "\\uFFFD")) { - str = from_json_str(json_out, j, &error_abort); - g_assert_cmpstr(qstring_get_str(str), ==, utf8_in); - qobject_unref(str); - } - } - } -} - -static void int_number(void) -{ - struct { - const char *encoded; - int64_t decoded; - const char *reencoded; - } test_cases[] = { - { "0", 0 }, - { "1234", 1234 }, - { "1", 1 }, - { "-32", -32 }, - { "-0", 0, "0" }, - {}, - }; - int i; - QNum *qnum; - int64_t ival; - uint64_t uval; - GString *str; - - for (i = 0; test_cases[i].encoded; i++) { - qnum = qobject_to(QNum, - qobject_from_json(test_cases[i].encoded, - &error_abort)); - g_assert(qnum); - g_assert(qnum_get_try_int(qnum, &ival)); - g_assert_cmpint(ival, ==, test_cases[i].decoded); - if (test_cases[i].decoded >= 0) { - g_assert(qnum_get_try_uint(qnum, &uval)); - g_assert_cmpuint(uval, ==, (uint64_t)test_cases[i].decoded); - } else { - g_assert(!qnum_get_try_uint(qnum, &uval)); - } - g_assert_cmpfloat(qnum_get_double(qnum), ==, - (double)test_cases[i].decoded); - - str = qobject_to_json(QOBJECT(qnum)); - g_assert_cmpstr(str->str, ==, - test_cases[i].reencoded ?: test_cases[i].encoded); - g_string_free(str, true); - - qobject_unref(qnum); - } -} - -static void uint_number(void) -{ - struct { - const char *encoded; - uint64_t decoded; - const char *reencoded; - } test_cases[] = { - { "9223372036854775808", (uint64_t)1 << 63 }, - { "18446744073709551615", UINT64_MAX }, - {}, - }; - int i; - QNum *qnum; - int64_t ival; - uint64_t uval; - GString *str; - - for (i = 0; test_cases[i].encoded; i++) { - qnum = qobject_to(QNum, - qobject_from_json(test_cases[i].encoded, - &error_abort)); - g_assert(qnum); - g_assert(qnum_get_try_uint(qnum, &uval)); - g_assert_cmpuint(uval, ==, test_cases[i].decoded); - g_assert(!qnum_get_try_int(qnum, &ival)); - g_assert_cmpfloat(qnum_get_double(qnum), ==, - (double)test_cases[i].decoded); - - str = qobject_to_json(QOBJECT(qnum)); - g_assert_cmpstr(str->str, ==, - test_cases[i].reencoded ?: test_cases[i].encoded); - g_string_free(str, true); - - qobject_unref(qnum); - } -} - -static void float_number(void) -{ - struct { - const char *encoded; - double decoded; - const char *reencoded; - } test_cases[] = { - { "32.43", 32.43 }, - { "0.222", 0.222 }, - { "-32.12313", -32.12313, "-32.123130000000003" }, - { "-32.20e-10", -32.20e-10, "-3.22e-09" }, - { "18446744073709551616", 0x1p64, "1.8446744073709552e+19" }, - { "-9223372036854775809", -0x1p63, "-9.2233720368547758e+18" }, - {}, - }; - int i; - QNum *qnum; - int64_t ival; - uint64_t uval; - GString *str; - - for (i = 0; test_cases[i].encoded; i++) { - qnum = qobject_to(QNum, - qobject_from_json(test_cases[i].encoded, - &error_abort)); - g_assert(qnum); - g_assert_cmpfloat(qnum_get_double(qnum), ==, test_cases[i].decoded); - g_assert(!qnum_get_try_int(qnum, &ival)); - g_assert(!qnum_get_try_uint(qnum, &uval)); - - str = qobject_to_json(QOBJECT(qnum)); - g_assert_cmpstr(str->str, ==, - test_cases[i].reencoded ?: test_cases[i].encoded); - g_string_free(str, true); - - qobject_unref(qnum); - } -} - -static void keyword_literal(void) -{ - QObject *obj; - QBool *qbool; - QNull *null; - GString *str; - - obj = qobject_from_json("true", &error_abort); - qbool = qobject_to(QBool, obj); - g_assert(qbool); - g_assert(qbool_get_bool(qbool) == true); - - str = qobject_to_json(obj); - g_assert_cmpstr(str->str, ==, "true"); - g_string_free(str, true); - - qobject_unref(qbool); - - obj = qobject_from_json("false", &error_abort); - qbool = qobject_to(QBool, obj); - g_assert(qbool); - g_assert(qbool_get_bool(qbool) == false); - - str = qobject_to_json(obj); - g_assert_cmpstr(str->str, ==, "false"); - g_string_free(str, true); - - qobject_unref(qbool); - - obj = qobject_from_json("null", &error_abort); - g_assert(obj != NULL); - g_assert(qobject_type(obj) == QTYPE_QNULL); - - null = qnull(); - g_assert(QOBJECT(null) == obj); - - qobject_unref(obj); - qobject_unref(null); -} - -static void interpolation_valid(void) -{ - long long value_lld = 0x123456789abcdefLL; - int64_t value_d64 = value_lld; - long value_ld = (long)value_lld; - int value_d = (int)value_lld; - unsigned long long value_llu = 0xfedcba9876543210ULL; - uint64_t value_u64 = value_llu; - unsigned long value_lu = (unsigned long)value_llu; - unsigned value_u = (unsigned)value_llu; - double value_f = 2.323423423; - const char *value_s = "hello world"; - QObject *value_p = QOBJECT(qnull()); - QBool *qbool; - QNum *qnum; - QString *qstr; - QObject *qobj; - - /* bool */ - - qbool = qobject_to(QBool, qobject_from_jsonf_nofail("%i", false)); - g_assert(qbool); - g_assert(qbool_get_bool(qbool) == false); - qobject_unref(qbool); - - /* Test that non-zero values other than 1 get collapsed to true */ - qbool = qobject_to(QBool, qobject_from_jsonf_nofail("%i", 2)); - g_assert(qbool); - g_assert(qbool_get_bool(qbool) == true); - qobject_unref(qbool); - - /* number */ - - qnum = qobject_to(QNum, qobject_from_jsonf_nofail("%d", value_d)); - g_assert_cmpint(qnum_get_int(qnum), ==, value_d); - qobject_unref(qnum); - - qnum = qobject_to(QNum, qobject_from_jsonf_nofail("%ld", value_ld)); - g_assert_cmpint(qnum_get_int(qnum), ==, value_ld); - qobject_unref(qnum); - - qnum = qobject_to(QNum, qobject_from_jsonf_nofail("%lld", value_lld)); - g_assert_cmpint(qnum_get_int(qnum), ==, value_lld); - qobject_unref(qnum); - - qnum = qobject_to(QNum, qobject_from_jsonf_nofail("%" PRId64, value_d64)); - g_assert_cmpint(qnum_get_int(qnum), ==, value_lld); - qobject_unref(qnum); - - qnum = qobject_to(QNum, qobject_from_jsonf_nofail("%u", value_u)); - g_assert_cmpuint(qnum_get_uint(qnum), ==, value_u); - qobject_unref(qnum); - - qnum = qobject_to(QNum, qobject_from_jsonf_nofail("%lu", value_lu)); - g_assert_cmpuint(qnum_get_uint(qnum), ==, value_lu); - qobject_unref(qnum); - - qnum = qobject_to(QNum, qobject_from_jsonf_nofail("%llu", value_llu)); - g_assert_cmpuint(qnum_get_uint(qnum), ==, value_llu); - qobject_unref(qnum); - - qnum = qobject_to(QNum, qobject_from_jsonf_nofail("%" PRIu64, value_u64)); - g_assert_cmpuint(qnum_get_uint(qnum), ==, value_llu); - qobject_unref(qnum); - - qnum = qobject_to(QNum, qobject_from_jsonf_nofail("%f", value_f)); - g_assert(qnum_get_double(qnum) == value_f); - qobject_unref(qnum); - - /* string */ - - qstr = qobject_to(QString, qobject_from_jsonf_nofail("%s", value_s)); - g_assert_cmpstr(qstring_get_str(qstr), ==, value_s); - qobject_unref(qstr); - - /* object */ - - qobj = qobject_from_jsonf_nofail("%p", value_p); - g_assert(qobj == value_p); -} - -static void interpolation_unknown(void) -{ - if (g_test_subprocess()) { - qobject_from_jsonf_nofail("%x", 666); - } - g_test_trap_subprocess(NULL, 0, 0); - g_test_trap_assert_failed(); - g_test_trap_assert_stderr("*Unexpected error*" - "invalid interpolation '%x'*"); -} - -static void interpolation_string(void) -{ - if (g_test_subprocess()) { - qobject_from_jsonf_nofail("['%s', %s]", "eins", "zwei"); - } - g_test_trap_subprocess(NULL, 0, 0); - g_test_trap_assert_failed(); - g_test_trap_assert_stderr("*Unexpected error*" - "can't interpolate into string*"); -} - -static void simple_dict(void) -{ - int i; - struct { - const char *encoded; - QLitObject decoded; - } test_cases[] = { - { - .encoded = "{\"foo\": 42, \"bar\": \"hello world\"}", - .decoded = QLIT_QDICT(((QLitDictEntry[]){ - { "foo", QLIT_QNUM(42) }, - { "bar", QLIT_QSTR("hello world") }, - { } - })), - }, { - .encoded = "{}", - .decoded = QLIT_QDICT(((QLitDictEntry[]){ - { } - })), - }, { - .encoded = "{\"foo\": 43}", - .decoded = QLIT_QDICT(((QLitDictEntry[]){ - { "foo", QLIT_QNUM(43) }, - { } - })), - }, - { } - }; - - for (i = 0; test_cases[i].encoded; i++) { - QObject *obj; - GString *str; - - obj = qobject_from_json(test_cases[i].encoded, &error_abort); - g_assert(qlit_equal_qobject(&test_cases[i].decoded, obj)); - - str = qobject_to_json(obj); - qobject_unref(obj); - - obj = qobject_from_json(str->str, &error_abort); - g_assert(qlit_equal_qobject(&test_cases[i].decoded, obj)); - qobject_unref(obj); - g_string_free(str, true); - } -} - -/* - * this generates json of the form: - * a(0,m) = [0, 1, ..., m-1] - * a(n,m) = { - * 'key0': a(0,m), - * 'key1': a(1,m), - * ... - * 'key(n-1)': a(n-1,m) - * } - */ -static void gen_test_json(GString *gstr, int nest_level_max, - int elem_count) -{ - int i; - - g_assert(gstr); - if (nest_level_max == 0) { - g_string_append(gstr, "["); - for (i = 0; i < elem_count; i++) { - g_string_append_printf(gstr, "%d", i); - if (i < elem_count - 1) { - g_string_append_printf(gstr, ", "); - } - } - g_string_append(gstr, "]"); - return; - } - - g_string_append(gstr, "{"); - for (i = 0; i < nest_level_max; i++) { - g_string_append_printf(gstr, "'key%d': ", i); - gen_test_json(gstr, i, elem_count); - if (i < nest_level_max - 1) { - g_string_append(gstr, ","); - } - } - g_string_append(gstr, "}"); -} - -static void large_dict(void) -{ - GString *gstr = g_string_new(""); - QObject *obj; - - gen_test_json(gstr, 10, 100); - obj = qobject_from_json(gstr->str, &error_abort); - g_assert(obj != NULL); - - qobject_unref(obj); - g_string_free(gstr, true); -} - -static void simple_list(void) -{ - int i; - struct { - const char *encoded; - QLitObject decoded; - } test_cases[] = { - { - .encoded = "[43,42]", - .decoded = QLIT_QLIST(((QLitObject[]){ - QLIT_QNUM(43), - QLIT_QNUM(42), - { } - })), - }, - { - .encoded = "[43]", - .decoded = QLIT_QLIST(((QLitObject[]){ - QLIT_QNUM(43), - { } - })), - }, - { - .encoded = "[]", - .decoded = QLIT_QLIST(((QLitObject[]){ - { } - })), - }, - { - .encoded = "[{}]", - .decoded = QLIT_QLIST(((QLitObject[]){ - QLIT_QDICT(((QLitDictEntry[]){ - {}, - })), - {}, - })), - }, - { } - }; - - for (i = 0; test_cases[i].encoded; i++) { - QObject *obj; - GString *str; - - obj = qobject_from_json(test_cases[i].encoded, &error_abort); - g_assert(qlit_equal_qobject(&test_cases[i].decoded, obj)); - - str = qobject_to_json(obj); - qobject_unref(obj); - - obj = qobject_from_json(str->str, &error_abort); - g_assert(qlit_equal_qobject(&test_cases[i].decoded, obj)); - qobject_unref(obj); - g_string_free(str, true); - } -} - -static void simple_whitespace(void) -{ - int i; - struct { - const char *encoded; - QLitObject decoded; - } test_cases[] = { - { - .encoded = " [ 43 , 42 ]", - .decoded = QLIT_QLIST(((QLitObject[]){ - QLIT_QNUM(43), - QLIT_QNUM(42), - { } - })), - }, - { - .encoded = "\t[ 43 , { 'h' : 'b' },\r\n\t[ ], 42 ]\n", - .decoded = QLIT_QLIST(((QLitObject[]){ - QLIT_QNUM(43), - QLIT_QDICT(((QLitDictEntry[]){ - { "h", QLIT_QSTR("b") }, - { }})), - QLIT_QLIST(((QLitObject[]){ - { }})), - QLIT_QNUM(42), - { } - })), - }, - { - .encoded = " [ 43 , { 'h' : 'b' , 'a' : 32 }, [ ], 42 ]", - .decoded = QLIT_QLIST(((QLitObject[]){ - QLIT_QNUM(43), - QLIT_QDICT(((QLitDictEntry[]){ - { "h", QLIT_QSTR("b") }, - { "a", QLIT_QNUM(32) }, - { }})), - QLIT_QLIST(((QLitObject[]){ - { }})), - QLIT_QNUM(42), - { } - })), - }, - { } - }; - - for (i = 0; test_cases[i].encoded; i++) { - QObject *obj; - GString *str; - - obj = qobject_from_json(test_cases[i].encoded, &error_abort); - g_assert(qlit_equal_qobject(&test_cases[i].decoded, obj)); - - str = qobject_to_json(obj); - qobject_unref(obj); - - obj = qobject_from_json(str->str, &error_abort); - g_assert(qlit_equal_qobject(&test_cases[i].decoded, obj)); - - qobject_unref(obj); - g_string_free(str, true); - } -} - -static void simple_interpolation(void) -{ - QObject *embedded_obj; - QObject *obj; - QLitObject decoded = QLIT_QLIST(((QLitObject[]){ - QLIT_QNUM(1), - QLIT_QSTR("100%"), - QLIT_QLIST(((QLitObject[]){ - QLIT_QNUM(32), - QLIT_QNUM(42), - {}})), - {}})); - - embedded_obj = qobject_from_json("[32, 42]", &error_abort); - g_assert(embedded_obj != NULL); - - obj = qobject_from_jsonf_nofail("[%d, '100%%', %p]", 1, embedded_obj); - g_assert(qlit_equal_qobject(&decoded, obj)); - - qobject_unref(obj); -} - -static void empty_input(void) -{ - Error *err = NULL; - QObject *obj; - - obj = qobject_from_json("", &err); - error_free_or_abort(&err); - g_assert(obj == NULL); -} - -static void blank_input(void) -{ - Error *err = NULL; - QObject *obj; - - obj = qobject_from_json("\n ", &err); - error_free_or_abort(&err); - g_assert(obj == NULL); -} - -static void junk_input(void) -{ - /* Note: junk within strings is covered elsewhere */ - Error *err = NULL; - QObject *obj; - - obj = qobject_from_json("@", &err); - error_free_or_abort(&err); - g_assert(obj == NULL); - - obj = qobject_from_json("{\x01", &err); - error_free_or_abort(&err); - g_assert(obj == NULL); - - obj = qobject_from_json("[0\xFF]", &err); - error_free_or_abort(&err); - g_assert(obj == NULL); - - obj = qobject_from_json("00", &err); - error_free_or_abort(&err); - g_assert(obj == NULL); - - obj = qobject_from_json("[1e", &err); - error_free_or_abort(&err); - g_assert(obj == NULL); - - obj = qobject_from_json("truer", &err); - error_free_or_abort(&err); - g_assert(obj == NULL); -} - -static void unterminated_string(void) -{ - Error *err = NULL; - QObject *obj = qobject_from_json("\"abc", &err); - error_free_or_abort(&err); - g_assert(obj == NULL); -} - -static void unterminated_sq_string(void) -{ - Error *err = NULL; - QObject *obj = qobject_from_json("'abc", &err); - error_free_or_abort(&err); - g_assert(obj == NULL); -} - -static void unterminated_escape(void) -{ - Error *err = NULL; - QObject *obj = qobject_from_json("\"abc\\\"", &err); - error_free_or_abort(&err); - g_assert(obj == NULL); -} - -static void unterminated_array(void) -{ - Error *err = NULL; - QObject *obj = qobject_from_json("[32", &err); - error_free_or_abort(&err); - g_assert(obj == NULL); -} - -static void unterminated_array_comma(void) -{ - Error *err = NULL; - QObject *obj = qobject_from_json("[32,", &err); - error_free_or_abort(&err); - g_assert(obj == NULL); -} - -static void invalid_array_comma(void) -{ - Error *err = NULL; - QObject *obj = qobject_from_json("[32,}", &err); - error_free_or_abort(&err); - g_assert(obj == NULL); -} - -static void unterminated_dict(void) -{ - Error *err = NULL; - QObject *obj = qobject_from_json("{'abc':32", &err); - error_free_or_abort(&err); - g_assert(obj == NULL); -} - -static void unterminated_dict_comma(void) -{ - Error *err = NULL; - QObject *obj = qobject_from_json("{'abc':32,", &err); - error_free_or_abort(&err); - g_assert(obj == NULL); -} - -static void invalid_dict_comma(void) -{ - Error *err = NULL; - QObject *obj = qobject_from_json("{'abc':32,}", &err); - error_free_or_abort(&err); - g_assert(obj == NULL); -} - -static void invalid_dict_key(void) -{ - Error *err = NULL; - QObject *obj = qobject_from_json("{32:'abc'}", &err); - error_free_or_abort(&err); - g_assert(obj == NULL); -} - -static void unterminated_literal(void) -{ - Error *err = NULL; - QObject *obj = qobject_from_json("nul", &err); - error_free_or_abort(&err); - g_assert(obj == NULL); -} - -static char *make_nest(char *buf, size_t cnt) -{ - memset(buf, '[', cnt - 1); - buf[cnt - 1] = '{'; - buf[cnt] = '}'; - memset(buf + cnt + 1, ']', cnt - 1); - buf[2 * cnt] = 0; - return buf; -} - -static void limits_nesting(void) -{ - Error *err = NULL; - enum { max_nesting = 1024 }; /* see qobject/json-streamer.c */ - char buf[2 * (max_nesting + 1) + 1]; - QObject *obj; - - obj = qobject_from_json(make_nest(buf, max_nesting), &error_abort); - g_assert(obj != NULL); - qobject_unref(obj); - - obj = qobject_from_json(make_nest(buf, max_nesting + 1), &err); - error_free_or_abort(&err); - g_assert(obj == NULL); -} - -static void multiple_values(void) -{ - Error *err = NULL; - QObject *obj; - - obj = qobject_from_json("false true", &err); - error_free_or_abort(&err); - g_assert(obj == NULL); - - obj = qobject_from_json("} true", &err); - error_free_or_abort(&err); - g_assert(obj == NULL); -} - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - - g_test_add_func("/literals/string/escaped", escaped_string); - g_test_add_func("/literals/string/quotes", string_with_quotes); - g_test_add_func("/literals/string/utf8", utf8_string); - - g_test_add_func("/literals/number/int", int_number); - g_test_add_func("/literals/number/uint", uint_number); - g_test_add_func("/literals/number/float", float_number); - - g_test_add_func("/literals/keyword", keyword_literal); - - g_test_add_func("/literals/interpolation/valid", interpolation_valid); - g_test_add_func("/literals/interpolation/unkown", interpolation_unknown); - g_test_add_func("/literals/interpolation/string", interpolation_string); - - g_test_add_func("/dicts/simple_dict", simple_dict); - g_test_add_func("/dicts/large_dict", large_dict); - g_test_add_func("/lists/simple_list", simple_list); - - g_test_add_func("/mixed/simple_whitespace", simple_whitespace); - g_test_add_func("/mixed/interpolation", simple_interpolation); - - g_test_add_func("/errors/empty", empty_input); - g_test_add_func("/errors/blank", blank_input); - g_test_add_func("/errors/junk", junk_input); - g_test_add_func("/errors/unterminated/string", unterminated_string); - g_test_add_func("/errors/unterminated/escape", unterminated_escape); - g_test_add_func("/errors/unterminated/sq_string", unterminated_sq_string); - g_test_add_func("/errors/unterminated/array", unterminated_array); - g_test_add_func("/errors/unterminated/array_comma", unterminated_array_comma); - g_test_add_func("/errors/unterminated/dict", unterminated_dict); - g_test_add_func("/errors/unterminated/dict_comma", unterminated_dict_comma); - g_test_add_func("/errors/invalid_array_comma", invalid_array_comma); - g_test_add_func("/errors/invalid_dict_comma", invalid_dict_comma); - g_test_add_func("/errors/invalid_dict_key", invalid_dict_key); - g_test_add_func("/errors/unterminated/literal", unterminated_literal); - g_test_add_func("/errors/limits/nesting", limits_nesting); - g_test_add_func("/errors/multiple_values", multiple_values); - - return g_test_run(); -} diff --git a/tests/check-qlist.c b/tests/check-qlist.c deleted file mode 100644 index 3cd0ccbf19..0000000000 --- a/tests/check-qlist.c +++ /dev/null @@ -1,103 +0,0 @@ -/* - * QList unit-tests. - * - * Copyright (C) 2009 Red Hat Inc. - * - * Authors: - * Luiz Capitulino - * - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - */ -#include "qemu/osdep.h" - -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qlist.h" - -/* - * Public Interface test-cases - * - * (with some violations to access 'private' data) - */ - -static void qlist_new_test(void) -{ - QList *qlist; - - qlist = qlist_new(); - g_assert(qlist != NULL); - g_assert(qlist->base.refcnt == 1); - g_assert(qobject_type(QOBJECT(qlist)) == QTYPE_QLIST); - - qobject_unref(qlist); -} - -static void qlist_append_test(void) -{ - QNum *qi; - QList *qlist; - QListEntry *entry; - - qi = qnum_from_int(42); - - qlist = qlist_new(); - qlist_append(qlist, qi); - - entry = QTAILQ_FIRST(&qlist->head); - g_assert(entry != NULL); - g_assert(entry->value == QOBJECT(qi)); - - qobject_unref(qlist); -} - -static void qobject_to_qlist_test(void) -{ - QList *qlist; - - qlist = qlist_new(); - - g_assert(qobject_to(QList, QOBJECT(qlist)) == qlist); - - qobject_unref(qlist); -} - -static void qlist_iter_test(void) -{ - const int iter_max = 42; - int i; - QList *qlist; - QListEntry *entry; - QNum *qi; - int64_t val; - - qlist = qlist_new(); - - for (i = 0; i < iter_max; i++) - qlist_append_int(qlist, i); - - i = 0; - QLIST_FOREACH_ENTRY(qlist, entry) { - qi = qobject_to(QNum, qlist_entry_obj(entry)); - g_assert(qi != NULL); - - g_assert(qnum_get_try_int(qi, &val)); - g_assert_cmpint(val, ==, i); - i++; - } - - g_assert(i == iter_max); - - qobject_unref(qlist); -} - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - - g_test_add_func("/public/new", qlist_new_test); - g_test_add_func("/public/append", qlist_append_test); - g_test_add_func("/public/to_qlist", qobject_to_qlist_test); - g_test_add_func("/public/iter", qlist_iter_test); - - return g_test_run(); -} diff --git a/tests/check-qlit.c b/tests/check-qlit.c deleted file mode 100644 index bd6798d912..0000000000 --- a/tests/check-qlit.c +++ /dev/null @@ -1,101 +0,0 @@ -/* - * QLit unit-tests. - * - * Copyright (C) 2017 Red Hat Inc. - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" - -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qlit.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qstring.h" - -static QLitObject qlit = QLIT_QDICT(((QLitDictEntry[]) { - { "foo", QLIT_QNUM(42) }, - { "bar", QLIT_QSTR("hello world") }, - { "baz", QLIT_QNULL }, - { "bee", QLIT_QLIST(((QLitObject[]) { - QLIT_QNUM(43), - QLIT_QNUM(44), - QLIT_QBOOL(true), - { }, - }))}, - { }, -})); - -static QLitObject qlit_foo = QLIT_QDICT(((QLitDictEntry[]) { - { "foo", QLIT_QNUM(42) }, - { }, -})); - -static QObject *make_qobject(void) -{ - QDict *qdict = qdict_new(); - QList *list = qlist_new(); - - qdict_put_int(qdict, "foo", 42); - qdict_put_str(qdict, "bar", "hello world"); - qdict_put_null(qdict, "baz"); - - qlist_append_int(list, 43); - qlist_append_int(list, 44); - qlist_append_bool(list, true); - qdict_put(qdict, "bee", list); - - return QOBJECT(qdict); -} - -static void qlit_equal_qobject_test(void) -{ - QObject *qobj = make_qobject(); - - g_assert(qlit_equal_qobject(&qlit, qobj)); - - g_assert(!qlit_equal_qobject(&qlit_foo, qobj)); - - qdict_put(qobject_to(QDict, qobj), "bee", qlist_new()); - g_assert(!qlit_equal_qobject(&qlit, qobj)); - - qobject_unref(qobj); -} - -static void qobject_from_qlit_test(void) -{ - QObject *obj, *qobj = qobject_from_qlit(&qlit); - QDict *qdict; - QList *bee; - - qdict = qobject_to(QDict, qobj); - g_assert_cmpint(qdict_get_int(qdict, "foo"), ==, 42); - g_assert_cmpstr(qdict_get_str(qdict, "bar"), ==, "hello world"); - g_assert(qobject_type(qdict_get(qdict, "baz")) == QTYPE_QNULL); - - bee = qdict_get_qlist(qdict, "bee"); - obj = qlist_pop(bee); - g_assert_cmpint(qnum_get_int(qobject_to(QNum, obj)), ==, 43); - qobject_unref(obj); - obj = qlist_pop(bee); - g_assert_cmpint(qnum_get_int(qobject_to(QNum, obj)), ==, 44); - qobject_unref(obj); - obj = qlist_pop(bee); - g_assert(qbool_get_bool(qobject_to(QBool, obj))); - qobject_unref(obj); - - qobject_unref(qobj); -} - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - - g_test_add_func("/qlit/equal_qobject", qlit_equal_qobject_test); - g_test_add_func("/qlit/qobject_from_qlit", qobject_from_qlit_test); - - return g_test_run(); -} diff --git a/tests/check-qnull.c b/tests/check-qnull.c deleted file mode 100644 index ebf21db83c..0000000000 --- a/tests/check-qnull.c +++ /dev/null @@ -1,78 +0,0 @@ -/* - * QNull unit-tests. - * - * Copyright (C) 2016 Red Hat Inc. - * - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - */ -#include "qemu/osdep.h" - -#include "qapi/qmp/qnull.h" -#include "qemu-common.h" -#include "qapi/qobject-input-visitor.h" -#include "qapi/qobject-output-visitor.h" -#include "qapi/error.h" - -/* - * Public Interface test-cases - * - * (with some violations to access 'private' data) - */ - -static void qnull_ref_test(void) -{ - QObject *obj; - - g_assert(qnull_.base.refcnt == 1); - obj = QOBJECT(qnull()); - g_assert(obj); - g_assert(obj == QOBJECT(&qnull_)); - g_assert(qnull_.base.refcnt == 2); - g_assert(qobject_type(obj) == QTYPE_QNULL); - qobject_unref(obj); - g_assert(qnull_.base.refcnt == 1); -} - -static void qnull_visit_test(void) -{ - QObject *obj; - Visitor *v; - QNull *null; - - /* - * Most tests of interactions between QObject and visitors are in - * test-qmp-*-visitor; but these tests live here because they - * depend on layering violations to check qnull_ refcnt. - */ - - g_assert(qnull_.base.refcnt == 1); - obj = QOBJECT(qnull()); - v = qobject_input_visitor_new(obj); - qobject_unref(obj); - visit_type_null(v, NULL, &null, &error_abort); - g_assert(obj == QOBJECT(&qnull_)); - qobject_unref(null); - visit_free(v); - - null = NULL; - v = qobject_output_visitor_new(&obj); - visit_type_null(v, NULL, &null, &error_abort); - visit_complete(v, &obj); - g_assert(obj == QOBJECT(&qnull_)); - qobject_unref(null); - qobject_unref(obj); - visit_free(v); - - g_assert(qnull_.base.refcnt == 1); -} - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - - g_test_add_func("/public/qnull_ref", qnull_ref_test); - g_test_add_func("/public/qnull_visit", qnull_visit_test); - - return g_test_run(); -} diff --git a/tests/check-qnum.c b/tests/check-qnum.c deleted file mode 100644 index b85fca2302..0000000000 --- a/tests/check-qnum.c +++ /dev/null @@ -1,175 +0,0 @@ -/* - * QNum unit-tests. - * - * Copyright (C) 2009 Red Hat Inc. - * Copyright IBM, Corp. 2009 - * - * Authors: - * Luiz Capitulino - * Anthony Liguori - * - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - */ - -#include "qemu/osdep.h" - -#include "qapi/qmp/qnum.h" -#include "qemu-common.h" - -/* - * Public Interface test-cases - * - * (with some violations to access 'private' data) - */ - -static void qnum_from_int_test(void) -{ - QNum *qn; - const int value = -42; - - qn = qnum_from_int(value); - g_assert(qn != NULL); - g_assert_cmpint(qn->kind, ==, QNUM_I64); - g_assert_cmpint(qn->u.i64, ==, value); - g_assert_cmpint(qn->base.refcnt, ==, 1); - g_assert_cmpint(qobject_type(QOBJECT(qn)), ==, QTYPE_QNUM); - - qobject_unref(qn); -} - -static void qnum_from_uint_test(void) -{ - QNum *qn; - const uint64_t value = UINT64_MAX; - - qn = qnum_from_uint(value); - g_assert(qn != NULL); - g_assert_cmpint(qn->kind, ==, QNUM_U64); - g_assert(qn->u.u64 == value); - g_assert(qn->base.refcnt == 1); - g_assert(qobject_type(QOBJECT(qn)) == QTYPE_QNUM); - - qobject_unref(qn); -} - -static void qnum_from_double_test(void) -{ - QNum *qn; - const double value = -42.23423; - - qn = qnum_from_double(value); - g_assert(qn != NULL); - g_assert_cmpint(qn->kind, ==, QNUM_DOUBLE); - g_assert_cmpfloat(qn->u.dbl, ==, value); - g_assert_cmpint(qn->base.refcnt, ==, 1); - g_assert_cmpint(qobject_type(QOBJECT(qn)), ==, QTYPE_QNUM); - - qobject_unref(qn); -} - -static void qnum_from_int64_test(void) -{ - QNum *qn; - const int64_t value = 0x1234567890abcdefLL; - - qn = qnum_from_int(value); - g_assert_cmpint((int64_t) qn->u.i64, ==, value); - - qobject_unref(qn); -} - -static void qnum_get_int_test(void) -{ - QNum *qn; - const int value = 123456; - - qn = qnum_from_int(value); - g_assert_cmpint(qnum_get_int(qn), ==, value); - - qobject_unref(qn); -} - -static void qnum_get_uint_test(void) -{ - QNum *qn; - const int value = 123456; - uint64_t val; - int64_t ival; - - qn = qnum_from_uint(value); - g_assert(qnum_get_try_uint(qn, &val)); - g_assert_cmpuint(val, ==, value); - qobject_unref(qn); - - qn = qnum_from_int(value); - g_assert(qnum_get_try_uint(qn, &val)); - g_assert_cmpuint(val, ==, value); - qobject_unref(qn); - - /* invalid cases */ - qn = qnum_from_int(-1); - g_assert(!qnum_get_try_uint(qn, &val)); - qobject_unref(qn); - - qn = qnum_from_uint(-1ULL); - g_assert(!qnum_get_try_int(qn, &ival)); - qobject_unref(qn); - - qn = qnum_from_double(0.42); - g_assert(!qnum_get_try_uint(qn, &val)); - qobject_unref(qn); -} - -static void qobject_to_qnum_test(void) -{ - QNum *qn; - - qn = qnum_from_int(0); - g_assert(qobject_to(QNum, QOBJECT(qn)) == qn); - qobject_unref(qn); - - qn = qnum_from_double(0); - g_assert(qobject_to(QNum, QOBJECT(qn)) == qn); - qobject_unref(qn); -} - -static void qnum_to_string_test(void) -{ - QNum *qn; - char *tmp; - - qn = qnum_from_int(123456); - tmp = qnum_to_string(qn); - g_assert_cmpstr(tmp, ==, "123456"); - g_free(tmp); - qobject_unref(qn); - - qn = qnum_from_double(0.42); - tmp = qnum_to_string(qn); - g_assert_cmpstr(tmp, ==, "0.41999999999999998"); - g_free(tmp); - qobject_unref(qn); - - qn = qnum_from_double(2.718281828459045); - tmp = qnum_to_string(qn); - g_assert_cmpstr(tmp, ==, "2.7182818284590451"); - g_free(tmp); - qobject_unref(qn); -} - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - - g_test_add_func("/qnum/from_int", qnum_from_int_test); - g_test_add_func("/qnum/from_uint", qnum_from_uint_test); - g_test_add_func("/qnum/from_double", qnum_from_double_test); - g_test_add_func("/qnum/from_int64", qnum_from_int64_test); - g_test_add_func("/qnum/get_int", qnum_get_int_test); - g_test_add_func("/qnum/get_uint", qnum_get_uint_test); - g_test_add_func("/qnum/to_qnum", qobject_to_qnum_test); - g_test_add_func("/qnum/to_string", qnum_to_string_test); - - return g_test_run(); -} diff --git a/tests/check-qobject.c b/tests/check-qobject.c deleted file mode 100644 index c1713d15af..0000000000 --- a/tests/check-qobject.c +++ /dev/null @@ -1,334 +0,0 @@ -/* - * Generic QObject unit-tests. - * - * Copyright (C) 2017 Red Hat Inc. - * - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "block/qdict.h" -#include "qapi/error.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qnull.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qstring.h" -#include "qemu-common.h" - -#include - -/* Marks the end of the test_equality() argument list. - * We cannot use NULL there because that is a valid argument. */ -static QObject test_equality_end_of_arguments; - -/** - * Test whether all variadic QObject *arguments are equal (@expected - * is true) or whether they are all not equal (@expected is false). - * Every QObject is tested to be equal to itself (to test - * reflexivity), all tests are done both ways (to test symmetry), and - * transitivity is not assumed but checked (each object is compared to - * every other one). - * - * Note that qobject_is_equal() is not really an equivalence relation, - * so this function may not be used for all objects (reflexivity is - * not guaranteed, e.g. in the case of a QNum containing NaN). - * - * The @_ argument is required because a boolean may not be the last - * argument before a variadic argument list (C11 7.16.1.4 para. 4). - */ -static void do_test_equality(bool expected, int _, ...) -{ - va_list ap_count, ap_extract; - QObject **args; - int arg_count = 0; - int i, j; - - va_start(ap_count, _); - va_copy(ap_extract, ap_count); - while (va_arg(ap_count, QObject *) != &test_equality_end_of_arguments) { - arg_count++; - } - va_end(ap_count); - - args = g_new(QObject *, arg_count); - for (i = 0; i < arg_count; i++) { - args[i] = va_arg(ap_extract, QObject *); - } - va_end(ap_extract); - - for (i = 0; i < arg_count; i++) { - g_assert(qobject_is_equal(args[i], args[i]) == true); - - for (j = i + 1; j < arg_count; j++) { - g_assert(qobject_is_equal(args[i], args[j]) == expected); - } - } - - g_free(args); -} - -#define check_equal(...) \ - do_test_equality(true, 0, __VA_ARGS__, &test_equality_end_of_arguments) -#define check_unequal(...) \ - do_test_equality(false, 0, __VA_ARGS__, &test_equality_end_of_arguments) - -static void do_free_all(int _, ...) -{ - va_list ap; - QObject *obj; - - va_start(ap, _); - while ((obj = va_arg(ap, QObject *)) != NULL) { - qobject_unref(obj); - } - va_end(ap); -} - -#define free_all(...) \ - do_free_all(0, __VA_ARGS__, NULL) - -static void qobject_is_equal_null_test(void) -{ - check_unequal(qnull(), NULL); -} - -static void qobject_is_equal_num_test(void) -{ - QNum *u0, *i0, *d0, *dnan, *um42, *im42, *dm42; - - u0 = qnum_from_uint(0u); - i0 = qnum_from_int(0); - d0 = qnum_from_double(0.0); - dnan = qnum_from_double(NAN); - um42 = qnum_from_uint((uint64_t)-42); - im42 = qnum_from_int(-42); - dm42 = qnum_from_double(-42.0); - - /* Integers representing a mathematically equal number should - * compare equal */ - check_equal(u0, i0); - /* Doubles, however, are always unequal to integers */ - check_unequal(u0, d0); - check_unequal(i0, d0); - - /* Do not assume any object is equal to itself -- note however - * that NaN cannot occur in a JSON object anyway. */ - g_assert(qobject_is_equal(QOBJECT(dnan), QOBJECT(dnan)) == false); - - /* No unsigned overflow */ - check_unequal(um42, im42); - check_unequal(um42, dm42); - check_unequal(im42, dm42); - - free_all(u0, i0, d0, dnan, um42, im42, dm42); -} - -static void qobject_is_equal_bool_test(void) -{ - QBool *btrue_0, *btrue_1, *bfalse_0, *bfalse_1; - - btrue_0 = qbool_from_bool(true); - btrue_1 = qbool_from_bool(true); - bfalse_0 = qbool_from_bool(false); - bfalse_1 = qbool_from_bool(false); - - check_equal(btrue_0, btrue_1); - check_equal(bfalse_0, bfalse_1); - check_unequal(btrue_0, bfalse_0); - - free_all(btrue_0, btrue_1, bfalse_0, bfalse_1); -} - -static void qobject_is_equal_string_test(void) -{ - QString *str_base, *str_whitespace_0, *str_whitespace_1, *str_whitespace_2; - QString *str_whitespace_3, *str_case, *str_built; - - str_base = qstring_from_str("foo"); - str_whitespace_0 = qstring_from_str(" foo"); - str_whitespace_1 = qstring_from_str("foo "); - str_whitespace_2 = qstring_from_str("foo\b"); - str_whitespace_3 = qstring_from_str("fooo\b"); - str_case = qstring_from_str("Foo"); - - /* Should yield "foo" */ - str_built = qstring_from_substr("buffoon", 3, 6); - - check_unequal(str_base, str_whitespace_0, str_whitespace_1, - str_whitespace_2, str_whitespace_3, str_case); - - check_equal(str_base, str_built); - - free_all(str_base, str_whitespace_0, str_whitespace_1, str_whitespace_2, - str_whitespace_3, str_case, str_built); -} - -static void qobject_is_equal_list_test(void) -{ - QList *list_0, *list_1, *list_cloned; - QList *list_reordered, *list_longer, *list_shorter; - - list_0 = qlist_new(); - list_1 = qlist_new(); - list_reordered = qlist_new(); - list_longer = qlist_new(); - list_shorter = qlist_new(); - - qlist_append_int(list_0, 1); - qlist_append_int(list_0, 2); - qlist_append_int(list_0, 3); - - qlist_append_int(list_1, 1); - qlist_append_int(list_1, 2); - qlist_append_int(list_1, 3); - - qlist_append_int(list_reordered, 1); - qlist_append_int(list_reordered, 3); - qlist_append_int(list_reordered, 2); - - qlist_append_int(list_longer, 1); - qlist_append_int(list_longer, 2); - qlist_append_int(list_longer, 3); - qlist_append_null(list_longer); - - qlist_append_int(list_shorter, 1); - qlist_append_int(list_shorter, 2); - - list_cloned = qlist_copy(list_0); - - check_equal(list_0, list_1, list_cloned); - check_unequal(list_0, list_reordered, list_longer, list_shorter); - - /* With a NaN in it, the list should no longer compare equal to - * itself */ - qlist_append(list_0, qnum_from_double(NAN)); - g_assert(qobject_is_equal(QOBJECT(list_0), QOBJECT(list_0)) == false); - - free_all(list_0, list_1, list_cloned, list_reordered, list_longer, - list_shorter); -} - -static void qobject_is_equal_dict_test(void) -{ - QDict *dict_0, *dict_1, *dict_cloned; - QDict *dict_different_key, *dict_different_value, *dict_different_null_key; - QDict *dict_longer, *dict_shorter, *dict_nested; - QDict *dict_crumpled; - - dict_0 = qdict_new(); - dict_1 = qdict_new(); - dict_different_key = qdict_new(); - dict_different_value = qdict_new(); - dict_different_null_key = qdict_new(); - dict_longer = qdict_new(); - dict_shorter = qdict_new(); - dict_nested = qdict_new(); - - qdict_put_int(dict_0, "f.o", 1); - qdict_put_int(dict_0, "bar", 2); - qdict_put_int(dict_0, "baz", 3); - qdict_put_null(dict_0, "null"); - - qdict_put_int(dict_1, "f.o", 1); - qdict_put_int(dict_1, "bar", 2); - qdict_put_int(dict_1, "baz", 3); - qdict_put_null(dict_1, "null"); - - qdict_put_int(dict_different_key, "F.o", 1); - qdict_put_int(dict_different_key, "bar", 2); - qdict_put_int(dict_different_key, "baz", 3); - qdict_put_null(dict_different_key, "null"); - - qdict_put_int(dict_different_value, "f.o", 42); - qdict_put_int(dict_different_value, "bar", 2); - qdict_put_int(dict_different_value, "baz", 3); - qdict_put_null(dict_different_value, "null"); - - qdict_put_int(dict_different_null_key, "f.o", 1); - qdict_put_int(dict_different_null_key, "bar", 2); - qdict_put_int(dict_different_null_key, "baz", 3); - qdict_put_null(dict_different_null_key, "none"); - - qdict_put_int(dict_longer, "f.o", 1); - qdict_put_int(dict_longer, "bar", 2); - qdict_put_int(dict_longer, "baz", 3); - qdict_put_int(dict_longer, "xyz", 4); - qdict_put_null(dict_longer, "null"); - - qdict_put_int(dict_shorter, "f.o", 1); - qdict_put_int(dict_shorter, "bar", 2); - qdict_put_int(dict_shorter, "baz", 3); - - qdict_put(dict_nested, "f", qdict_new()); - qdict_put_int(qdict_get_qdict(dict_nested, "f"), "o", 1); - qdict_put_int(dict_nested, "bar", 2); - qdict_put_int(dict_nested, "baz", 3); - qdict_put_null(dict_nested, "null"); - - dict_cloned = qdict_clone_shallow(dict_0); - - check_equal(dict_0, dict_1, dict_cloned); - check_unequal(dict_0, dict_different_key, dict_different_value, - dict_different_null_key, dict_longer, dict_shorter, - dict_nested); - - dict_crumpled = qobject_to(QDict, qdict_crumple(dict_1, &error_abort)); - check_equal(dict_crumpled, dict_nested); - - qdict_flatten(dict_nested); - check_equal(dict_0, dict_nested); - - /* Containing an NaN value will make this dict compare unequal to - * itself */ - qdict_put(dict_0, "NaN", qnum_from_double(NAN)); - g_assert(qobject_is_equal(QOBJECT(dict_0), QOBJECT(dict_0)) == false); - - free_all(dict_0, dict_1, dict_cloned, dict_different_key, - dict_different_value, dict_different_null_key, dict_longer, - dict_shorter, dict_nested, dict_crumpled); -} - -static void qobject_is_equal_conversion_test(void) -{ - QNum *u0, *i0, *d0; - QString *s0, *s_empty; - QBool *bfalse; - - u0 = qnum_from_uint(0u); - i0 = qnum_from_int(0); - d0 = qnum_from_double(0.0); - s0 = qstring_from_str("0"); - s_empty = qstring_new(); - bfalse = qbool_from_bool(false); - - /* No automatic type conversion */ - check_unequal(u0, s0, s_empty, bfalse, qnull(), NULL); - check_unequal(i0, s0, s_empty, bfalse, qnull(), NULL); - check_unequal(d0, s0, s_empty, bfalse, qnull(), NULL); - - free_all(u0, i0, d0, s0, s_empty, bfalse); -} - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - - g_test_add_func("/public/qobject_is_equal_null", - qobject_is_equal_null_test); - g_test_add_func("/public/qobject_is_equal_num", qobject_is_equal_num_test); - g_test_add_func("/public/qobject_is_equal_bool", - qobject_is_equal_bool_test); - g_test_add_func("/public/qobject_is_equal_string", - qobject_is_equal_string_test); - g_test_add_func("/public/qobject_is_equal_list", - qobject_is_equal_list_test); - g_test_add_func("/public/qobject_is_equal_dict", - qobject_is_equal_dict_test); - g_test_add_func("/public/qobject_is_equal_conversion", - qobject_is_equal_conversion_test); - - return g_test_run(); -} diff --git a/tests/check-qom-interface.c b/tests/check-qom-interface.c deleted file mode 100644 index c99be97ed8..0000000000 --- a/tests/check-qom-interface.c +++ /dev/null @@ -1,103 +0,0 @@ -/* - * QOM interface test. - * - * Copyright (C) 2013 Red Hat Inc. - * - * Authors: - * Igor Mammedov - * - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - */ -#include "qemu/osdep.h" - -#include "qom/object.h" -#include "qemu/module.h" - - -#define TYPE_TEST_IF "test-interface" -typedef struct TestIfClass TestIfClass; -DECLARE_CLASS_CHECKERS(TestIfClass, TEST_IF, - TYPE_TEST_IF) -#define TEST_IF(obj) \ - INTERFACE_CHECK(TestIf, (obj), TYPE_TEST_IF) - -typedef struct TestIf TestIf; - -struct TestIfClass { - InterfaceClass parent_class; - - uint32_t test; -}; - -static const TypeInfo test_if_info = { - .name = TYPE_TEST_IF, - .parent = TYPE_INTERFACE, - .class_size = sizeof(TestIfClass), -}; - -#define PATTERN 0xFAFBFCFD - -static void test_class_init(ObjectClass *oc, void *data) -{ - TestIfClass *tc = TEST_IF_CLASS(oc); - - g_assert(tc); - tc->test = PATTERN; -} - -#define TYPE_DIRECT_IMPL "direct-impl" - -static const TypeInfo direct_impl_info = { - .name = TYPE_DIRECT_IMPL, - .parent = TYPE_OBJECT, - .class_init = test_class_init, - .interfaces = (InterfaceInfo[]) { - { TYPE_TEST_IF }, - { } - } -}; - -#define TYPE_INTERMEDIATE_IMPL "intermediate-impl" - -static const TypeInfo intermediate_impl_info = { - .name = TYPE_INTERMEDIATE_IMPL, - .parent = TYPE_DIRECT_IMPL, -}; - -static void test_interface_impl(const char *type) -{ - Object *obj = object_new(type); - TestIf *iobj = TEST_IF(obj); - TestIfClass *ioc = TEST_IF_GET_CLASS(iobj); - - g_assert(iobj); - g_assert(ioc->test == PATTERN); - object_unref(obj); -} - -static void interface_direct_test(void) -{ - test_interface_impl(TYPE_DIRECT_IMPL); -} - -static void interface_intermediate_test(void) -{ - test_interface_impl(TYPE_INTERMEDIATE_IMPL); -} - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - - module_call_init(MODULE_INIT_QOM); - type_register_static(&test_if_info); - type_register_static(&direct_impl_info); - type_register_static(&intermediate_impl_info); - - g_test_add_func("/qom/interface/direct_impl", interface_direct_test); - g_test_add_func("/qom/interface/intermediate_impl", - interface_intermediate_test); - - return g_test_run(); -} diff --git a/tests/check-qom-proplist.c b/tests/check-qom-proplist.c deleted file mode 100644 index 1b76581980..0000000000 --- a/tests/check-qom-proplist.c +++ /dev/null @@ -1,638 +0,0 @@ -/* - * Copyright (C) 2015 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see - * . - * - * Author: Daniel P. Berrange - */ - -#include "qemu/osdep.h" - -#include "qapi/error.h" -#include "qom/object.h" -#include "qemu/module.h" -#include "qemu/option.h" -#include "qemu/config-file.h" -#include "qom/object_interfaces.h" - - -#define TYPE_DUMMY "qemu-dummy" - -typedef struct DummyObject DummyObject; -typedef struct DummyObjectClass DummyObjectClass; - -DECLARE_INSTANCE_CHECKER(DummyObject, DUMMY_OBJECT, - TYPE_DUMMY) - -typedef enum DummyAnimal DummyAnimal; - -enum DummyAnimal { - DUMMY_FROG, - DUMMY_ALLIGATOR, - DUMMY_PLATYPUS, - - DUMMY_LAST, -}; - -const QEnumLookup dummy_animal_map = { - .array = (const char *const[]) { - [DUMMY_FROG] = "frog", - [DUMMY_ALLIGATOR] = "alligator", - [DUMMY_PLATYPUS] = "platypus", - }, - .size = DUMMY_LAST -}; - -struct DummyObject { - Object parent_obj; - - bool bv; - DummyAnimal av; - char *sv; -}; - -struct DummyObjectClass { - ObjectClass parent_class; -}; - - -static void dummy_set_bv(Object *obj, - bool value, - Error **errp) -{ - DummyObject *dobj = DUMMY_OBJECT(obj); - - dobj->bv = value; -} - -static bool dummy_get_bv(Object *obj, - Error **errp) -{ - DummyObject *dobj = DUMMY_OBJECT(obj); - - return dobj->bv; -} - - -static void dummy_set_av(Object *obj, - int value, - Error **errp) -{ - DummyObject *dobj = DUMMY_OBJECT(obj); - - dobj->av = value; -} - -static int dummy_get_av(Object *obj, - Error **errp) -{ - DummyObject *dobj = DUMMY_OBJECT(obj); - - return dobj->av; -} - - -static void dummy_set_sv(Object *obj, - const char *value, - Error **errp) -{ - DummyObject *dobj = DUMMY_OBJECT(obj); - - g_free(dobj->sv); - dobj->sv = g_strdup(value); -} - -static char *dummy_get_sv(Object *obj, - Error **errp) -{ - DummyObject *dobj = DUMMY_OBJECT(obj); - - return g_strdup(dobj->sv); -} - - -static void dummy_init(Object *obj) -{ - object_property_add_bool(obj, "bv", - dummy_get_bv, - dummy_set_bv); -} - - -static void dummy_class_init(ObjectClass *cls, void *data) -{ - object_class_property_add_str(cls, "sv", - dummy_get_sv, - dummy_set_sv); - object_class_property_add_enum(cls, "av", - "DummyAnimal", - &dummy_animal_map, - dummy_get_av, - dummy_set_av); -} - - -static void dummy_finalize(Object *obj) -{ - DummyObject *dobj = DUMMY_OBJECT(obj); - - g_free(dobj->sv); -} - - -static const TypeInfo dummy_info = { - .name = TYPE_DUMMY, - .parent = TYPE_OBJECT, - .instance_size = sizeof(DummyObject), - .instance_init = dummy_init, - .instance_finalize = dummy_finalize, - .class_size = sizeof(DummyObjectClass), - .class_init = dummy_class_init, - .interfaces = (InterfaceInfo[]) { - { TYPE_USER_CREATABLE }, - { } - } -}; - - -/* - * The following 3 object classes are used to - * simulate the kind of relationships seen in - * qdev, which result in complex object - * property destruction ordering. - * - * DummyDev has a 'bus' child to a DummyBus - * DummyBus has a 'backend' child to a DummyBackend - * DummyDev has a 'backend' link to DummyBackend - * - * When DummyDev is finalized, it unparents the - * DummyBackend, which unparents the DummyDev - * which deletes the 'backend' link from DummyDev - * to DummyBackend. This illustrates that the - * object_property_del_all() method needs to - * cope with the list of properties being changed - * while it iterates over them. - */ -typedef struct DummyDev DummyDev; -typedef struct DummyDevClass DummyDevClass; -typedef struct DummyBus DummyBus; -typedef struct DummyBusClass DummyBusClass; -typedef struct DummyBackend DummyBackend; -typedef struct DummyBackendClass DummyBackendClass; - -#define TYPE_DUMMY_DEV "qemu-dummy-dev" -#define TYPE_DUMMY_BUS "qemu-dummy-bus" -#define TYPE_DUMMY_BACKEND "qemu-dummy-backend" - -DECLARE_INSTANCE_CHECKER(DummyDev, DUMMY_DEV, - TYPE_DUMMY_DEV) -DECLARE_INSTANCE_CHECKER(DummyBus, DUMMY_BUS, - TYPE_DUMMY_BUS) -DECLARE_INSTANCE_CHECKER(DummyBackend, DUMMY_BACKEND, - TYPE_DUMMY_BACKEND) - -struct DummyDev { - Object parent_obj; - - DummyBus *bus; -}; - -struct DummyDevClass { - ObjectClass parent_class; -}; - -struct DummyBus { - Object parent_obj; - - DummyBackend *backend; -}; - -struct DummyBusClass { - ObjectClass parent_class; -}; - -struct DummyBackend { - Object parent_obj; -}; - -struct DummyBackendClass { - ObjectClass parent_class; -}; - - -static void dummy_dev_finalize(Object *obj) -{ - DummyDev *dev = DUMMY_DEV(obj); - - object_unref(OBJECT(dev->bus)); -} - -static void dummy_dev_init(Object *obj) -{ - DummyDev *dev = DUMMY_DEV(obj); - DummyBus *bus = DUMMY_BUS(object_new(TYPE_DUMMY_BUS)); - DummyBackend *backend = DUMMY_BACKEND(object_new(TYPE_DUMMY_BACKEND)); - - object_property_add_child(obj, "bus", OBJECT(bus)); - dev->bus = bus; - object_property_add_child(OBJECT(bus), "backend", OBJECT(backend)); - bus->backend = backend; - - object_property_add_link(obj, "backend", TYPE_DUMMY_BACKEND, - (Object **)&bus->backend, NULL, 0); -} - -static void dummy_dev_unparent(Object *obj) -{ - DummyDev *dev = DUMMY_DEV(obj); - object_unparent(OBJECT(dev->bus)); -} - -static void dummy_dev_class_init(ObjectClass *klass, void *opaque) -{ - klass->unparent = dummy_dev_unparent; -} - - -static void dummy_bus_finalize(Object *obj) -{ - DummyBus *bus = DUMMY_BUS(obj); - - object_unref(OBJECT(bus->backend)); -} - -static void dummy_bus_init(Object *obj) -{ -} - -static void dummy_bus_unparent(Object *obj) -{ - DummyBus *bus = DUMMY_BUS(obj); - object_property_del(obj->parent, "backend"); - object_unparent(OBJECT(bus->backend)); -} - -static void dummy_bus_class_init(ObjectClass *klass, void *opaque) -{ - klass->unparent = dummy_bus_unparent; -} - -static void dummy_backend_init(Object *obj) -{ -} - - -static const TypeInfo dummy_dev_info = { - .name = TYPE_DUMMY_DEV, - .parent = TYPE_OBJECT, - .instance_size = sizeof(DummyDev), - .instance_init = dummy_dev_init, - .instance_finalize = dummy_dev_finalize, - .class_size = sizeof(DummyDevClass), - .class_init = dummy_dev_class_init, -}; - -static const TypeInfo dummy_bus_info = { - .name = TYPE_DUMMY_BUS, - .parent = TYPE_OBJECT, - .instance_size = sizeof(DummyBus), - .instance_init = dummy_bus_init, - .instance_finalize = dummy_bus_finalize, - .class_size = sizeof(DummyBusClass), - .class_init = dummy_bus_class_init, -}; - -static const TypeInfo dummy_backend_info = { - .name = TYPE_DUMMY_BACKEND, - .parent = TYPE_OBJECT, - .instance_size = sizeof(DummyBackend), - .instance_init = dummy_backend_init, - .class_size = sizeof(DummyBackendClass), -}; - -static QemuOptsList qemu_object_opts = { - .name = "object", - .implied_opt_name = "qom-type", - .head = QTAILQ_HEAD_INITIALIZER(qemu_object_opts.head), - .desc = { - { } - }, -}; - - -static void test_dummy_createv(void) -{ - Error *err = NULL; - Object *parent = object_get_objects_root(); - DummyObject *dobj = DUMMY_OBJECT( - object_new_with_props(TYPE_DUMMY, - parent, - "dummy0", - &err, - "bv", "yes", - "sv", "Hiss hiss hiss", - "av", "platypus", - NULL)); - - g_assert(err == NULL); - g_assert_cmpstr(dobj->sv, ==, "Hiss hiss hiss"); - g_assert(dobj->bv == true); - g_assert(dobj->av == DUMMY_PLATYPUS); - - g_assert(object_resolve_path_component(parent, "dummy0") - == OBJECT(dobj)); - - object_unparent(OBJECT(dobj)); -} - - -static Object *new_helper(Error **errp, - Object *parent, - ...) -{ - va_list vargs; - Object *obj; - - va_start(vargs, parent); - obj = object_new_with_propv(TYPE_DUMMY, - parent, - "dummy0", - errp, - vargs); - va_end(vargs); - return obj; -} - -static void test_dummy_createlist(void) -{ - Error *err = NULL; - Object *parent = object_get_objects_root(); - DummyObject *dobj = DUMMY_OBJECT( - new_helper(&err, - parent, - "bv", "yes", - "sv", "Hiss hiss hiss", - "av", "platypus", - NULL)); - - g_assert(err == NULL); - g_assert_cmpstr(dobj->sv, ==, "Hiss hiss hiss"); - g_assert(dobj->bv == true); - g_assert(dobj->av == DUMMY_PLATYPUS); - - g_assert(object_resolve_path_component(parent, "dummy0") - == OBJECT(dobj)); - - object_unparent(OBJECT(dobj)); -} - -static void test_dummy_createcmdl(void) -{ - QemuOpts *opts; - DummyObject *dobj; - Error *err = NULL; - const char *params = TYPE_DUMMY \ - ",id=dev0," \ - "bv=yes,sv=Hiss hiss hiss,av=platypus"; - - qemu_add_opts(&qemu_object_opts); - opts = qemu_opts_parse(&qemu_object_opts, params, true, &err); - g_assert(err == NULL); - g_assert(opts); - - dobj = DUMMY_OBJECT(user_creatable_add_opts(opts, &err)); - g_assert(err == NULL); - g_assert(dobj); - g_assert_cmpstr(dobj->sv, ==, "Hiss hiss hiss"); - g_assert(dobj->bv == true); - g_assert(dobj->av == DUMMY_PLATYPUS); - - user_creatable_del("dev0", &error_abort); - - object_unref(OBJECT(dobj)); - - /* - * cmdline-parsing via qemu_opts_parse() results in a QemuOpts entry - * corresponding to the Object's ID to be added to the QemuOptsList - * for objects. To avoid having this entry conflict with future - * Objects using the same ID (which can happen in cases where - * qemu_opts_parse() is used to parse the object params, such as - * with hmp_object_add() at the time of this comment), we need to - * check for this in user_creatable_del() and remove the QemuOpts if - * it is present. - * - * The below check ensures this works as expected. - */ - g_assert_null(qemu_opts_find(&qemu_object_opts, "dev0")); -} - -static void test_dummy_badenum(void) -{ - Error *err = NULL; - Object *parent = object_get_objects_root(); - Object *dobj = - object_new_with_props(TYPE_DUMMY, - parent, - "dummy0", - &err, - "bv", "yes", - "sv", "Hiss hiss hiss", - "av", "yeti", - NULL); - - g_assert(dobj == NULL); - g_assert(err != NULL); - g_assert_cmpstr(error_get_pretty(err), ==, - "Invalid parameter 'yeti'"); - - g_assert(object_resolve_path_component(parent, "dummy0") - == NULL); - - error_free(err); -} - - -static void test_dummy_getenum(void) -{ - Error *err = NULL; - int val; - Object *parent = object_get_objects_root(); - DummyObject *dobj = DUMMY_OBJECT( - object_new_with_props(TYPE_DUMMY, - parent, - "dummy0", - &err, - "av", "platypus", - NULL)); - - g_assert(err == NULL); - g_assert(dobj->av == DUMMY_PLATYPUS); - - val = object_property_get_enum(OBJECT(dobj), - "av", - "DummyAnimal", - &error_abort); - g_assert(val == DUMMY_PLATYPUS); - - /* A bad enum type name */ - val = object_property_get_enum(OBJECT(dobj), - "av", - "BadAnimal", - &err); - g_assert(val == -1); - error_free_or_abort(&err); - - /* A non-enum property name */ - val = object_property_get_enum(OBJECT(dobj), - "iv", - "DummyAnimal", - &err); - g_assert(val == -1); - error_free_or_abort(&err); - - object_unparent(OBJECT(dobj)); -} - - -static void test_dummy_prop_iterator(ObjectPropertyIterator *iter, - const char *expected[], int n) -{ - ObjectProperty *prop; - int i; - - while ((prop = object_property_iter_next(iter))) { - for (i = 0; i < n; i++) { - if (!g_strcmp0(prop->name, expected[i])) { - break; - } - } - g_assert(i < n); - expected[i] = NULL; - } - - for (i = 0; i < n; i++) { - g_assert(!expected[i]); - } -} - -static void test_dummy_iterator(void) -{ - const char *expected[] = { - "type", /* inherited from TYPE_OBJECT */ - "sv", "av", /* class properties */ - "bv"}; /* instance property */ - Object *parent = object_get_objects_root(); - DummyObject *dobj = DUMMY_OBJECT( - object_new_with_props(TYPE_DUMMY, - parent, - "dummy0", - &error_abort, - "bv", "yes", - "sv", "Hiss hiss hiss", - "av", "platypus", - NULL)); - ObjectPropertyIterator iter; - - object_property_iter_init(&iter, OBJECT(dobj)); - test_dummy_prop_iterator(&iter, expected, ARRAY_SIZE(expected)); - object_unparent(OBJECT(dobj)); -} - -static void test_dummy_class_iterator(void) -{ - const char *expected[] = { "type", "av", "sv" }; - ObjectPropertyIterator iter; - ObjectClass *klass = object_class_by_name(TYPE_DUMMY); - - object_class_property_iter_init(&iter, klass); - test_dummy_prop_iterator(&iter, expected, ARRAY_SIZE(expected)); -} - -static void test_dummy_delchild(void) -{ - Object *parent = object_get_objects_root(); - DummyDev *dev = DUMMY_DEV( - object_new_with_props(TYPE_DUMMY_DEV, - parent, - "dev0", - &error_abort, - NULL)); - - object_unparent(OBJECT(dev)); -} - -static void test_qom_partial_path(void) -{ - Object *root = object_get_objects_root(); - Object *cont1 = container_get(root, "/cont1"); - Object *obj1 = object_new(TYPE_DUMMY); - Object *obj2a = object_new(TYPE_DUMMY); - Object *obj2b = object_new(TYPE_DUMMY); - bool ambiguous; - - /* Objects created: - * /cont1 - * /cont1/obj1 - * /cont1/obj2 (obj2a) - * /obj2 (obj2b) - */ - object_property_add_child(cont1, "obj1", obj1); - object_unref(obj1); - object_property_add_child(cont1, "obj2", obj2a); - object_unref(obj2a); - object_property_add_child(root, "obj2", obj2b); - object_unref(obj2b); - - ambiguous = false; - g_assert(!object_resolve_path_type("", TYPE_DUMMY, &ambiguous)); - g_assert(ambiguous); - g_assert(!object_resolve_path_type("", TYPE_DUMMY, NULL)); - - ambiguous = false; - g_assert(!object_resolve_path("obj2", &ambiguous)); - g_assert(ambiguous); - g_assert(!object_resolve_path("obj2", NULL)); - - ambiguous = false; - g_assert(object_resolve_path("obj1", &ambiguous) == obj1); - g_assert(!ambiguous); - g_assert(object_resolve_path("obj1", NULL) == obj1); - - object_unparent(obj2b); - object_unparent(cont1); -} - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - - module_call_init(MODULE_INIT_QOM); - type_register_static(&dummy_info); - type_register_static(&dummy_dev_info); - type_register_static(&dummy_bus_info); - type_register_static(&dummy_backend_info); - - g_test_add_func("/qom/proplist/createlist", test_dummy_createlist); - g_test_add_func("/qom/proplist/createv", test_dummy_createv); - g_test_add_func("/qom/proplist/createcmdline", test_dummy_createcmdl); - g_test_add_func("/qom/proplist/badenum", test_dummy_badenum); - g_test_add_func("/qom/proplist/getenum", test_dummy_getenum); - g_test_add_func("/qom/proplist/iterator", test_dummy_iterator); - g_test_add_func("/qom/proplist/class_iterator", test_dummy_class_iterator); - g_test_add_func("/qom/proplist/delchild", test_dummy_delchild); - g_test_add_func("/qom/resolve/partial", test_qom_partial_path); - - return g_test_run(); -} diff --git a/tests/check-qstring.c b/tests/check-qstring.c deleted file mode 100644 index 4bf9772093..0000000000 --- a/tests/check-qstring.c +++ /dev/null @@ -1,82 +0,0 @@ -/* - * QString unit-tests. - * - * Copyright (C) 2009 Red Hat Inc. - * - * Authors: - * Luiz Capitulino - * - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - */ -#include "qemu/osdep.h" - -#include "qapi/qmp/qstring.h" -#include "qemu-common.h" - -/* - * Public Interface test-cases - * - * (with some violations to access 'private' data) - */ - -static void qstring_from_str_test(void) -{ - QString *qstring; - const char *str = "QEMU"; - - qstring = qstring_from_str(str); - g_assert(qstring != NULL); - g_assert(qstring->base.refcnt == 1); - g_assert(strcmp(str, qstring->string) == 0); - g_assert(qobject_type(QOBJECT(qstring)) == QTYPE_QSTRING); - - qobject_unref(qstring); -} - -static void qstring_get_str_test(void) -{ - QString *qstring; - const char *ret_str; - const char *str = "QEMU/KVM"; - - qstring = qstring_from_str(str); - ret_str = qstring_get_str(qstring); - g_assert(strcmp(ret_str, str) == 0); - - qobject_unref(qstring); -} - -static void qstring_from_substr_test(void) -{ - QString *qs; - - qs = qstring_from_substr("virtualization", 3, 10); - g_assert(qs != NULL); - g_assert(strcmp(qstring_get_str(qs), "tualiza") == 0); - - qobject_unref(qs); -} - - -static void qobject_to_qstring_test(void) -{ - QString *qstring; - - qstring = qstring_from_str("foo"); - g_assert(qobject_to(QString, QOBJECT(qstring)) == qstring); - - qobject_unref(qstring); -} - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - - g_test_add_func("/public/from_str", qstring_from_str_test); - g_test_add_func("/public/get_str", qstring_get_str_test); - g_test_add_func("/public/from_substr", qstring_from_substr_test); - g_test_add_func("/public/to_qstring", qobject_to_qstring_test); - - return g_test_run(); -} diff --git a/tests/crypto-tls-psk-helpers.c b/tests/crypto-tls-psk-helpers.c deleted file mode 100644 index a8395477c3..0000000000 --- a/tests/crypto-tls-psk-helpers.c +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2015-2018 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see - * . - * - * Author: Richard W.M. Jones - */ - -#include "qemu/osdep.h" - -/* Include this first because it defines QCRYPTO_HAVE_TLS_TEST_SUPPORT */ -#include "crypto-tls-x509-helpers.h" - -#include "crypto-tls-psk-helpers.h" -#include "qemu/sockets.h" - -#ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT - -void test_tls_psk_init(const char *pskfile) -{ - FILE *fp; - - fp = fopen(pskfile, "w"); - if (fp == NULL) { - g_critical("Failed to create pskfile %s", pskfile); - abort(); - } - /* Don't do this in real applications! Use psktool. */ - fprintf(fp, "qemu:009d5638c40fde0c\n"); - fclose(fp); -} - -void test_tls_psk_cleanup(const char *pskfile) -{ - unlink(pskfile); -} - -#endif /* QCRYPTO_HAVE_TLS_TEST_SUPPORT */ diff --git a/tests/crypto-tls-psk-helpers.h b/tests/crypto-tls-psk-helpers.h deleted file mode 100644 index 5aa9951cb6..0000000000 --- a/tests/crypto-tls-psk-helpers.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2015-2018 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see - * . - * - * Author: Richard W.M. Jones - */ - -#ifndef TESTS_CRYPTO_TLS_PSK_HELPERS_H -#define TESTS_CRYPTO_TLS_PSK_HELPERS_H - -#include - -#ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT - -void test_tls_psk_init(const char *keyfile); -void test_tls_psk_cleanup(const char *keyfile); - -#endif /* QCRYPTO_HAVE_TLS_TEST_SUPPORT */ - -#endif diff --git a/tests/crypto-tls-x509-helpers.c b/tests/crypto-tls-x509-helpers.c deleted file mode 100644 index 97658592a2..0000000000 --- a/tests/crypto-tls-x509-helpers.c +++ /dev/null @@ -1,508 +0,0 @@ -/* - * Copyright (C) 2015 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see - * . - * - * Author: Daniel P. Berrange - */ - -#include "qemu/osdep.h" - -#include "crypto-tls-x509-helpers.h" -#include "crypto/init.h" -#include "qemu/sockets.h" - -#ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT - -/* - * This stores some static data that is needed when - * encoding extensions in the x509 certs - */ -asn1_node pkix_asn1; - -/* - * To avoid consuming random entropy to generate keys, - * here's one we prepared earlier :-) - */ -gnutls_x509_privkey_t privkey; -# define PRIVATE_KEY \ - "-----BEGIN RSA PRIVATE KEY-----\n" \ - "MIIG5AIBAAKCAYEAyjWyLSNm5PZvYUKUcDWGqbLX10b2ood+YaFjWSnJrqx/q3qh\n" \ - "rVGBJglD25AJENJsmZF3zPP1oMhfIxsXu63Hdkb6Rdlc2RUoUP34x9VC1izH25mR\n" \ - "6c8DPDp1d6IraZ/llDMI1HsBFz0qGWtvOHgm815XG4PAr/N8rDsuqfv/cJ01KlnO\n" \ - "0OdO5QRXCJf9g/dYd41MPu7wOXk9FqjQlmRoP59HgtJ+zUpE4z+Keruw9cMT9VJj\n" \ - "0oT+pQ9ysenqeZ3gbT224T1khrEhT5kifhtFLNyDssRchUUWH0hiqoOO1vgb+850\n" \ - "W6/1VdxvuPam48py4diSPi1Vip8NITCOBaX9FIpVp4Ruw4rTPVMNMjq9Cpx/DwMP\n" \ - "9MbfXfnaVaZaMrmq67/zPhl0eVbUrecH2hQ3ZB9oIF4GkNskzlWF5+yPy6zqk304\n" \ - "AKaiFR6jRyh3YfHo2XFqV8x/hxdsIEXOtEUGhSIcpynsW+ckUCartzu7xbhXjd4b\n" \ - "kxJT89+riPFYij09AgMBAAECggGBAKyFkaZXXROeejrmHlV6JZGlp+fhgM38gkRz\n" \ - "+Jp7P7rLLAY3E7gXIPQ91WqAAmwazFNdvHPd9USfkCQYmnAi/VoZhrCPmlsQZRxt\n" \ - "A5QjjOnEvSPMa6SrXZxGWDCg6R8uMCb4P+FhrPWR1thnRDZOtRTQ+crc50p3mHgt\n" \ - "6ktXWIJRbqnag8zSfQqCYGtRmhe8sfsWT+Yl4El4+jjaAVU/B364u7+PLmaiphGp\n" \ - "BdJfTsTwEpgtGkPj+osDmhzXcZkfq3V+fz5JLkemsCiQKmn4VJRpg8c3ZmE8NPNt\n" \ - "gRtGWZ4W3WKDvhotT65WpQx4+6R8Duux/blNPBmH1Upmwd7kj7GYFBArbCjgd9PT\n" \ - "xgfCSUZpgOZHHkcgSB+022a8XncXna7WYYij28SLtwImFyu0nNtqECFQHH5u+k6C\n" \ - "LRYBSN+3t3At8dQuk01NVrJBndmjmXRfxpqUtTdeaNgVpdUYRY98s30G68NYGSra\n" \ - "aEvhhRSghkcLNetkobpY9pUgeqW/tQKBwQDZHHK9nDMt/zk1TxtILeUSitPXcv1/\n" \ - "8ufXqO0miHdH23XuXhIEA6Ef26RRVGDGgpjkveDJK/1w5feJ4H/ni4Vclil/cm38\n" \ - "OwRqjjd7ElHJX6JQbsxEx/gNTk5/QW1iAL9TXUalgepsSXYT6AJ0/CJv0jmJSJ36\n" \ - "YoKMOM8uqzb2KhN6i+RlJRi5iY53kUhWTJq5ArWvNhUzQNSYODI4bNxlsKSBL2Ik\n" \ - "LZ5QKHuaEjQet0IlPlfIb4PzMm8CHa/urOcCgcEA7m3zW/lL5bIFoKPjWig5Lbn1\n" \ - "aHfrG2ngqzWtgWtfZqMH8OkZc1Mdhhmvd46titjiLjeI+UP/uHXR0068PnrNngzl\n" \ - "tTgwlakzu+bWzqhBm1F+3/341st/FEk07r0P/3/PhezVjwfO8c8Exj7pLxH4wrH0\n" \ - "ROHgDbClmlJRu6OO78wk1+Vapf5DWa8YfA+q+fdvr7KvgGyytheKMT/b/dsqOq7y\n" \ - "qZPjmaJKWAvV3RWG8lWHFSdHx2IAHMHfGr17Y/w7AoHBALzwZeYebeekiVucGSjq\n" \ - "T8SgLhT7zCIx+JMUPjVfYzaUhP/Iu7Lkma6IzWm9nW6Drpy5pUpMzwUWDCLfzU9q\n" \ - "eseFIl337kEn9wLn+t5OpgAyCqYmlftxbqvdrrBN9uvnrJjWvqk/8wsDrw9JxAGc\n" \ - "fjeD4nBXUqvYWLXApoR9mZoGKedmoH9pFig4zlO9ig8YITnKYuQ0k6SD0b8agJHc\n" \ - "Ir0YSUDnRGgpjvFBGbeOCe+FGbohk/EpItJc3IAh5740lwKBwAdXd2DjokSmYKn7\n" \ - "oeqKxofz6+yVlLW5YuOiuX78sWlVp87xPolgi84vSEnkKM/Xsc8+goc6YstpRVa+\n" \ - "W+mImoA9YW1dF5HkLeWhTAf9AlgoAEIhbeIfTgBv6KNZSv7RDrDPBBxtXx/vAfSg\n" \ - "x0ldwk0scZsVYXLKd67yzfV7KdGUdaX4N/xYgfZm/9gCG3+q8NN2KxVHQ5F71BOE\n" \ - "JeABOaGo9WvnU+DNMIDZjHJMUWVw4MHz/a/UArDf/2CxaPVBNQKBwASg6j4ohSTk\n" \ - "J7aE6RQ3OBmmDDpixcoCJt9u9SjHVYMlbs5CEJGVSczk0SG3y8P1lOWNDSRnMksZ\n" \ - "xWnHdP/ogcuYMuvK7UACNAF0zNddtzOhzcpNmejFj+WCHYY/UmPr2/Kf6t7Cxk2K\n" \ - "3cZ4tqWsiTmBT8Bknmah7L5DrhS+ZBJliDeFAA8fZHdMH0Xjr4UBp9kF90EMTdW1\n" \ - "Xr5uz7ZrMsYpYQI7mmyqV9SSjUg4iBXwVSoag1iDJ1K8Qg/L7Semgg==\n" \ - "-----END RSA PRIVATE KEY-----\n" - -/* - * This loads the private key we defined earlier - */ -static gnutls_x509_privkey_t test_tls_load_key(void) -{ - gnutls_x509_privkey_t key; - const gnutls_datum_t data = { (unsigned char *)PRIVATE_KEY, - strlen(PRIVATE_KEY) }; - int err; - - err = gnutls_x509_privkey_init(&key); - if (err < 0) { - g_critical("Failed to init key %s", gnutls_strerror(err)); - abort(); - } - - err = gnutls_x509_privkey_import(key, &data, - GNUTLS_X509_FMT_PEM); - if (err < 0) { - if (err != GNUTLS_E_BASE64_UNEXPECTED_HEADER_ERROR && - err != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { - g_critical("Failed to import key %s", gnutls_strerror(err)); - abort(); - } - - err = gnutls_x509_privkey_import_pkcs8( - key, &data, GNUTLS_X509_FMT_PEM, NULL, 0); - if (err < 0) { - g_critical("Failed to import PKCS8 key %s", gnutls_strerror(err)); - abort(); - } - } - - return key; -} - - -void test_tls_init(const char *keyfile) -{ - qcrypto_init(&error_abort); - - if (asn1_array2tree(pkix_asn1_tab, &pkix_asn1, NULL) != ASN1_SUCCESS) { - abort(); - } - - privkey = test_tls_load_key(); - if (!g_file_set_contents(keyfile, PRIVATE_KEY, -1, NULL)) { - abort(); - } -} - - -void test_tls_cleanup(const char *keyfile) -{ - asn1_delete_structure(&pkix_asn1); - unlink(keyfile); -} - -/* - * Turns an ASN1 object into a DER encoded byte array - */ -static void test_tls_der_encode(asn1_node src, - const char *src_name, - gnutls_datum_t *res) -{ - int size; - char *data = NULL; - - size = 0; - asn1_der_coding(src, src_name, NULL, &size, NULL); - - data = g_new0(char, size); - - asn1_der_coding(src, src_name, data, &size, NULL); - - res->data = (unsigned char *)data; - res->size = size; -} - - -static void -test_tls_get_ipaddr(const char *addrstr, - char **data, - int *datalen) -{ - struct addrinfo *res; - struct addrinfo hints; - - memset(&hints, 0, sizeof(hints)); - hints.ai_flags = AI_NUMERICHOST; - g_assert(getaddrinfo(addrstr, NULL, &hints, &res) == 0); - - *datalen = res->ai_addrlen; - *data = g_new(char, *datalen); - memcpy(*data, res->ai_addr, *datalen); - freeaddrinfo(res); -} - -/* - * This is a fairly lame x509 certificate generator. - * - * Do not copy/use this code for generating real certificates - * since it leaves out many things that you would want in - * certificates for real world usage. - * - * This is good enough only for doing tests of the QEMU - * TLS certificate code - */ -void -test_tls_generate_cert(QCryptoTLSTestCertReq *req, - gnutls_x509_crt_t ca) -{ - gnutls_x509_crt_t crt; - int err; - static char buffer[1024 * 1024]; - size_t size = sizeof(buffer); - char serial[5] = { 1, 2, 3, 4, 0 }; - gnutls_datum_t der; - time_t start = time(NULL) + (60 * 60 * req->start_offset); - time_t expire = time(NULL) + (60 * 60 * (req->expire_offset - ? req->expire_offset : 24)); - - /* - * Prepare our new certificate object - */ - err = gnutls_x509_crt_init(&crt); - if (err < 0) { - g_critical("Failed to initialize certificate %s", gnutls_strerror(err)); - abort(); - } - err = gnutls_x509_crt_set_key(crt, privkey); - if (err < 0) { - g_critical("Failed to set certificate key %s", gnutls_strerror(err)); - abort(); - } - - /* - * A v3 certificate is required in order to be able - * set any of the basic constraints, key purpose and - * key usage data - */ - gnutls_x509_crt_set_version(crt, 3); - - if (req->country) { - err = gnutls_x509_crt_set_dn_by_oid( - crt, GNUTLS_OID_X520_COUNTRY_NAME, 0, - req->country, strlen(req->country)); - if (err < 0) { - g_critical("Failed to set certificate country name %s", - gnutls_strerror(err)); - abort(); - } - } - if (req->cn) { - err = gnutls_x509_crt_set_dn_by_oid( - crt, GNUTLS_OID_X520_COMMON_NAME, 0, - req->cn, strlen(req->cn)); - if (err < 0) { - g_critical("Failed to set certificate common name %s", - gnutls_strerror(err)); - abort(); - } - } - - /* - * Setup the subject altnames, which are used - * for hostname checks in live sessions - */ - if (req->altname1) { - err = gnutls_x509_crt_set_subject_alt_name( - crt, GNUTLS_SAN_DNSNAME, - req->altname1, - strlen(req->altname1), - GNUTLS_FSAN_APPEND); - if (err < 0) { - g_critical("Failed to set certificate alt name %s", - gnutls_strerror(err)); - abort(); - } - } - if (req->altname2) { - err = gnutls_x509_crt_set_subject_alt_name( - crt, GNUTLS_SAN_DNSNAME, - req->altname2, - strlen(req->altname2), - GNUTLS_FSAN_APPEND); - if (err < 0) { - g_critical("Failed to set certificate %s alt name", - gnutls_strerror(err)); - abort(); - } - } - - /* - * IP address need to be put into the cert in their - * raw byte form, not strings, hence this is a little - * more complicated - */ - if (req->ipaddr1) { - char *data; - int len; - - test_tls_get_ipaddr(req->ipaddr1, &data, &len); - - err = gnutls_x509_crt_set_subject_alt_name( - crt, GNUTLS_SAN_IPADDRESS, - data, len, GNUTLS_FSAN_APPEND); - if (err < 0) { - g_critical("Failed to set certificate alt name %s", - gnutls_strerror(err)); - abort(); - } - g_free(data); - } - if (req->ipaddr2) { - char *data; - int len; - - test_tls_get_ipaddr(req->ipaddr2, &data, &len); - - err = gnutls_x509_crt_set_subject_alt_name( - crt, GNUTLS_SAN_IPADDRESS, - data, len, GNUTLS_FSAN_APPEND); - if (err < 0) { - g_critical("Failed to set certificate alt name %s", - gnutls_strerror(err)); - abort(); - } - g_free(data); - } - - - /* - * Basic constraints are used to decide if the cert - * is for a CA or not. We can't use the convenient - * gnutls API for setting this, since it hardcodes - * the 'critical' field which we want control over - */ - if (req->basicConstraintsEnable) { - asn1_node ext = NULL; - - asn1_create_element(pkix_asn1, "PKIX1.BasicConstraints", &ext); - asn1_write_value(ext, "cA", - req->basicConstraintsIsCA ? "TRUE" : "FALSE", 1); - asn1_write_value(ext, "pathLenConstraint", NULL, 0); - test_tls_der_encode(ext, "", &der); - err = gnutls_x509_crt_set_extension_by_oid( - crt, "2.5.29.19", - der.data, der.size, - req->basicConstraintsCritical); - if (err < 0) { - g_critical("Failed to set certificate basic constraints %s", - gnutls_strerror(err)); - g_free(der.data); - abort(); - } - asn1_delete_structure(&ext); - g_free(der.data); - } - - /* - * Next up the key usage extension. Again we can't - * use the gnutls API since it hardcodes the extension - * to be 'critical' - */ - if (req->keyUsageEnable) { - asn1_node ext = NULL; - char str[2]; - - str[0] = req->keyUsageValue & 0xff; - str[1] = (req->keyUsageValue >> 8) & 0xff; - - asn1_create_element(pkix_asn1, "PKIX1.KeyUsage", &ext); - asn1_write_value(ext, "", str, 9); - test_tls_der_encode(ext, "", &der); - err = gnutls_x509_crt_set_extension_by_oid( - crt, "2.5.29.15", - der.data, der.size, - req->keyUsageCritical); - if (err < 0) { - g_critical("Failed to set certificate key usage %s", - gnutls_strerror(err)); - g_free(der.data); - abort(); - } - asn1_delete_structure(&ext); - g_free(der.data); - } - - /* - * Finally the key purpose extension. This time - * gnutls has the opposite problem, always hardcoding - * it to be non-critical. So once again we have to - * set this the hard way building up ASN1 data ourselves - */ - if (req->keyPurposeEnable) { - asn1_node ext = NULL; - - asn1_create_element(pkix_asn1, "PKIX1.ExtKeyUsageSyntax", &ext); - if (req->keyPurposeOID1) { - asn1_write_value(ext, "", "NEW", 1); - asn1_write_value(ext, "?LAST", req->keyPurposeOID1, 1); - } - if (req->keyPurposeOID2) { - asn1_write_value(ext, "", "NEW", 1); - asn1_write_value(ext, "?LAST", req->keyPurposeOID2, 1); - } - test_tls_der_encode(ext, "", &der); - err = gnutls_x509_crt_set_extension_by_oid( - crt, "2.5.29.37", - der.data, der.size, - req->keyPurposeCritical); - if (err < 0) { - g_critical("Failed to set certificate key purpose %s", - gnutls_strerror(err)); - g_free(der.data); - abort(); - } - asn1_delete_structure(&ext); - g_free(der.data); - } - - /* - * Any old serial number will do, so lets pick 5 - */ - err = gnutls_x509_crt_set_serial(crt, serial, 5); - if (err < 0) { - g_critical("Failed to set certificate serial %s", - gnutls_strerror(err)); - abort(); - } - - err = gnutls_x509_crt_set_activation_time(crt, start); - if (err < 0) { - g_critical("Failed to set certificate activation %s", - gnutls_strerror(err)); - abort(); - } - err = gnutls_x509_crt_set_expiration_time(crt, expire); - if (err < 0) { - g_critical("Failed to set certificate expiration %s", - gnutls_strerror(err)); - abort(); - } - - - /* - * If no 'ca' is set then we are self signing - * the cert. This is done for the root CA certs - */ - err = gnutls_x509_crt_sign2(crt, ca ? ca : crt, privkey, - GNUTLS_DIG_SHA256, 0); - if (err < 0) { - g_critical("Failed to sign certificate %s", - gnutls_strerror(err)); - abort(); - } - - /* - * Finally write the new cert out to disk - */ - err = gnutls_x509_crt_export( - crt, GNUTLS_X509_FMT_PEM, buffer, &size); - if (err < 0) { - g_critical("Failed to export certificate %s: %d", - gnutls_strerror(err), err); - abort(); - } - - if (!g_file_set_contents(req->filename, buffer, -1, NULL)) { - g_critical("Failed to write certificate %s", - req->filename); - abort(); - } - - req->crt = crt; -} - - -void test_tls_write_cert_chain(const char *filename, - gnutls_x509_crt_t *certs, - size_t ncerts) -{ - size_t i; - size_t capacity = 1024, offset = 0; - char *buffer = g_new0(char, capacity); - int err; - - for (i = 0; i < ncerts; i++) { - size_t len = capacity - offset; - retry: - err = gnutls_x509_crt_export(certs[i], GNUTLS_X509_FMT_PEM, - buffer + offset, &len); - if (err < 0) { - if (err == GNUTLS_E_SHORT_MEMORY_BUFFER) { - buffer = g_renew(char, buffer, offset + len); - capacity = offset + len; - goto retry; - } - g_critical("Failed to export certificate chain %s: %d", - gnutls_strerror(err), err); - abort(); - } - offset += len; - } - - if (!g_file_set_contents(filename, buffer, offset, NULL)) { - abort(); - } - g_free(buffer); -} - - -void test_tls_discard_cert(QCryptoTLSTestCertReq *req) -{ - if (!req->crt) { - return; - } - - gnutls_x509_crt_deinit(req->crt); - req->crt = NULL; - - if (getenv("QEMU_TEST_DEBUG_CERTS") == NULL) { - unlink(req->filename); - } -} - -#endif /* QCRYPTO_HAVE_TLS_TEST_SUPPORT */ diff --git a/tests/crypto-tls-x509-helpers.h b/tests/crypto-tls-x509-helpers.h deleted file mode 100644 index 8fcd7785ab..0000000000 --- a/tests/crypto-tls-x509-helpers.h +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (C) 2015 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see - * . - * - * Author: Daniel P. Berrange - */ - -#ifndef TESTS_CRYPTO_TLS_X509_HELPERS_H -#define TESTS_CRYPTO_TLS_X509_HELPERS_H - -#include -#include - -#if !(defined WIN32) && \ - defined(CONFIG_TASN1) -# define QCRYPTO_HAVE_TLS_TEST_SUPPORT -#endif - -#ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT -# include - - -/* - * This contains parameter about how to generate - * certificates. - */ -typedef struct QCryptoTLSTestCertReq QCryptoTLSTestCertReq; -struct QCryptoTLSTestCertReq { - gnutls_x509_crt_t crt; - - const char *filename; - - /* Identifying information */ - const char *country; - const char *cn; - const char *altname1; - const char *altname2; - const char *ipaddr1; - const char *ipaddr2; - - /* Basic constraints */ - bool basicConstraintsEnable; - bool basicConstraintsCritical; - bool basicConstraintsIsCA; - - /* Key usage */ - bool keyUsageEnable; - bool keyUsageCritical; - int keyUsageValue; - - /* Key purpose (aka Extended key usage) */ - bool keyPurposeEnable; - bool keyPurposeCritical; - const char *keyPurposeOID1; - const char *keyPurposeOID2; - - /* zero for current time, or non-zero for hours from now */ - int start_offset; - /* zero for 24 hours from now, or non-zero for hours from now */ - int expire_offset; -}; - -void test_tls_generate_cert(QCryptoTLSTestCertReq *req, - gnutls_x509_crt_t ca); -void test_tls_write_cert_chain(const char *filename, - gnutls_x509_crt_t *certs, - size_t ncerts); -void test_tls_discard_cert(QCryptoTLSTestCertReq *req); - -void test_tls_init(const char *keyfile); -void test_tls_cleanup(const char *keyfile); - -# define TLS_CERT_REQ(varname, cavarname, \ - country, commonname, \ - altname1, altname2, \ - ipaddr1, ipaddr2, \ - basicconsenable, basicconscritical, basicconsca, \ - keyusageenable, keyusagecritical, keyusagevalue, \ - keypurposeenable, keypurposecritical, \ - keypurposeoid1, keypurposeoid2, \ - startoffset, endoffset) \ - static QCryptoTLSTestCertReq varname = { \ - NULL, WORKDIR #varname "-ctx.pem", \ - country, commonname, altname1, altname2, \ - ipaddr1, ipaddr2, \ - basicconsenable, basicconscritical, basicconsca, \ - keyusageenable, keyusagecritical, keyusagevalue, \ - keypurposeenable, keypurposecritical, \ - keypurposeoid1, keypurposeoid2, \ - startoffset, endoffset \ - }; \ - test_tls_generate_cert(&varname, cavarname.crt) - -# define TLS_ROOT_REQ(varname, \ - country, commonname, \ - altname1, altname2, \ - ipaddr1, ipaddr2, \ - basicconsenable, basicconscritical, basicconsca, \ - keyusageenable, keyusagecritical, keyusagevalue, \ - keypurposeenable, keypurposecritical, \ - keypurposeoid1, keypurposeoid2, \ - startoffset, endoffset) \ - static QCryptoTLSTestCertReq varname = { \ - NULL, WORKDIR #varname "-ctx.pem", \ - country, commonname, altname1, altname2, \ - ipaddr1, ipaddr2, \ - basicconsenable, basicconscritical, basicconsca, \ - keyusageenable, keyusagecritical, keyusagevalue, \ - keypurposeenable, keypurposecritical, \ - keypurposeoid1, keypurposeoid2, \ - startoffset, endoffset \ - }; \ - test_tls_generate_cert(&varname, NULL) - -extern const asn1_static_node pkix_asn1_tab[]; - -#endif /* QCRYPTO_HAVE_TLS_TEST_SUPPORT */ - -#endif diff --git a/tests/io-channel-helpers.c b/tests/io-channel-helpers.c deleted file mode 100644 index ff156ed3c4..0000000000 --- a/tests/io-channel-helpers.c +++ /dev/null @@ -1,163 +0,0 @@ -/* - * QEMU I/O channel test helpers - * - * Copyright (c) 2015 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - * - */ - -#include "qemu/osdep.h" -#include "io-channel-helpers.h" -#include "qemu/iov.h" - -struct QIOChannelTest { - QIOChannel *src; - QIOChannel *dst; - bool blocking; - size_t len; - size_t niov; - char *input; - struct iovec *inputv; - char *output; - struct iovec *outputv; - Error *writeerr; - Error *readerr; -}; - - -/* This thread sends all data using iovecs */ -static gpointer test_io_thread_writer(gpointer opaque) -{ - QIOChannelTest *data = opaque; - - qio_channel_set_blocking(data->src, data->blocking, NULL); - - qio_channel_writev_all(data->src, - data->inputv, - data->niov, - &data->writeerr); - - return NULL; -} - - -/* This thread receives all data using iovecs */ -static gpointer test_io_thread_reader(gpointer opaque) -{ - QIOChannelTest *data = opaque; - - qio_channel_set_blocking(data->dst, data->blocking, NULL); - - qio_channel_readv_all(data->dst, - data->outputv, - data->niov, - &data->readerr); - - return NULL; -} - - -QIOChannelTest *qio_channel_test_new(void) -{ - QIOChannelTest *data = g_new0(QIOChannelTest, 1); - size_t i; - size_t offset; - - - /* We'll send 1 MB of data */ -#define CHUNK_COUNT 250 -#define CHUNK_LEN 4194 - - data->len = CHUNK_COUNT * CHUNK_LEN; - data->input = g_new0(char, data->len); - data->output = g_new0(gchar, data->len); - - /* Fill input with a pattern */ - for (i = 0; i < data->len; i += CHUNK_LEN) { - memset(data->input + i, (i / CHUNK_LEN), CHUNK_LEN); - } - - /* We'll split the data across a bunch of IO vecs */ - data->niov = CHUNK_COUNT; - data->inputv = g_new0(struct iovec, data->niov); - data->outputv = g_new0(struct iovec, data->niov); - - for (i = 0, offset = 0; i < data->niov; i++, offset += CHUNK_LEN) { - data->inputv[i].iov_base = data->input + offset; - data->outputv[i].iov_base = data->output + offset; - data->inputv[i].iov_len = CHUNK_LEN; - data->outputv[i].iov_len = CHUNK_LEN; - } - - return data; -} - -void qio_channel_test_run_threads(QIOChannelTest *test, - bool blocking, - QIOChannel *src, - QIOChannel *dst) -{ - GThread *reader, *writer; - - test->src = src; - test->dst = dst; - test->blocking = blocking; - - reader = g_thread_new("reader", - test_io_thread_reader, - test); - writer = g_thread_new("writer", - test_io_thread_writer, - test); - - g_thread_join(reader); - g_thread_join(writer); - - test->dst = test->src = NULL; -} - - -void qio_channel_test_run_writer(QIOChannelTest *test, - QIOChannel *src) -{ - test->src = src; - test_io_thread_writer(test); - test->src = NULL; -} - - -void qio_channel_test_run_reader(QIOChannelTest *test, - QIOChannel *dst) -{ - test->dst = dst; - test_io_thread_reader(test); - test->dst = NULL; -} - - -void qio_channel_test_validate(QIOChannelTest *test) -{ - g_assert(test->readerr == NULL); - g_assert(test->writeerr == NULL); - g_assert_cmpint(memcmp(test->input, - test->output, - test->len), ==, 0); - - g_free(test->inputv); - g_free(test->outputv); - g_free(test->input); - g_free(test->output); - g_free(test); -} diff --git a/tests/io-channel-helpers.h b/tests/io-channel-helpers.h deleted file mode 100644 index 3d14043710..0000000000 --- a/tests/io-channel-helpers.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * QEMU I/O channel test helpers - * - * Copyright (c) 2015 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - */ - -#ifndef TEST_IO_CHANNEL_HELPERS_H -#define TEST_IO_CHANNEL_HELPERS_H - -#include "io/channel.h" - -typedef struct QIOChannelTest QIOChannelTest; - -QIOChannelTest *qio_channel_test_new(void); - -void qio_channel_test_run_threads(QIOChannelTest *test, - bool blocking, - QIOChannel *src, - QIOChannel *dst); - -void qio_channel_test_run_writer(QIOChannelTest *test, - QIOChannel *src); -void qio_channel_test_run_reader(QIOChannelTest *test, - QIOChannel *dst); - -void qio_channel_test_validate(QIOChannelTest *test); - -#endif /* TEST_IO_CHANNEL_HELPERS_H */ diff --git a/tests/iothread.c b/tests/iothread.c deleted file mode 100644 index afde12b4ef..0000000000 --- a/tests/iothread.c +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Event loop thread implementation for unit tests - * - * Copyright Red Hat Inc., 2013, 2016 - * - * Authors: - * Stefan Hajnoczi - * Paolo Bonzini - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "block/aio.h" -#include "qemu/main-loop.h" -#include "qemu/rcu.h" -#include "iothread.h" - -struct IOThread { - AioContext *ctx; - GMainContext *worker_context; - GMainLoop *main_loop; - - QemuThread thread; - QemuMutex init_done_lock; - QemuCond init_done_cond; /* is thread initialization done? */ - bool stopping; -}; - -static __thread IOThread *my_iothread; - -AioContext *qemu_get_current_aio_context(void) -{ - return my_iothread ? my_iothread->ctx : qemu_get_aio_context(); -} - -static void iothread_init_gcontext(IOThread *iothread) -{ - GSource *source; - - iothread->worker_context = g_main_context_new(); - source = aio_get_g_source(iothread_get_aio_context(iothread)); - g_source_attach(source, iothread->worker_context); - g_source_unref(source); - iothread->main_loop = g_main_loop_new(iothread->worker_context, TRUE); -} - -static void *iothread_run(void *opaque) -{ - IOThread *iothread = opaque; - - rcu_register_thread(); - - my_iothread = iothread; - qemu_mutex_lock(&iothread->init_done_lock); - iothread->ctx = aio_context_new(&error_abort); - - /* - * We must connect the ctx to a GMainContext, because in older versions - * of glib the g_source_ref()/unref() functions are not threadsafe - * on sources without a context. - */ - iothread_init_gcontext(iothread); - - /* - * g_main_context_push_thread_default() must be called before anything - * in this new thread uses glib. - */ - g_main_context_push_thread_default(iothread->worker_context); - - qemu_cond_signal(&iothread->init_done_cond); - qemu_mutex_unlock(&iothread->init_done_lock); - - while (!qatomic_read(&iothread->stopping)) { - aio_poll(iothread->ctx, true); - } - - g_main_context_pop_thread_default(iothread->worker_context); - rcu_unregister_thread(); - return NULL; -} - -static void iothread_stop_bh(void *opaque) -{ - IOThread *iothread = opaque; - - iothread->stopping = true; -} - -void iothread_join(IOThread *iothread) -{ - aio_bh_schedule_oneshot(iothread->ctx, iothread_stop_bh, iothread); - qemu_thread_join(&iothread->thread); - g_main_context_unref(iothread->worker_context); - g_main_loop_unref(iothread->main_loop); - qemu_cond_destroy(&iothread->init_done_cond); - qemu_mutex_destroy(&iothread->init_done_lock); - aio_context_unref(iothread->ctx); - g_free(iothread); -} - -IOThread *iothread_new(void) -{ - IOThread *iothread = g_new0(IOThread, 1); - - qemu_mutex_init(&iothread->init_done_lock); - qemu_cond_init(&iothread->init_done_cond); - qemu_thread_create(&iothread->thread, NULL, iothread_run, - iothread, QEMU_THREAD_JOINABLE); - - /* Wait for initialization to complete */ - qemu_mutex_lock(&iothread->init_done_lock); - while (iothread->ctx == NULL) { - qemu_cond_wait(&iothread->init_done_cond, - &iothread->init_done_lock); - } - qemu_mutex_unlock(&iothread->init_done_lock); - return iothread; -} - -AioContext *iothread_get_aio_context(IOThread *iothread) -{ - return iothread->ctx; -} diff --git a/tests/iothread.h b/tests/iothread.h deleted file mode 100644 index 4877cea6a3..0000000000 --- a/tests/iothread.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Event loop thread implementation for unit tests - * - * Copyright Red Hat Inc., 2013, 2016 - * - * Authors: - * Stefan Hajnoczi - * Paolo Bonzini - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ -#ifndef TEST_IOTHREAD_H -#define TEST_IOTHREAD_H - -#include "block/aio.h" -#include "qemu/thread.h" - -typedef struct IOThread IOThread; - -IOThread *iothread_new(void); -void iothread_join(IOThread *iothread); -AioContext *iothread_get_aio_context(IOThread *iothread); - -#endif diff --git a/tests/meson.build b/tests/meson.build index 656d211e25..af43fd1eaf 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -69,59 +69,6 @@ endforeach libtestqapi = static_library('testqapi', sources: [genh, test_qapi_sources]) testqapi = declare_dependency(link_with: libtestqapi, sources: [genh, test_qapi_headers]) -testblock = declare_dependency(dependencies: [block], sources: 'iothread.c') - -tests = { - 'check-block-qdict': [], - 'check-qdict': [], - 'check-qnum': [], - 'check-qstring': [], - 'check-qlist': [], - 'check-qnull': [], - 'check-qobject': [], - 'check-qjson': [], - 'check-qlit': [], - 'test-qobject-output-visitor': [testqapi], - 'test-clone-visitor': [testqapi], - 'test-qobject-input-visitor': [testqapi], - 'test-string-input-visitor': [testqapi], - 'test-string-output-visitor': [testqapi], - 'test-opts-visitor': [testqapi], - 'test-visitor-serialization': [testqapi], - 'test-bitmap': [], - # all code tested by test-x86-cpuid is inside topology.h - 'test-x86-cpuid': [], - 'test-cutils': [], - 'test-shift128': [], - 'test-mul64': [], - # all code tested by test-int128 is inside int128.h - 'test-int128': [], - 'rcutorture': [], - 'test-rcu-list': [], - 'test-rcu-simpleq': [], - 'test-rcu-tailq': [], - 'test-rcu-slist': [], - 'test-qdist': [], - 'test-qht': [], - 'test-bitops': [], - 'test-bitcnt': [], - 'test-qgraph': ['qtest/libqos/qgraph.c'], - 'check-qom-interface': [qom], - 'check-qom-proplist': [qom], - 'test-qemu-opts': [], - 'test-keyval': [testqapi], - 'test-logging': [], - 'test-uuid': [], - 'ptimer-test': ['ptimer-test-stubs.c', meson.source_root() / 'hw/core/ptimer.c'], - 'test-qapi-util': [], -} - -if have_system or have_tools - tests += { - 'test-qmp-event': [testqapi], - } -endif - test_deps = { 'test-qht-par': qht_bench, } @@ -129,65 +76,6 @@ test_deps = { benchs = {} if have_block - tests += { - 'test-coroutine': [testblock], - 'test-aio': [testblock], - 'test-aio-multithread': [testblock], - 'test-throttle': [testblock], - 'test-thread-pool': [testblock], - 'test-hbitmap': [testblock], - 'test-bdrv-drain': [testblock], - 'test-bdrv-graph-mod': [testblock], - 'test-blockjob': [testblock], - 'test-blockjob-txn': [testblock], - 'test-block-backend': [testblock], - 'test-block-iothread': [testblock], - 'test-write-threshold': [testblock], - 'test-crypto-hash': [crypto], - 'test-crypto-hmac': [crypto], - 'test-crypto-cipher': [crypto], - 'test-crypto-secret': [crypto, keyutils], - 'test-authz-simple': [authz], - 'test-authz-list': [authz], - 'test-authz-listfile': [authz], - 'test-io-task': [testblock], - 'test-io-channel-socket': ['socket-helpers.c', 'io-channel-helpers.c', io], - 'test-io-channel-file': ['io-channel-helpers.c', io], - 'test-io-channel-command': ['io-channel-helpers.c', io], - 'test-io-channel-buffer': ['io-channel-helpers.c', io], - 'test-crypto-ivgen': [io], - 'test-crypto-afsplit': [io], - 'test-crypto-block': [io], - } - if 'CONFIG_GNUTLS' in config_host and \ - 'CONFIG_TASN1' in config_host and \ - 'CONFIG_POSIX' in config_host - tests += { - 'test-crypto-tlscredsx509': ['crypto-tls-x509-helpers.c', 'pkix_asn1_tab.c', - tasn1, crypto, gnutls], - 'test-crypto-tlssession': ['crypto-tls-x509-helpers.c', 'pkix_asn1_tab.c', 'crypto-tls-psk-helpers.c', - tasn1, crypto, gnutls], - 'test-io-channel-tls': ['io-channel-helpers.c', 'crypto-tls-x509-helpers.c', 'pkix_asn1_tab.c', - tasn1, io, crypto, gnutls]} - endif - if 'CONFIG_AUTH_PAM' in config_host - tests += {'test-authz-pam': [authz]} - endif - if 'CONFIG_QEMU_PRIVATE_XTS' in config_host - tests += {'test-crypto-xts': [crypto, io]} - endif - if 'CONFIG_POSIX' in config_host - tests += {'test-image-locking': [testblock]} - endif - if 'CONFIG_REPLICATION' in config_host - tests += {'test-replication': [testblock]} - endif - if 'CONFIG_NETTLE' in config_host or 'CONFIG_GCRYPT' in config_host - tests += {'test-crypto-pbkdf': [io]} - endif - if 'CONFIG_EPOLL_CREATE1' in config_host - tests += {'test-fdmon-epoll': [testblock]} - endif benchs += { 'benchmark-crypto-hash': [crypto], 'benchmark-crypto-hmac': [crypto], @@ -195,75 +83,6 @@ if have_block } endif -if have_system - tests += { - 'test-iov': [], - 'test-qmp-cmds': [testqapi], - 'test-xbzrle': [migration], - 'test-timed-average': [], - 'test-util-sockets': ['socket-helpers.c'], - 'test-base64': [], - 'test-bufferiszero': [], - 'test-vmstate': [migration, io] - } - if 'CONFIG_INOTIFY1' in config_host - tests += {'test-util-filemonitor': []} - endif - - # Some tests: test-char, test-qdev-global-props, and test-qga, - # are not runnable under TSan due to a known issue. - # https://github.com/google/sanitizers/issues/1116 - if 'CONFIG_TSAN' not in config_host - if 'CONFIG_POSIX' in config_host - tests += { - 'test-char': ['socket-helpers.c', qom, io, chardev] - } - endif - - tests += { - 'test-qdev-global-props': [qom, hwcore, testqapi] - } - endif -endif - -if 'CONFIG_TSAN' not in config_host and \ - 'CONFIG_GUEST_AGENT' in config_host and \ - 'CONFIG_LINUX' in config_host - tests += {'test-qga': ['qtest/libqtest.c']} - test_deps += {'test-qga': qga} -endif - -test_env = environment() -test_env.set('G_TEST_SRCDIR', meson.current_source_dir()) -test_env.set('G_TEST_BUILDDIR', meson.current_build_dir()) - -slow_tests = { - 'test-crypto-tlscredsx509': 45, - 'test-crypto-tlssession': 45 -} - -foreach test_name, extra: tests - src = [test_name + '.c'] - deps = [qemuutil] - if extra.length() > 0 - # use a sourceset to quickly separate sources and deps - test_ss = ss.source_set() - test_ss.add(extra) - src += test_ss.all_sources() - deps += test_ss.all_dependencies() - endif - exe = executable(test_name, src, genh, dependencies: deps) - - test(test_name, exe, - depends: test_deps.get(test_name, []), - env: test_env, - args: ['--tap', '-k'], - protocol: 'tap', - timeout: slow_tests.get(test_name, 30), - priority: slow_tests.get(test_name, 30), - suite: ['unit']) -endforeach - foreach bench_name, deps: benchs exe = executable(bench_name, bench_name + '.c', dependencies: [qemuutil] + deps) @@ -299,6 +118,7 @@ if not get_option('tcg').disabled() endif endif +subdir('unit') subdir('qapi-schema') subdir('qtest') subdir('migration') diff --git a/tests/pkix_asn1_tab.c b/tests/pkix_asn1_tab.c deleted file mode 100644 index 4aaf736d3f..0000000000 --- a/tests/pkix_asn1_tab.c +++ /dev/null @@ -1,1108 +0,0 @@ -/* - * This file is taken from gnutls 1.6.3 under the GPLv2+ - * and is under copyright of various GNUTLS contributors. - */ - -#include "qemu/osdep.h" -#include "tests/crypto-tls-x509-helpers.h" - -#ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT - -const asn1_static_node pkix_asn1_tab[] = { - {"PKIX1", 536875024, 0}, - {0, 1073741836, 0}, - {"id-ce", 1879048204, 0}, - {"joint-iso-ccitt", 1073741825, "2"}, - {"ds", 1073741825, "5"}, - {0, 1, "29"}, - {"id-ce-authorityKeyIdentifier", 1879048204, 0}, - {0, 1073741825, "id-ce"}, - {0, 1, "35"}, - {"AuthorityKeyIdentifier", 1610612741, 0}, - {"keyIdentifier", 1610637314, "KeyIdentifier"}, - {0, 4104, "0"}, - {"authorityCertIssuer", 1610637314, "GeneralNames"}, - {0, 4104, "1"}, - {"authorityCertSerialNumber", 536895490, "CertificateSerialNumber"}, - {0, 4104, "2"}, - {"KeyIdentifier", 1073741831, 0}, - {"id-ce-subjectKeyIdentifier", 1879048204, 0}, - {0, 1073741825, "id-ce"}, - {0, 1, "14"}, - {"SubjectKeyIdentifier", 1073741826, "KeyIdentifier"}, - {"id-ce-keyUsage", 1879048204, 0}, - {0, 1073741825, "id-ce"}, - {0, 1, "15"}, - {"KeyUsage", 1610874886, 0}, - {"digitalSignature", 1073741825, "0"}, - {"nonRepudiation", 1073741825, "1"}, - {"keyEncipherment", 1073741825, "2"}, - {"dataEncipherment", 1073741825, "3"}, - {"keyAgreement", 1073741825, "4"}, - {"keyCertSign", 1073741825, "5"}, - {"cRLSign", 1073741825, "6"}, - {"encipherOnly", 1073741825, "7"}, - {"decipherOnly", 1, "8"}, - {"id-ce-privateKeyUsagePeriod", 1879048204, 0}, - {0, 1073741825, "id-ce"}, - {0, 1, "16"}, - {"PrivateKeyUsagePeriod", 1610612741, 0}, - {"notBefore", 1619025937, 0}, - {0, 4104, "0"}, - {"notAfter", 545284113, 0}, - {0, 4104, "1"}, - {"id-ce-certificatePolicies", 1879048204, 0}, - {0, 1073741825, "id-ce"}, - {0, 1, "32"}, - {"CertificatePolicies", 1612709899, 0}, - {"MAX", 1074266122, "1"}, - {0, 2, "PolicyInformation"}, - {"PolicyInformation", 1610612741, 0}, - {"policyIdentifier", 1073741826, "CertPolicyId"}, - {"policyQualifiers", 538984459, 0}, - {"MAX", 1074266122, "1"}, - {0, 2, "PolicyQualifierInfo"}, - {"CertPolicyId", 1073741836, 0}, - {"PolicyQualifierInfo", 1610612741, 0}, - {"policyQualifierId", 1073741826, "PolicyQualifierId"}, - {"qualifier", 541065229, 0}, - {"policyQualifierId", 1, 0}, - {"PolicyQualifierId", 1073741836, 0}, - {"CPSuri", 1073741826, "IA5String"}, - {"UserNotice", 1610612741, 0}, - {"noticeRef", 1073758210, "NoticeReference"}, - {"explicitText", 16386, "DisplayText"}, - {"NoticeReference", 1610612741, 0}, - {"organization", 1073741826, "DisplayText"}, - {"noticeNumbers", 536870923, 0}, - {0, 3, 0}, - {"DisplayText", 1610612754, 0}, - {"visibleString", 1612709890, "VisibleString"}, - {"200", 524298, "1"}, - {"bmpString", 1612709890, "BMPString"}, - {"200", 524298, "1"}, - {"utf8String", 538968066, "UTF8String"}, - {"200", 524298, "1"}, - {"id-ce-policyMappings", 1879048204, 0}, - {0, 1073741825, "id-ce"}, - {0, 1, "33"}, - {"PolicyMappings", 1612709899, 0}, - {"MAX", 1074266122, "1"}, - {0, 536870917, 0}, - {"issuerDomainPolicy", 1073741826, "CertPolicyId"}, - {"subjectDomainPolicy", 2, "CertPolicyId"}, - {"DirectoryString", 1610612754, 0}, - {"teletexString", 1612709890, "TeletexString"}, - {"MAX", 524298, "1"}, - {"printableString", 1612709890, "PrintableString"}, - {"MAX", 524298, "1"}, - {"universalString", 1612709890, "UniversalString"}, - {"MAX", 524298, "1"}, - {"utf8String", 1612709890, "UTF8String"}, - {"MAX", 524298, "1"}, - {"bmpString", 1612709890, "BMPString"}, - {"MAX", 524298, "1"}, - {"ia5String", 538968066, "IA5String"}, - {"MAX", 524298, "1"}, - {"id-ce-subjectAltName", 1879048204, 0}, - {0, 1073741825, "id-ce"}, - {0, 1, "17"}, - {"SubjectAltName", 1073741826, "GeneralNames"}, - {"GeneralNames", 1612709899, 0}, - {"MAX", 1074266122, "1"}, - {0, 2, "GeneralName"}, - {"GeneralName", 1610612754, 0}, - {"otherName", 1610620930, "AnotherName"}, - {0, 4104, "0"}, - {"rfc822Name", 1610620930, "IA5String"}, - {0, 4104, "1"}, - {"dNSName", 1610620930, "IA5String"}, - {0, 4104, "2"}, - {"x400Address", 1610620930, "ORAddress"}, - {0, 4104, "3"}, - {"directoryName", 1610620930, "RDNSequence"}, - {0, 2056, "4"}, - {"ediPartyName", 1610620930, "EDIPartyName"}, - {0, 4104, "5"}, - {"uniformResourceIdentifier", 1610620930, "IA5String"}, - {0, 4104, "6"}, - {"iPAddress", 1610620935, 0}, - {0, 4104, "7"}, - {"registeredID", 536879116, 0}, - {0, 4104, "8"}, - {"AnotherName", 1610612741, 0}, - {"type-id", 1073741836, 0}, - {"value", 541073421, 0}, - {0, 1073743880, "0"}, - {"type-id", 1, 0}, - {"EDIPartyName", 1610612741, 0}, - {"nameAssigner", 1610637314, "DirectoryString"}, - {0, 4104, "0"}, - {"partyName", 536879106, "DirectoryString"}, - {0, 4104, "1"}, - {"id-ce-issuerAltName", 1879048204, 0}, - {0, 1073741825, "id-ce"}, - {0, 1, "18"}, - {"IssuerAltName", 1073741826, "GeneralNames"}, - {"id-ce-subjectDirectoryAttributes", 1879048204, 0}, - {0, 1073741825, "id-ce"}, - {0, 1, "9"}, - {"SubjectDirectoryAttributes", 1612709899, 0}, - {"MAX", 1074266122, "1"}, - {0, 2, "Attribute"}, - {"id-ce-basicConstraints", 1879048204, 0}, - {0, 1073741825, "id-ce"}, - {0, 1, "19"}, - {"BasicConstraints", 1610612741, 0}, - {"cA", 1610645508, 0}, - {0, 131081, 0}, - {"pathLenConstraint", 537411587, 0}, - {"0", 10, "MAX"}, - {"id-ce-nameConstraints", 1879048204, 0}, - {0, 1073741825, "id-ce"}, - {0, 1, "30"}, - {"NameConstraints", 1610612741, 0}, - {"permittedSubtrees", 1610637314, "GeneralSubtrees"}, - {0, 4104, "0"}, - {"excludedSubtrees", 536895490, "GeneralSubtrees"}, - {0, 4104, "1"}, - {"GeneralSubtrees", 1612709899, 0}, - {"MAX", 1074266122, "1"}, - {0, 2, "GeneralSubtree"}, - {"GeneralSubtree", 1610612741, 0}, - {"base", 1073741826, "GeneralName"}, - {"minimum", 1610653698, "BaseDistance"}, - {0, 1073741833, "0"}, - {0, 4104, "0"}, - {"maximum", 536895490, "BaseDistance"}, - {0, 4104, "1"}, - {"BaseDistance", 1611137027, 0}, - {"0", 10, "MAX"}, - {"id-ce-policyConstraints", 1879048204, 0}, - {0, 1073741825, "id-ce"}, - {0, 1, "36"}, - {"PolicyConstraints", 1610612741, 0}, - {"requireExplicitPolicy", 1610637314, "SkipCerts"}, - {0, 4104, "0"}, - {"inhibitPolicyMapping", 536895490, "SkipCerts"}, - {0, 4104, "1"}, - {"SkipCerts", 1611137027, 0}, - {"0", 10, "MAX"}, - {"id-ce-cRLDistributionPoints", 1879048204, 0}, - {0, 1073741825, "id-ce"}, - {0, 1, "31"}, - {"CRLDistributionPoints", 1612709899, 0}, - {"MAX", 1074266122, "1"}, - {0, 2, "DistributionPoint"}, - {"DistributionPoint", 1610612741, 0}, - {"distributionPoint", 1610637314, "DistributionPointName"}, - {0, 2056, "0"}, - {"reasons", 1610637314, "ReasonFlags"}, - {0, 4104, "1"}, - {"cRLIssuer", 536895490, "GeneralNames"}, - {0, 4104, "2"}, - {"DistributionPointName", 1610612754, 0}, - {"fullName", 1610620930, "GeneralNames"}, - {0, 4104, "0"}, - {"nameRelativeToCRLIssuer", 536879106, "RelativeDistinguishedName"}, - {0, 4104, "1"}, - {"ReasonFlags", 1610874886, 0}, - {"unused", 1073741825, "0"}, - {"keyCompromise", 1073741825, "1"}, - {"cACompromise", 1073741825, "2"}, - {"affiliationChanged", 1073741825, "3"}, - {"superseded", 1073741825, "4"}, - {"cessationOfOperation", 1073741825, "5"}, - {"certificateHold", 1073741825, "6"}, - {"privilegeWithdrawn", 1073741825, "7"}, - {"aACompromise", 1, "8"}, - {"id-ce-extKeyUsage", 1879048204, 0}, - {0, 1073741825, "id-ce"}, - {0, 1, "37"}, - {"ExtKeyUsageSyntax", 1612709899, 0}, - {"MAX", 1074266122, "1"}, - {0, 2, "KeyPurposeId"}, - {"KeyPurposeId", 1073741836, 0}, - {"id-kp-serverAuth", 1879048204, 0}, - {0, 1073741825, "id-kp"}, - {0, 1, "1"}, - {"id-kp-clientAuth", 1879048204, 0}, - {0, 1073741825, "id-kp"}, - {0, 1, "2"}, - {"id-kp-codeSigning", 1879048204, 0}, - {0, 1073741825, "id-kp"}, - {0, 1, "3"}, - {"id-kp-emailProtection", 1879048204, 0}, - {0, 1073741825, "id-kp"}, - {0, 1, "4"}, - {"id-kp-ipsecEndSystem", 1879048204, 0}, - {0, 1073741825, "id-kp"}, - {0, 1, "5"}, - {"id-kp-ipsecTunnel", 1879048204, 0}, - {0, 1073741825, "id-kp"}, - {0, 1, "6"}, - {"id-kp-ipsecUser", 1879048204, 0}, - {0, 1073741825, "id-kp"}, - {0, 1, "7"}, - {"id-kp-timeStamping", 1879048204, 0}, - {0, 1073741825, "id-kp"}, - {0, 1, "8"}, - {"id-pe-authorityInfoAccess", 1879048204, 0}, - {0, 1073741825, "id-pe"}, - {0, 1, "1"}, - {"AuthorityInfoAccessSyntax", 1612709899, 0}, - {"MAX", 1074266122, "1"}, - {0, 2, "AccessDescription"}, - {"AccessDescription", 1610612741, 0}, - {"accessMethod", 1073741836, 0}, - {"accessLocation", 2, "GeneralName"}, - {"id-ce-cRLNumber", 1879048204, 0}, - {0, 1073741825, "id-ce"}, - {0, 1, "20"}, - {"CRLNumber", 1611137027, 0}, - {"0", 10, "MAX"}, - {"id-ce-issuingDistributionPoint", 1879048204, 0}, - {0, 1073741825, "id-ce"}, - {0, 1, "28"}, - {"IssuingDistributionPoint", 1610612741, 0}, - {"distributionPoint", 1610637314, "DistributionPointName"}, - {0, 4104, "0"}, - {"onlyContainsUserCerts", 1610653700, 0}, - {0, 1073872905, 0}, - {0, 4104, "1"}, - {"onlyContainsCACerts", 1610653700, 0}, - {0, 1073872905, 0}, - {0, 4104, "2"}, - {"onlySomeReasons", 1610637314, "ReasonFlags"}, - {0, 4104, "3"}, - {"indirectCRL", 536911876, 0}, - {0, 1073872905, 0}, - {0, 4104, "4"}, - {"id-ce-deltaCRLIndicator", 1879048204, 0}, - {0, 1073741825, "id-ce"}, - {0, 1, "27"}, - {"BaseCRLNumber", 1073741826, "CRLNumber"}, - {"id-ce-cRLReasons", 1879048204, 0}, - {0, 1073741825, "id-ce"}, - {0, 1, "21"}, - {"CRLReason", 1610874901, 0}, - {"unspecified", 1073741825, "0"}, - {"keyCompromise", 1073741825, "1"}, - {"cACompromise", 1073741825, "2"}, - {"affiliationChanged", 1073741825, "3"}, - {"superseded", 1073741825, "4"}, - {"cessationOfOperation", 1073741825, "5"}, - {"certificateHold", 1073741825, "6"}, - {"removeFromCRL", 1, "8"}, - {"id-ce-certificateIssuer", 1879048204, 0}, - {0, 1073741825, "id-ce"}, - {0, 1, "29"}, - {"CertificateIssuer", 1073741826, "GeneralNames"}, - {"id-ce-holdInstructionCode", 1879048204, 0}, - {0, 1073741825, "id-ce"}, - {0, 1, "23"}, - {"HoldInstructionCode", 1073741836, 0}, - {"holdInstruction", 1879048204, 0}, - {"joint-iso-itu-t", 1073741825, "2"}, - {"member-body", 1073741825, "2"}, - {"us", 1073741825, "840"}, - {"x9cm", 1073741825, "10040"}, - {0, 1, "2"}, - {"id-holdinstruction-none", 1879048204, 0}, - {0, 1073741825, "holdInstruction"}, - {0, 1, "1"}, - {"id-holdinstruction-callissuer", 1879048204, 0}, - {0, 1073741825, "holdInstruction"}, - {0, 1, "2"}, - {"id-holdinstruction-reject", 1879048204, 0}, - {0, 1073741825, "holdInstruction"}, - {0, 1, "3"}, - {"id-ce-invalidityDate", 1879048204, 0}, - {0, 1073741825, "id-ce"}, - {0, 1, "24"}, - {"InvalidityDate", 1082130449, 0}, - {"VisibleString", 1610620935, 0}, - {0, 4360, "26"}, - {"NumericString", 1610620935, 0}, - {0, 4360, "18"}, - {"IA5String", 1610620935, 0}, - {0, 4360, "22"}, - {"TeletexString", 1610620935, 0}, - {0, 4360, "20"}, - {"PrintableString", 1610620935, 0}, - {0, 4360, "19"}, - {"UniversalString", 1610620935, 0}, - {0, 4360, "28"}, - {"BMPString", 1610620935, 0}, - {0, 4360, "30"}, - {"UTF8String", 1610620935, 0}, - {0, 4360, "12"}, - {"id-pkix", 1879048204, 0}, - {"iso", 1073741825, "1"}, - {"identified-organization", 1073741825, "3"}, - {"dod", 1073741825, "6"}, - {"internet", 1073741825, "1"}, - {"security", 1073741825, "5"}, - {"mechanisms", 1073741825, "5"}, - {"pkix", 1, "7"}, - {"id-pe", 1879048204, 0}, - {0, 1073741825, "id-pkix"}, - {0, 1, "1"}, - {"id-qt", 1879048204, 0}, - {0, 1073741825, "id-pkix"}, - {0, 1, "2"}, - {"id-kp", 1879048204, 0}, - {0, 1073741825, "id-pkix"}, - {0, 1, "3"}, - {"id-ad", 1879048204, 0}, - {0, 1073741825, "id-pkix"}, - {0, 1, "48"}, - {"id-qt-cps", 1879048204, 0}, - {0, 1073741825, "id-qt"}, - {0, 1, "1"}, - {"id-qt-unotice", 1879048204, 0}, - {0, 1073741825, "id-qt"}, - {0, 1, "2"}, - {"id-ad-ocsp", 1879048204, 0}, - {0, 1073741825, "id-ad"}, - {0, 1, "1"}, - {"id-ad-caIssuers", 1879048204, 0}, - {0, 1073741825, "id-ad"}, - {0, 1, "2"}, - {"Attribute", 1610612741, 0}, - {"type", 1073741826, "AttributeType"}, - {"values", 536870927, 0}, - {0, 2, "AttributeValue"}, - {"AttributeType", 1073741836, 0}, - {"AttributeValue", 1614807053, 0}, - {"type", 1, 0}, - {"AttributeTypeAndValue", 1610612741, 0}, - {"type", 1073741826, "AttributeType"}, - {"value", 2, "AttributeValue"}, - {"id-at", 1879048204, 0}, - {"joint-iso-ccitt", 1073741825, "2"}, - {"ds", 1073741825, "5"}, - {0, 1, "4"}, - {"id-at-initials", 1880096780, "AttributeType"}, - {0, 1073741825, "id-at"}, - {0, 1, "43"}, - {"X520initials", 1073741826, "DirectoryString"}, - {"id-at-generationQualifier", 1880096780, "AttributeType"}, - {0, 1073741825, "id-at"}, - {0, 1, "44"}, - {"X520generationQualifier", 1073741826, "DirectoryString"}, - {"id-at-surname", 1880096780, "AttributeType"}, - {0, 1073741825, "id-at"}, - {0, 1, "4"}, - {"X520surName", 1073741826, "DirectoryString"}, - {"id-at-givenName", 1880096780, "AttributeType"}, - {0, 1073741825, "id-at"}, - {0, 1, "42"}, - {"X520givenName", 1073741826, "DirectoryString"}, - {"id-at-name", 1880096780, "AttributeType"}, - {0, 1073741825, "id-at"}, - {0, 1, "41"}, - {"X520name", 1073741826, "DirectoryString"}, - {"id-at-commonName", 1880096780, "AttributeType"}, - {0, 1073741825, "id-at"}, - {0, 1, "3"}, - {"X520CommonName", 1073741826, "DirectoryString"}, - {"id-at-localityName", 1880096780, "AttributeType"}, - {0, 1073741825, "id-at"}, - {0, 1, "7"}, - {"X520LocalityName", 1073741826, "DirectoryString"}, - {"id-at-stateOrProvinceName", 1880096780, "AttributeType"}, - {0, 1073741825, "id-at"}, - {0, 1, "8"}, - {"X520StateOrProvinceName", 1073741826, "DirectoryString"}, - {"id-at-organizationName", 1880096780, "AttributeType"}, - {0, 1073741825, "id-at"}, - {0, 1, "10"}, - {"X520OrganizationName", 1073741826, "DirectoryString"}, - {"id-at-organizationalUnitName", 1880096780, "AttributeType"}, - {0, 1073741825, "id-at"}, - {0, 1, "11"}, - {"X520OrganizationalUnitName", 1073741826, "DirectoryString"}, - {"id-at-title", 1880096780, "AttributeType"}, - {0, 1073741825, "id-at"}, - {0, 1, "12"}, - {"X520Title", 1073741826, "DirectoryString"}, - {"id-at-description", 1880096780, "AttributeType"}, - {0, 1073741825, "id-at"}, - {0, 1, "13"}, - {"X520Description", 1073741826, "DirectoryString"}, - {"id-at-dnQualifier", 1880096780, "AttributeType"}, - {0, 1073741825, "id-at"}, - {0, 1, "46"}, - {"X520dnQualifier", 1073741826, "PrintableString"}, - {"id-at-countryName", 1880096780, "AttributeType"}, - {0, 1073741825, "id-at"}, - {0, 1, "6"}, - {"X520countryName", 1612709890, "PrintableString"}, - {0, 1048586, "2"}, - {"id-at-serialNumber", 1880096780, "AttributeType"}, - {0, 1073741825, "id-at"}, - {0, 1, "5"}, - {"X520serialNumber", 1073741826, "PrintableString"}, - {"id-at-telephoneNumber", 1880096780, "AttributeType"}, - {0, 1073741825, "id-at"}, - {0, 1, "20"}, - {"X520telephoneNumber", 1073741826, "PrintableString"}, - {"id-at-facsimileTelephoneNumber", 1880096780, "AttributeType"}, - {0, 1073741825, "id-at"}, - {0, 1, "23"}, - {"X520facsimileTelephoneNumber", 1073741826, "PrintableString"}, - {"id-at-pseudonym", 1880096780, "AttributeType"}, - {0, 1073741825, "id-at"}, - {0, 1, "65"}, - {"X520pseudonym", 1073741826, "DirectoryString"}, - {"id-at-name", 1880096780, "AttributeType"}, - {0, 1073741825, "id-at"}, - {0, 1, "41"}, - {"X520name", 1073741826, "DirectoryString"}, - {"id-at-streetAddress", 1880096780, "AttributeType"}, - {0, 1073741825, "id-at"}, - {0, 1, "9"}, - {"X520streetAddress", 1073741826, "DirectoryString"}, - {"id-at-postalAddress", 1880096780, "AttributeType"}, - {0, 1073741825, "id-at"}, - {0, 1, "16"}, - {"X520postalAddress", 1073741826, "PostalAddress"}, - {"PostalAddress", 1610612747, 0}, - {0, 2, "DirectoryString"}, - {"pkcs", 1879048204, 0}, - {"iso", 1073741825, "1"}, - {"member-body", 1073741825, "2"}, - {"us", 1073741825, "840"}, - {"rsadsi", 1073741825, "113549"}, - {"pkcs", 1, "1"}, - {"pkcs-9", 1879048204, 0}, - {0, 1073741825, "pkcs"}, - {0, 1, "9"}, - {"emailAddress", 1880096780, "AttributeType"}, - {0, 1073741825, "pkcs-9"}, - {0, 1, "1"}, - {"Pkcs9email", 1612709890, "IA5String"}, - {"ub-emailaddress-length", 524298, "1"}, - {"Name", 1610612754, 0}, - {"rdnSequence", 2, "RDNSequence"}, - {"RDNSequence", 1610612747, 0}, - {0, 2, "RelativeDistinguishedName"}, - {"DistinguishedName", 1073741826, "RDNSequence"}, - {"RelativeDistinguishedName", 1612709903, 0}, - {"MAX", 1074266122, "1"}, - {0, 2, "AttributeTypeAndValue"}, - {"Certificate", 1610612741, 0}, - {"tbsCertificate", 1073741826, "TBSCertificate"}, - {"signatureAlgorithm", 1073741826, "AlgorithmIdentifier"}, - {"signature", 6, 0}, - {"TBSCertificate", 1610612741, 0}, - {"version", 1610653698, "Version"}, - {0, 1073741833, "v1"}, - {0, 2056, "0"}, - {"serialNumber", 1073741826, "CertificateSerialNumber"}, - {"signature", 1073741826, "AlgorithmIdentifier"}, - {"issuer", 1073741826, "Name"}, - {"validity", 1073741826, "Validity"}, - {"subject", 1073741826, "Name"}, - {"subjectPublicKeyInfo", 1073741826, "SubjectPublicKeyInfo"}, - {"issuerUniqueID", 1610637314, "UniqueIdentifier"}, - {0, 4104, "1"}, - {"subjectUniqueID", 1610637314, "UniqueIdentifier"}, - {0, 4104, "2"}, - {"extensions", 536895490, "Extensions"}, - {0, 2056, "3"}, - {"Version", 1610874883, 0}, - {"v1", 1073741825, "0"}, - {"v2", 1073741825, "1"}, - {"v3", 1, "2"}, - {"CertificateSerialNumber", 1073741827, 0}, - {"Validity", 1610612741, 0}, - {"notBefore", 1073741826, "Time"}, - {"notAfter", 2, "Time"}, - {"Time", 1610612754, 0}, - {"utcTime", 1090519057, 0}, - {"generalTime", 8388625, 0}, - {"UniqueIdentifier", 1073741830, 0}, - {"SubjectPublicKeyInfo", 1610612741, 0}, - {"algorithm", 1073741826, "AlgorithmIdentifier"}, - {"subjectPublicKey", 6, 0}, - {"Extensions", 1612709899, 0}, - {"MAX", 1074266122, "1"}, - {0, 2, "Extension"}, - {"Extension", 1610612741, 0}, - {"extnID", 1073741836, 0}, - {"critical", 1610645508, 0}, - {0, 131081, 0}, - {"extnValue", 7, 0}, - {"CertificateList", 1610612741, 0}, - {"tbsCertList", 1073741826, "TBSCertList"}, - {"signatureAlgorithm", 1073741826, "AlgorithmIdentifier"}, - {"signature", 6, 0}, - {"TBSCertList", 1610612741, 0}, - {"version", 1073758210, "Version"}, - {"signature", 1073741826, "AlgorithmIdentifier"}, - {"issuer", 1073741826, "Name"}, - {"thisUpdate", 1073741826, "Time"}, - {"nextUpdate", 1073758210, "Time"}, - {"revokedCertificates", 1610629131, 0}, - {0, 536870917, 0}, - {"userCertificate", 1073741826, "CertificateSerialNumber"}, - {"revocationDate", 1073741826, "Time"}, - {"crlEntryExtensions", 16386, "Extensions"}, - {"crlExtensions", 536895490, "Extensions"}, - {0, 2056, "0"}, - {"AlgorithmIdentifier", 1610612741, 0}, - {"algorithm", 1073741836, 0}, - {"parameters", 541081613, 0}, - {"algorithm", 1, 0}, - {"pkcs-1", 1879048204, 0}, - {0, 1073741825, "pkcs"}, - {0, 1, "1"}, - {"rsaEncryption", 1879048204, 0}, - {0, 1073741825, "pkcs-1"}, - {0, 1, "1"}, - {"md2WithRSAEncryption", 1879048204, 0}, - {0, 1073741825, "pkcs-1"}, - {0, 1, "2"}, - {"md5WithRSAEncryption", 1879048204, 0}, - {0, 1073741825, "pkcs-1"}, - {0, 1, "4"}, - {"sha1WithRSAEncryption", 1879048204, 0}, - {0, 1073741825, "pkcs-1"}, - {0, 1, "5"}, - {"id-dsa-with-sha1", 1879048204, 0}, - {"iso", 1073741825, "1"}, - {"member-body", 1073741825, "2"}, - {"us", 1073741825, "840"}, - {"x9-57", 1073741825, "10040"}, - {"x9algorithm", 1073741825, "4"}, - {0, 1, "3"}, - {"Dss-Sig-Value", 1610612741, 0}, - {"r", 1073741827, 0}, - {"s", 3, 0}, - {"dhpublicnumber", 1879048204, 0}, - {"iso", 1073741825, "1"}, - {"member-body", 1073741825, "2"}, - {"us", 1073741825, "840"}, - {"ansi-x942", 1073741825, "10046"}, - {"number-type", 1073741825, "2"}, - {0, 1, "1"}, - {"DomainParameters", 1610612741, 0}, - {"p", 1073741827, 0}, - {"g", 1073741827, 0}, - {"q", 1073741827, 0}, - {"j", 1073758211, 0}, - {"validationParms", 16386, "ValidationParms"}, - {"ValidationParms", 1610612741, 0}, - {"seed", 1073741830, 0}, - {"pgenCounter", 3, 0}, - {"id-dsa", 1879048204, 0}, - {"iso", 1073741825, "1"}, - {"member-body", 1073741825, "2"}, - {"us", 1073741825, "840"}, - {"x9-57", 1073741825, "10040"}, - {"x9algorithm", 1073741825, "4"}, - {0, 1, "1"}, - {"Dss-Parms", 1610612741, 0}, - {"p", 1073741827, 0}, - {"q", 1073741827, 0}, - {"g", 3, 0}, - {"ORAddress", 1610612741, 0}, - {"built-in-standard-attributes", 1073741826, "BuiltInStandardAttributes"}, - {"built-in-domain-defined-attributes", 1073758210, - "BuiltInDomainDefinedAttributes"}, - {"extension-attributes", 16386, "ExtensionAttributes"}, - {"BuiltInStandardAttributes", 1610612741, 0}, - {"country-name", 1073758210, "CountryName"}, - {"administration-domain-name", 1073758210, "AdministrationDomainName"}, - {"network-address", 1610637314, "NetworkAddress"}, - {0, 2056, "0"}, - {"terminal-identifier", 1610637314, "TerminalIdentifier"}, - {0, 2056, "1"}, - {"private-domain-name", 1610637314, "PrivateDomainName"}, - {0, 2056, "2"}, - {"organization-name", 1610637314, "OrganizationName"}, - {0, 2056, "3"}, - {"numeric-user-identifier", 1610637314, "NumericUserIdentifier"}, - {0, 2056, "4"}, - {"personal-name", 1610637314, "PersonalName"}, - {0, 2056, "5"}, - {"organizational-unit-names", 536895490, "OrganizationalUnitNames"}, - {0, 2056, "6"}, - {"CountryName", 1610620946, 0}, - {0, 1073746952, "1"}, - {"x121-dcc-code", 1612709890, "NumericString"}, - {0, 1048586, "ub-country-name-numeric-length"}, - {"iso-3166-alpha2-code", 538968066, "PrintableString"}, - {0, 1048586, "ub-country-name-alpha-length"}, - {"AdministrationDomainName", 1610620946, 0}, - {0, 1073744904, "2"}, - {"numeric", 1612709890, "NumericString"}, - {"ub-domain-name-length", 524298, "0"}, - {"printable", 538968066, "PrintableString"}, - {"ub-domain-name-length", 524298, "0"}, - {"NetworkAddress", 1073741826, "X121Address"}, - {"X121Address", 1612709890, "NumericString"}, - {"ub-x121-address-length", 524298, "1"}, - {"TerminalIdentifier", 1612709890, "PrintableString"}, - {"ub-terminal-id-length", 524298, "1"}, - {"PrivateDomainName", 1610612754, 0}, - {"numeric", 1612709890, "NumericString"}, - {"ub-domain-name-length", 524298, "1"}, - {"printable", 538968066, "PrintableString"}, - {"ub-domain-name-length", 524298, "1"}, - {"OrganizationName", 1612709890, "PrintableString"}, - {"ub-organization-name-length", 524298, "1"}, - {"NumericUserIdentifier", 1612709890, "NumericString"}, - {"ub-numeric-user-id-length", 524298, "1"}, - {"PersonalName", 1610612750, 0}, - {"surname", 1814044674, "PrintableString"}, - {0, 1073745928, "0"}, - {"ub-surname-length", 524298, "1"}, - {"given-name", 1814061058, "PrintableString"}, - {0, 1073745928, "1"}, - {"ub-given-name-length", 524298, "1"}, - {"initials", 1814061058, "PrintableString"}, - {0, 1073745928, "2"}, - {"ub-initials-length", 524298, "1"}, - {"generation-qualifier", 740319234, "PrintableString"}, - {0, 1073745928, "3"}, - {"ub-generation-qualifier-length", 524298, "1"}, - {"OrganizationalUnitNames", 1612709899, 0}, - {"ub-organizational-units", 1074266122, "1"}, - {0, 2, "OrganizationalUnitName"}, - {"OrganizationalUnitName", 1612709890, "PrintableString"}, - {"ub-organizational-unit-name-length", 524298, "1"}, - {"BuiltInDomainDefinedAttributes", 1612709899, 0}, - {"ub-domain-defined-attributes", 1074266122, "1"}, - {0, 2, "BuiltInDomainDefinedAttribute"}, - {"BuiltInDomainDefinedAttribute", 1610612741, 0}, - {"type", 1612709890, "PrintableString"}, - {"ub-domain-defined-attribute-type-length", 524298, "1"}, - {"value", 538968066, "PrintableString"}, - {"ub-domain-defined-attribute-value-length", 524298, "1"}, - {"ExtensionAttributes", 1612709903, 0}, - {"ub-extension-attributes", 1074266122, "1"}, - {0, 2, "ExtensionAttribute"}, - {"ExtensionAttribute", 1610612741, 0}, - {"extension-attribute-type", 1611145219, 0}, - {0, 1073743880, "0"}, - {"0", 10, "ub-extension-attributes"}, - {"extension-attribute-value", 541073421, 0}, - {0, 1073743880, "1"}, - {"extension-attribute-type", 1, 0}, - {"common-name", 1342177283, "1"}, - {"CommonName", 1612709890, "PrintableString"}, - {"ub-common-name-length", 524298, "1"}, - {"teletex-common-name", 1342177283, "2"}, - {"TeletexCommonName", 1612709890, "TeletexString"}, - {"ub-common-name-length", 524298, "1"}, - {"teletex-organization-name", 1342177283, "3"}, - {"TeletexOrganizationName", 1612709890, "TeletexString"}, - {"ub-organization-name-length", 524298, "1"}, - {"teletex-personal-name", 1342177283, "4"}, - {"TeletexPersonalName", 1610612750, 0}, - {"surname", 1814044674, "TeletexString"}, - {0, 1073743880, "0"}, - {"ub-surname-length", 524298, "1"}, - {"given-name", 1814061058, "TeletexString"}, - {0, 1073743880, "1"}, - {"ub-given-name-length", 524298, "1"}, - {"initials", 1814061058, "TeletexString"}, - {0, 1073743880, "2"}, - {"ub-initials-length", 524298, "1"}, - {"generation-qualifier", 740319234, "TeletexString"}, - {0, 1073743880, "3"}, - {"ub-generation-qualifier-length", 524298, "1"}, - {"teletex-organizational-unit-names", 1342177283, "5"}, - {"TeletexOrganizationalUnitNames", 1612709899, 0}, - {"ub-organizational-units", 1074266122, "1"}, - {0, 2, "TeletexOrganizationalUnitName"}, - {"TeletexOrganizationalUnitName", 1612709890, "TeletexString"}, - {"ub-organizational-unit-name-length", 524298, "1"}, - {"pds-name", 1342177283, "7"}, - {"PDSName", 1612709890, "PrintableString"}, - {"ub-pds-name-length", 524298, "1"}, - {"physical-delivery-country-name", 1342177283, "8"}, - {"PhysicalDeliveryCountryName", 1610612754, 0}, - {"x121-dcc-code", 1612709890, "NumericString"}, - {0, 1048586, "ub-country-name-numeric-length"}, - {"iso-3166-alpha2-code", 538968066, "PrintableString"}, - {0, 1048586, "ub-country-name-alpha-length"}, - {"postal-code", 1342177283, "9"}, - {"PostalCode", 1610612754, 0}, - {"numeric-code", 1612709890, "NumericString"}, - {"ub-postal-code-length", 524298, "1"}, - {"printable-code", 538968066, "PrintableString"}, - {"ub-postal-code-length", 524298, "1"}, - {"physical-delivery-office-name", 1342177283, "10"}, - {"PhysicalDeliveryOfficeName", 1073741826, "PDSParameter"}, - {"physical-delivery-office-number", 1342177283, "11"}, - {"PhysicalDeliveryOfficeNumber", 1073741826, "PDSParameter"}, - {"extension-OR-address-components", 1342177283, "12"}, - {"ExtensionORAddressComponents", 1073741826, "PDSParameter"}, - {"physical-delivery-personal-name", 1342177283, "13"}, - {"PhysicalDeliveryPersonalName", 1073741826, "PDSParameter"}, - {"physical-delivery-organization-name", 1342177283, "14"}, - {"PhysicalDeliveryOrganizationName", 1073741826, "PDSParameter"}, - {"extension-physical-delivery-address-components", 1342177283, "15"}, - {"ExtensionPhysicalDeliveryAddressComponents", 1073741826, "PDSParameter"}, - {"unformatted-postal-address", 1342177283, "16"}, - {"UnformattedPostalAddress", 1610612750, 0}, - {"printable-address", 1814052875, 0}, - {"ub-pds-physical-address-lines", 1074266122, "1"}, - {0, 538968066, "PrintableString"}, - {"ub-pds-parameter-length", 524298, "1"}, - {"teletex-string", 740311042, "TeletexString"}, - {"ub-unformatted-address-length", 524298, "1"}, - {"street-address", 1342177283, "17"}, - {"StreetAddress", 1073741826, "PDSParameter"}, - {"post-office-box-address", 1342177283, "18"}, - {"PostOfficeBoxAddress", 1073741826, "PDSParameter"}, - {"poste-restante-address", 1342177283, "19"}, - {"PosteRestanteAddress", 1073741826, "PDSParameter"}, - {"unique-postal-name", 1342177283, "20"}, - {"UniquePostalName", 1073741826, "PDSParameter"}, - {"local-postal-attributes", 1342177283, "21"}, - {"LocalPostalAttributes", 1073741826, "PDSParameter"}, - {"PDSParameter", 1610612750, 0}, - {"printable-string", 1814052866, "PrintableString"}, - {"ub-pds-parameter-length", 524298, "1"}, - {"teletex-string", 740311042, "TeletexString"}, - {"ub-pds-parameter-length", 524298, "1"}, - {"extended-network-address", 1342177283, "22"}, - {"ExtendedNetworkAddress", 1610612754, 0}, - {"e163-4-address", 1610612741, 0}, - {"number", 1612718082, "NumericString"}, - {0, 1073743880, "0"}, - {"ub-e163-4-number-length", 524298, "1"}, - {"sub-address", 538992642, "NumericString"}, - {0, 1073743880, "1"}, - {"ub-e163-4-sub-address-length", 524298, "1"}, - {"psap-address", 536879106, "PresentationAddress"}, - {0, 2056, "0"}, - {"PresentationAddress", 1610612741, 0}, - {"pSelector", 1610637319, 0}, - {0, 2056, "0"}, - {"sSelector", 1610637319, 0}, - {0, 2056, "1"}, - {"tSelector", 1610637319, 0}, - {0, 2056, "2"}, - {"nAddresses", 538976271, 0}, - {0, 1073743880, "3"}, - {"MAX", 1074266122, "1"}, - {0, 7, 0}, - {"terminal-type", 1342177283, "23"}, - {"TerminalType", 1610874883, 0}, - {"telex", 1073741825, "3"}, - {"teletex", 1073741825, "4"}, - {"g3-facsimile", 1073741825, "5"}, - {"g4-facsimile", 1073741825, "6"}, - {"ia5-terminal", 1073741825, "7"}, - {"videotex", 1, "8"}, - {"teletex-domain-defined-attributes", 1342177283, "6"}, - {"TeletexDomainDefinedAttributes", 1612709899, 0}, - {"ub-domain-defined-attributes", 1074266122, "1"}, - {0, 2, "TeletexDomainDefinedAttribute"}, - {"TeletexDomainDefinedAttribute", 1610612741, 0}, - {"type", 1612709890, "TeletexString"}, - {"ub-domain-defined-attribute-type-length", 524298, "1"}, - {"value", 538968066, "TeletexString"}, - {"ub-domain-defined-attribute-value-length", 524298, "1"}, - {"ub-name", 1342177283, "32768"}, - {"ub-common-name", 1342177283, "64"}, - {"ub-locality-name", 1342177283, "128"}, - {"ub-state-name", 1342177283, "128"}, - {"ub-organization-name", 1342177283, "64"}, - {"ub-organizational-unit-name", 1342177283, "64"}, - {"ub-title", 1342177283, "64"}, - {"ub-match", 1342177283, "128"}, - {"ub-emailaddress-length", 1342177283, "128"}, - {"ub-common-name-length", 1342177283, "64"}, - {"ub-country-name-alpha-length", 1342177283, "2"}, - {"ub-country-name-numeric-length", 1342177283, "3"}, - {"ub-domain-defined-attributes", 1342177283, "4"}, - {"ub-domain-defined-attribute-type-length", 1342177283, "8"}, - {"ub-domain-defined-attribute-value-length", 1342177283, "128"}, - {"ub-domain-name-length", 1342177283, "16"}, - {"ub-extension-attributes", 1342177283, "256"}, - {"ub-e163-4-number-length", 1342177283, "15"}, - {"ub-e163-4-sub-address-length", 1342177283, "40"}, - {"ub-generation-qualifier-length", 1342177283, "3"}, - {"ub-given-name-length", 1342177283, "16"}, - {"ub-initials-length", 1342177283, "5"}, - {"ub-integer-options", 1342177283, "256"}, - {"ub-numeric-user-id-length", 1342177283, "32"}, - {"ub-organization-name-length", 1342177283, "64"}, - {"ub-organizational-unit-name-length", 1342177283, "32"}, - {"ub-organizational-units", 1342177283, "4"}, - {"ub-pds-name-length", 1342177283, "16"}, - {"ub-pds-parameter-length", 1342177283, "30"}, - {"ub-pds-physical-address-lines", 1342177283, "6"}, - {"ub-postal-code-length", 1342177283, "16"}, - {"ub-surname-length", 1342177283, "40"}, - {"ub-terminal-id-length", 1342177283, "24"}, - {"ub-unformatted-address-length", 1342177283, "180"}, - {"ub-x121-address-length", 1342177283, "16"}, - {"pkcs-7-ContentInfo", 1610612741, 0}, - {"contentType", 1073741826, "pkcs-7-ContentType"}, - {"content", 541073421, 0}, - {0, 1073743880, "0"}, - {"contentType", 1, 0}, - {"pkcs-7-DigestInfo", 1610612741, 0}, - {"digestAlgorithm", 1073741826, "pkcs-7-DigestAlgorithmIdentifier"}, - {"digest", 2, "pkcs-7-Digest"}, - {"pkcs-7-Digest", 1073741831, 0}, - {"pkcs-7-ContentType", 1073741836, 0}, - {"pkcs-7-SignedData", 1610612741, 0}, - {"version", 1073741826, "pkcs-7-CMSVersion"}, - {"digestAlgorithms", 1073741826, "pkcs-7-DigestAlgorithmIdentifiers"}, - {"encapContentInfo", 1073741826, "pkcs-7-EncapsulatedContentInfo"}, - {"certificates", 1610637314, "pkcs-7-CertificateSet"}, - {0, 4104, "0"}, - {"crls", 1610637314, "pkcs-7-CertificateRevocationLists"}, - {0, 4104, "1"}, - {"signerInfos", 2, "pkcs-7-SignerInfos"}, - {"pkcs-7-CMSVersion", 1610874883, 0}, - {"v0", 1073741825, "0"}, - {"v1", 1073741825, "1"}, - {"v2", 1073741825, "2"}, - {"v3", 1073741825, "3"}, - {"v4", 1, "4"}, - {"pkcs-7-DigestAlgorithmIdentifiers", 1610612751, 0}, - {0, 2, "pkcs-7-DigestAlgorithmIdentifier"}, - {"pkcs-7-DigestAlgorithmIdentifier", 1073741826, "AlgorithmIdentifier"}, - {"pkcs-7-EncapsulatedContentInfo", 1610612741, 0}, - {"eContentType", 1073741826, "pkcs-7-ContentType"}, - {"eContent", 536895495, 0}, - {0, 2056, "0"}, - {"pkcs-7-CertificateRevocationLists", 1610612751, 0}, - {0, 13, 0}, - {"pkcs-7-CertificateChoices", 1610612754, 0}, - {"certificate", 13, 0}, - {"pkcs-7-CertificateSet", 1610612751, 0}, - {0, 2, "pkcs-7-CertificateChoices"}, - {"pkcs-7-SignerInfos", 1610612751, 0}, - {0, 13, 0}, - {"pkcs-10-CertificationRequestInfo", 1610612741, 0}, - {"version", 1610874883, 0}, - {"v1", 1, "0"}, - {"subject", 1073741826, "Name"}, - {"subjectPKInfo", 1073741826, "SubjectPublicKeyInfo"}, - {"attributes", 536879106, "Attributes"}, - {0, 4104, "0"}, - {"Attributes", 1610612751, 0}, - {0, 2, "Attribute"}, - {"pkcs-10-CertificationRequest", 1610612741, 0}, - {"certificationRequestInfo", 1073741826, "pkcs-10-CertificationRequestInfo"}, - {"signatureAlgorithm", 1073741826, "AlgorithmIdentifier"}, - {"signature", 6, 0}, - {"pkcs-9-ub-challengePassword", 1342177283, "255"}, - {"pkcs-9-certTypes", 1879048204, 0}, - {0, 1073741825, "pkcs-9"}, - {0, 1, "22"}, - {"pkcs-9-crlTypes", 1879048204, 0}, - {0, 1073741825, "pkcs-9"}, - {0, 1, "23"}, - {"pkcs-9-at-challengePassword", 1879048204, 0}, - {0, 1073741825, "pkcs-9"}, - {0, 1, "7"}, - {"pkcs-9-challengePassword", 1610612754, 0}, - {"printableString", 1612709890, "PrintableString"}, - {"pkcs-9-ub-challengePassword", 524298, "1"}, - {"utf8String", 538968066, "UTF8String"}, - {"pkcs-9-ub-challengePassword", 524298, "1"}, - {"pkcs-9-at-localKeyId", 1879048204, 0}, - {0, 1073741825, "pkcs-9"}, - {0, 1, "21"}, - {"pkcs-9-localKeyId", 1073741831, 0}, - {"pkcs-9-at-friendlyName", 1879048204, 0}, - {0, 1073741825, "pkcs-9"}, - {0, 1, "20"}, - {"pkcs-9-friendlyName", 1612709890, "BMPString"}, - {"255", 524298, "1"}, - {"pkcs-8-PrivateKeyInfo", 1610612741, 0}, - {"version", 1073741826, "pkcs-8-Version"}, - {"privateKeyAlgorithm", 1073741826, "AlgorithmIdentifier"}, - {"privateKey", 1073741826, "pkcs-8-PrivateKey"}, - {"attributes", 536895490, "Attributes"}, - {0, 4104, "0"}, - {"pkcs-8-Version", 1610874883, 0}, - {"v1", 1, "0"}, - {"pkcs-8-PrivateKey", 1073741831, 0}, - {"pkcs-8-Attributes", 1610612751, 0}, - {0, 2, "Attribute"}, - {"pkcs-8-EncryptedPrivateKeyInfo", 1610612741, 0}, - {"encryptionAlgorithm", 1073741826, "AlgorithmIdentifier"}, - {"encryptedData", 2, "pkcs-8-EncryptedData"}, - {"pkcs-8-EncryptedData", 1073741831, 0}, - {"pkcs-5", 1879048204, 0}, - {0, 1073741825, "pkcs"}, - {0, 1, "5"}, - {"pkcs-5-encryptionAlgorithm", 1879048204, 0}, - {"iso", 1073741825, "1"}, - {"member-body", 1073741825, "2"}, - {"us", 1073741825, "840"}, - {"rsadsi", 1073741825, "113549"}, - {0, 1, "3"}, - {"pkcs-5-des-EDE3-CBC", 1879048204, 0}, - {0, 1073741825, "pkcs-5-encryptionAlgorithm"}, - {0, 1, "7"}, - {"pkcs-5-des-EDE3-CBC-params", 1612709895, 0}, - {0, 1048586, "8"}, - {"pkcs-5-id-PBES2", 1879048204, 0}, - {0, 1073741825, "pkcs-5"}, - {0, 1, "13"}, - {"pkcs-5-PBES2-params", 1610612741, 0}, - {"keyDerivationFunc", 1073741826, "AlgorithmIdentifier"}, - {"encryptionScheme", 2, "AlgorithmIdentifier"}, - {"pkcs-5-id-PBKDF2", 1879048204, 0}, - {0, 1073741825, "pkcs-5"}, - {0, 1, "12"}, - {"pkcs-5-PBKDF2-params", 1610612741, 0}, - {"salt", 1610612754, 0}, - {"specified", 1073741831, 0}, - {"otherSource", 2, "AlgorithmIdentifier"}, - {"iterationCount", 1611137027, 0}, - {"1", 10, "MAX"}, - {"keyLength", 1611153411, 0}, - {"1", 10, "MAX"}, - {"prf", 16386, "AlgorithmIdentifier"}, - {"pkcs-12", 1879048204, 0}, - {0, 1073741825, "pkcs"}, - {0, 1, "12"}, - {"pkcs-12-PFX", 1610612741, 0}, - {"version", 1610874883, 0}, - {"v3", 1, "3"}, - {"authSafe", 1073741826, "pkcs-7-ContentInfo"}, - {"macData", 16386, "pkcs-12-MacData"}, - {"pkcs-12-PbeParams", 1610612741, 0}, - {"salt", 1073741831, 0}, - {"iterations", 3, 0}, - {"pkcs-12-MacData", 1610612741, 0}, - {"mac", 1073741826, "pkcs-7-DigestInfo"}, - {"macSalt", 1073741831, 0}, - {"iterations", 536903683, 0}, - {0, 9, "1"}, - {"pkcs-12-AuthenticatedSafe", 1610612747, 0}, - {0, 2, "pkcs-7-ContentInfo"}, - {"pkcs-12-SafeContents", 1610612747, 0}, - {0, 2, "pkcs-12-SafeBag"}, - {"pkcs-12-SafeBag", 1610612741, 0}, - {"bagId", 1073741836, 0}, - {"bagValue", 1614815245, 0}, - {0, 1073743880, "0"}, - {"badId", 1, 0}, - {"bagAttributes", 536887311, 0}, - {0, 2, "pkcs-12-PKCS12Attribute"}, - {"pkcs-12-bagtypes", 1879048204, 0}, - {0, 1073741825, "pkcs-12"}, - {0, 1073741825, "10"}, - {0, 1, "1"}, - {"pkcs-12-keyBag", 1879048204, 0}, - {0, 1073741825, "pkcs-12-bagtypes"}, - {0, 1, "1"}, - {"pkcs-12-pkcs8ShroudedKeyBag", 1879048204, 0}, - {0, 1073741825, "pkcs-12-bagtypes"}, - {0, 1, "2"}, - {"pkcs-12-certBag", 1879048204, 0}, - {0, 1073741825, "pkcs-12-bagtypes"}, - {0, 1, "3"}, - {"pkcs-12-crlBag", 1879048204, 0}, - {0, 1073741825, "pkcs-12-bagtypes"}, - {0, 1, "4"}, - {"pkcs-12-KeyBag", 1073741826, "pkcs-8-PrivateKeyInfo"}, - {"pkcs-12-PKCS8ShroudedKeyBag", 1073741826, "pkcs-8-EncryptedPrivateKeyInfo"}, - {"pkcs-12-CertBag", 1610612741, 0}, - {"certId", 1073741836, 0}, - {"certValue", 541073421, 0}, - {0, 1073743880, "0"}, - {"certId", 1, 0}, - {"pkcs-12-CRLBag", 1610612741, 0}, - {"crlId", 1073741836, 0}, - {"crlValue", 541073421, 0}, - {0, 1073743880, "0"}, - {"crlId", 1, 0}, - {"pkcs-12-PKCS12Attribute", 1073741826, "Attribute"}, - {"pkcs-7-data", 1879048204, 0}, - {"iso", 1073741825, "1"}, - {"member-body", 1073741825, "2"}, - {"us", 1073741825, "840"}, - {"rsadsi", 1073741825, "113549"}, - {"pkcs", 1073741825, "1"}, - {"pkcs7", 1073741825, "7"}, - {0, 1, "1"}, - {"pkcs-7-encryptedData", 1879048204, 0}, - {"iso", 1073741825, "1"}, - {"member-body", 1073741825, "2"}, - {"us", 1073741825, "840"}, - {"rsadsi", 1073741825, "113549"}, - {"pkcs", 1073741825, "1"}, - {"pkcs7", 1073741825, "7"}, - {0, 1, "6"}, - {"pkcs-7-Data", 1073741831, 0}, - {"pkcs-7-EncryptedData", 1610612741, 0}, - {"version", 1073741826, "pkcs-7-CMSVersion"}, - {"encryptedContentInfo", 1073741826, "pkcs-7-EncryptedContentInfo"}, - {"unprotectedAttrs", 536895490, "pkcs-7-UnprotectedAttributes"}, - {0, 4104, "1"}, - {"pkcs-7-EncryptedContentInfo", 1610612741, 0}, - {"contentType", 1073741826, "pkcs-7-ContentType"}, - {"contentEncryptionAlgorithm", 1073741826, - "pkcs-7-ContentEncryptionAlgorithmIdentifier"}, - {"encryptedContent", 536895490, "pkcs-7-EncryptedContent"}, - {0, 4104, "0"}, - {"pkcs-7-ContentEncryptionAlgorithmIdentifier", 1073741826, - "AlgorithmIdentifier"}, - {"pkcs-7-EncryptedContent", 1073741831, 0}, - {"pkcs-7-UnprotectedAttributes", 1612709903, 0}, - {"MAX", 1074266122, "1"}, - {0, 2, "Attribute"}, - {"id-at-ldap-DC", 1880096780, "AttributeType"}, - {0, 1073741825, "0"}, - {0, 1073741825, "9"}, - {0, 1073741825, "2342"}, - {0, 1073741825, "19200300"}, - {0, 1073741825, "100"}, - {0, 1073741825, "1"}, - {0, 1, "25"}, - {"ldap-DC", 1073741826, "IA5String"}, - {"id-at-ldap-UID", 1880096780, "AttributeType"}, - {0, 1073741825, "0"}, - {0, 1073741825, "9"}, - {0, 1073741825, "2342"}, - {0, 1073741825, "19200300"}, - {0, 1073741825, "100"}, - {0, 1073741825, "1"}, - {0, 1, "1"}, - {"ldap-UID", 1073741826, "DirectoryString"}, - {"id-pda", 1879048204, 0}, - {0, 1073741825, "id-pkix"}, - {0, 1, "9"}, - {"id-pda-dateOfBirth", 1880096780, "AttributeType"}, - {0, 1073741825, "id-pda"}, - {0, 1, "1"}, - {"DateOfBirth", 1082130449, 0}, - {"id-pda-placeOfBirth", 1880096780, "AttributeType"}, - {0, 1073741825, "id-pda"}, - {0, 1, "2"}, - {"PlaceOfBirth", 1073741826, "DirectoryString"}, - {"id-pda-gender", 1880096780, "AttributeType"}, - {0, 1073741825, "id-pda"}, - {0, 1, "3"}, - {"Gender", 1612709890, "PrintableString"}, - {0, 1048586, "1"}, - {"id-pda-countryOfCitizenship", 1880096780, "AttributeType"}, - {0, 1073741825, "id-pda"}, - {0, 1, "4"}, - {"CountryOfCitizenship", 1612709890, "PrintableString"}, - {0, 1048586, "2"}, - {"id-pda-countryOfResidence", 1880096780, "AttributeType"}, - {0, 1073741825, "id-pda"}, - {0, 1, "5"}, - {"CountryOfResidence", 538968066, "PrintableString"}, - {0, 1048586, "2"}, - {0, 0, 0} -}; -#endif /* QCRYPTO_HAVE_TLS_TEST_SUPPORT */ diff --git a/tests/ptimer-test-stubs.c b/tests/ptimer-test-stubs.c deleted file mode 100644 index 7f801a4d09..0000000000 --- a/tests/ptimer-test-stubs.c +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Stubs for the ptimer-test - * - * Copyright (c) 2016 Dmitry Osipenko - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#include "qemu/osdep.h" -#include "qemu/main-loop.h" -#include "sysemu/replay.h" -#include "migration/vmstate.h" -#include "sysemu/cpu-timers.h" - -#include "ptimer-test.h" - -const VMStateInfo vmstate_info_uint8; -const VMStateInfo vmstate_info_uint32; -const VMStateInfo vmstate_info_uint64; -const VMStateInfo vmstate_info_int64; -const VMStateInfo vmstate_info_timer; - -struct QEMUBH { - QEMUBHFunc *cb; - void *opaque; -}; - -QEMUTimerListGroup main_loop_tlg; - -int64_t ptimer_test_time_ns; - -/* under qtest_enabled(), will not artificially limit period - see hw/core/ptimer.c. */ -int use_icount; -bool qtest_allowed; - -void timer_init_full(QEMUTimer *ts, - QEMUTimerListGroup *timer_list_group, QEMUClockType type, - int scale, int attributes, - QEMUTimerCB *cb, void *opaque) -{ - if (!timer_list_group) { - timer_list_group = &main_loop_tlg; - } - ts->timer_list = timer_list_group->tl[type]; - ts->cb = cb; - ts->opaque = opaque; - ts->scale = scale; - ts->attributes = attributes; - ts->expire_time = -1; -} - -void timer_mod(QEMUTimer *ts, int64_t expire_time) -{ - QEMUTimerList *timer_list = ts->timer_list; - QEMUTimer *t = &timer_list->active_timers; - - while (t->next != NULL) { - if (t->next == ts) { - break; - } - - t = t->next; - } - - ts->expire_time = MAX(expire_time * ts->scale, 0); - ts->next = NULL; - t->next = ts; -} - -void timer_del(QEMUTimer *ts) -{ - QEMUTimerList *timer_list = ts->timer_list; - QEMUTimer *t = &timer_list->active_timers; - - while (t->next != NULL) { - if (t->next == ts) { - t->next = ts->next; - return; - } - - t = t->next; - } -} - -int64_t qemu_clock_get_ns(QEMUClockType type) -{ - return ptimer_test_time_ns; -} - -int64_t qemu_clock_deadline_ns_all(QEMUClockType type, int attr_mask) -{ - QEMUTimerList *timer_list = main_loop_tlg.tl[QEMU_CLOCK_VIRTUAL]; - QEMUTimer *t = timer_list->active_timers.next; - int64_t deadline = -1; - - while (t != NULL) { - if (deadline == -1) { - deadline = t->expire_time; - } else { - deadline = MIN(deadline, t->expire_time); - } - - t = t->next; - } - - return deadline; -} - -QEMUBH *qemu_bh_new(QEMUBHFunc *cb, void *opaque) -{ - QEMUBH *bh = g_new(QEMUBH, 1); - - bh->cb = cb; - bh->opaque = opaque; - - return bh; -} - -void qemu_bh_delete(QEMUBH *bh) -{ - g_free(bh); -} diff --git a/tests/ptimer-test.c b/tests/ptimer-test.c deleted file mode 100644 index 9176b96c1c..0000000000 --- a/tests/ptimer-test.c +++ /dev/null @@ -1,892 +0,0 @@ -/* - * QTest testcase for the ptimer - * - * Copyright (c) 2016 Dmitry Osipenko - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#include "qemu/osdep.h" -#include - -#include "qemu/main-loop.h" -#include "hw/ptimer.h" - -#include "ptimer-test.h" - -static bool triggered; - -static void ptimer_trigger(void *opaque) -{ - triggered = true; -} - -static void ptimer_test_expire_qemu_timers(int64_t expire_time, - QEMUClockType type) -{ - QEMUTimerList *timer_list = main_loop_tlg.tl[type]; - QEMUTimer *t = timer_list->active_timers.next; - - while (t != NULL) { - if (t->expire_time == expire_time) { - timer_del(t); - - if (t->cb != NULL) { - t->cb(t->opaque); - } - } - - t = t->next; - } -} - -static void ptimer_test_set_qemu_time_ns(int64_t ns) -{ - ptimer_test_time_ns = ns; -} - -static void qemu_clock_step(uint64_t ns) -{ - int64_t deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL, - QEMU_TIMER_ATTR_ALL); - int64_t advanced_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + ns; - - while (deadline != -1 && deadline <= advanced_time) { - ptimer_test_set_qemu_time_ns(deadline); - ptimer_test_expire_qemu_timers(deadline, QEMU_CLOCK_VIRTUAL); - deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL, - QEMU_TIMER_ATTR_ALL); - } - - ptimer_test_set_qemu_time_ns(advanced_time); -} - -static void check_set_count(gconstpointer arg) -{ - const uint8_t *policy = arg; - ptimer_state *ptimer = ptimer_init(ptimer_trigger, NULL, *policy); - - triggered = false; - - ptimer_transaction_begin(ptimer); - ptimer_set_count(ptimer, 1000); - ptimer_transaction_commit(ptimer); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 1000); - g_assert_false(triggered); - ptimer_free(ptimer); -} - -static void check_set_limit(gconstpointer arg) -{ - const uint8_t *policy = arg; - ptimer_state *ptimer = ptimer_init(ptimer_trigger, NULL, *policy); - - triggered = false; - - ptimer_transaction_begin(ptimer); - ptimer_set_limit(ptimer, 1000, 0); - ptimer_transaction_commit(ptimer); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); - g_assert_cmpuint(ptimer_get_limit(ptimer), ==, 1000); - g_assert_false(triggered); - - ptimer_transaction_begin(ptimer); - ptimer_set_limit(ptimer, 2000, 1); - ptimer_transaction_commit(ptimer); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 2000); - g_assert_cmpuint(ptimer_get_limit(ptimer), ==, 2000); - g_assert_false(triggered); - ptimer_free(ptimer); -} - -static void check_oneshot(gconstpointer arg) -{ - const uint8_t *policy = arg; - ptimer_state *ptimer = ptimer_init(ptimer_trigger, NULL, *policy); - bool no_round_down = (*policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN); - - triggered = false; - - ptimer_transaction_begin(ptimer); - ptimer_set_period(ptimer, 2000000); - ptimer_set_count(ptimer, 10); - ptimer_run(ptimer, 1); - ptimer_transaction_commit(ptimer); - - qemu_clock_step(2000000 * 2 + 1); - - g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 8 : 7); - g_assert_false(triggered); - - ptimer_transaction_begin(ptimer); - ptimer_stop(ptimer); - ptimer_transaction_commit(ptimer); - - g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 8 : 7); - g_assert_false(triggered); - - qemu_clock_step(2000000 * 11); - - g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 8 : 7); - g_assert_false(triggered); - - ptimer_transaction_begin(ptimer); - ptimer_run(ptimer, 1); - ptimer_transaction_commit(ptimer); - - qemu_clock_step(2000000 * 7 + 1); - - g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 1 : 0); - - if (no_round_down) { - g_assert_false(triggered); - } else { - g_assert_true(triggered); - - triggered = false; - } - - qemu_clock_step(2000000); - - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); - - if (no_round_down) { - g_assert_true(triggered); - - triggered = false; - } else { - g_assert_false(triggered); - } - - qemu_clock_step(4000000); - - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); - g_assert_false(triggered); - - ptimer_transaction_begin(ptimer); - ptimer_set_count(ptimer, 10); - ptimer_transaction_commit(ptimer); - - qemu_clock_step(20000000 + 1); - - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 10); - g_assert_false(triggered); - - ptimer_transaction_begin(ptimer); - ptimer_set_limit(ptimer, 9, 1); - ptimer_transaction_commit(ptimer); - - qemu_clock_step(20000000 + 1); - - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 9); - g_assert_false(triggered); - - ptimer_transaction_begin(ptimer); - ptimer_run(ptimer, 1); - ptimer_transaction_commit(ptimer); - - qemu_clock_step(2000000 + 1); - - g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 8 : 7); - g_assert_false(triggered); - - ptimer_transaction_begin(ptimer); - ptimer_set_count(ptimer, 20); - ptimer_transaction_commit(ptimer); - - qemu_clock_step(2000000 * 19 + 1); - - g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 1 : 0); - g_assert_false(triggered); - - qemu_clock_step(2000000); - - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); - g_assert_true(triggered); - - ptimer_transaction_begin(ptimer); - ptimer_stop(ptimer); - ptimer_transaction_commit(ptimer); - - triggered = false; - - qemu_clock_step(2000000 * 12 + 1); - - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); - g_assert_false(triggered); - ptimer_free(ptimer); -} - -static void check_periodic(gconstpointer arg) -{ - const uint8_t *policy = arg; - ptimer_state *ptimer = ptimer_init(ptimer_trigger, NULL, *policy); - bool wrap_policy = (*policy & PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD); - bool no_immediate_trigger = (*policy & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER); - bool no_immediate_reload = (*policy & PTIMER_POLICY_NO_IMMEDIATE_RELOAD); - bool no_round_down = (*policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN); - bool trig_only_on_dec = (*policy & PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT); - - triggered = false; - - ptimer_transaction_begin(ptimer); - ptimer_set_period(ptimer, 2000000); - ptimer_set_limit(ptimer, 10, 1); - ptimer_run(ptimer, 0); - ptimer_transaction_commit(ptimer); - - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 10); - g_assert_false(triggered); - - qemu_clock_step(1); - - g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 10 : 9); - g_assert_false(triggered); - - qemu_clock_step(2000000 * 10 - 1); - - g_assert_cmpuint(ptimer_get_count(ptimer), ==, wrap_policy ? 0 : 10); - g_assert_true(triggered); - - qemu_clock_step(1); - - g_assert_cmpuint(ptimer_get_count(ptimer), ==, - wrap_policy ? 0 : (no_round_down ? 10 : 9)); - g_assert_true(triggered); - - triggered = false; - - qemu_clock_step(2000000); - - g_assert_cmpuint(ptimer_get_count(ptimer), ==, - (no_round_down ? 9 : 8) + (wrap_policy ? 1 : 0)); - g_assert_false(triggered); - - ptimer_transaction_begin(ptimer); - ptimer_set_count(ptimer, 20); - ptimer_transaction_commit(ptimer); - - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 20); - g_assert_false(triggered); - - qemu_clock_step(1); - - g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 20 : 19); - g_assert_false(triggered); - - qemu_clock_step(2000000 * 11 + 1); - - g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 9 : 8); - g_assert_false(triggered); - - qemu_clock_step(2000000 * 10); - - g_assert_cmpuint(ptimer_get_count(ptimer), ==, - (no_round_down ? 9 : 8) + (wrap_policy ? 1 : 0)); - g_assert_true(triggered); - - triggered = false; - - ptimer_transaction_begin(ptimer); - ptimer_set_count(ptimer, 3); - ptimer_transaction_commit(ptimer); - - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 3); - g_assert_false(triggered); - - qemu_clock_step(1); - - g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 3 : 2); - g_assert_false(triggered); - - qemu_clock_step(2000000 * 4); - - g_assert_cmpuint(ptimer_get_count(ptimer), ==, - (no_round_down ? 9 : 8) + (wrap_policy ? 1 : 0)); - g_assert_true(triggered); - - ptimer_transaction_begin(ptimer); - ptimer_stop(ptimer); - ptimer_transaction_commit(ptimer); - triggered = false; - - qemu_clock_step(2000000); - - g_assert_cmpuint(ptimer_get_count(ptimer), ==, - (no_round_down ? 9 : 8) + (wrap_policy ? 1 : 0)); - g_assert_false(triggered); - - ptimer_transaction_begin(ptimer); - ptimer_set_count(ptimer, 3); - ptimer_run(ptimer, 0); - ptimer_transaction_commit(ptimer); - - qemu_clock_step(2000000 * 3 + 1); - - g_assert_cmpuint(ptimer_get_count(ptimer), ==, - wrap_policy ? 0 : (no_round_down ? 10 : 9)); - g_assert_true(triggered); - - triggered = false; - - qemu_clock_step(2000000); - - g_assert_cmpuint(ptimer_get_count(ptimer), ==, - (no_round_down ? 9 : 8) + (wrap_policy ? 1 : 0)); - g_assert_false(triggered); - - ptimer_transaction_begin(ptimer); - ptimer_set_count(ptimer, 0); - ptimer_transaction_commit(ptimer); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, - no_immediate_reload ? 0 : 10); - - if (no_immediate_trigger || trig_only_on_dec) { - g_assert_false(triggered); - } else { - g_assert_true(triggered); - } - - triggered = false; - - qemu_clock_step(1); - - if (no_immediate_reload) { - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); - g_assert_false(triggered); - - qemu_clock_step(2000000); - - if (no_immediate_trigger) { - g_assert_true(triggered); - } else { - g_assert_false(triggered); - } - - triggered = false; - } - - g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 10 : 9); - g_assert_false(triggered); - - qemu_clock_step(2000000 * 12); - - g_assert_cmpuint(ptimer_get_count(ptimer), ==, - (no_round_down ? 8 : 7) + (wrap_policy ? 1 : 0)); - g_assert_true(triggered); - - ptimer_transaction_begin(ptimer); - ptimer_stop(ptimer); - ptimer_transaction_commit(ptimer); - - triggered = false; - - qemu_clock_step(2000000 * 10); - - g_assert_cmpuint(ptimer_get_count(ptimer), ==, - (no_round_down ? 8 : 7) + (wrap_policy ? 1 : 0)); - g_assert_false(triggered); - - ptimer_transaction_begin(ptimer); - ptimer_run(ptimer, 0); - ptimer_transaction_commit(ptimer); - - ptimer_transaction_begin(ptimer); - ptimer_set_period(ptimer, 0); - ptimer_transaction_commit(ptimer); - - qemu_clock_step(2000000 + 1); - - g_assert_cmpuint(ptimer_get_count(ptimer), ==, - (no_round_down ? 8 : 7) + (wrap_policy ? 1 : 0)); - g_assert_false(triggered); - ptimer_free(ptimer); -} - -static void check_on_the_fly_mode_change(gconstpointer arg) -{ - const uint8_t *policy = arg; - ptimer_state *ptimer = ptimer_init(ptimer_trigger, NULL, *policy); - bool wrap_policy = (*policy & PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD); - bool no_round_down = (*policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN); - - triggered = false; - - ptimer_transaction_begin(ptimer); - ptimer_set_period(ptimer, 2000000); - ptimer_set_limit(ptimer, 10, 1); - ptimer_run(ptimer, 1); - ptimer_transaction_commit(ptimer); - - qemu_clock_step(2000000 * 9 + 1); - - g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 1 : 0); - g_assert_false(triggered); - - ptimer_transaction_begin(ptimer); - ptimer_run(ptimer, 0); - ptimer_transaction_commit(ptimer); - - g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 1 : 0); - g_assert_false(triggered); - - qemu_clock_step(2000000); - - g_assert_cmpuint(ptimer_get_count(ptimer), ==, - wrap_policy ? 0 : (no_round_down ? 10 : 9)); - g_assert_true(triggered); - - triggered = false; - - qemu_clock_step(2000000 * 9); - - ptimer_transaction_begin(ptimer); - ptimer_run(ptimer, 1); - ptimer_transaction_commit(ptimer); - - g_assert_cmpuint(ptimer_get_count(ptimer), ==, - (no_round_down ? 1 : 0) + (wrap_policy ? 1 : 0)); - g_assert_false(triggered); - - qemu_clock_step(2000000 * 3); - - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); - g_assert_true(triggered); - ptimer_free(ptimer); -} - -static void check_on_the_fly_period_change(gconstpointer arg) -{ - const uint8_t *policy = arg; - ptimer_state *ptimer = ptimer_init(ptimer_trigger, NULL, *policy); - bool no_round_down = (*policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN); - - triggered = false; - - ptimer_transaction_begin(ptimer); - ptimer_set_period(ptimer, 2000000); - ptimer_set_limit(ptimer, 8, 1); - ptimer_run(ptimer, 1); - ptimer_transaction_commit(ptimer); - - qemu_clock_step(2000000 * 4 + 1); - - g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 4 : 3); - g_assert_false(triggered); - - ptimer_transaction_begin(ptimer); - ptimer_set_period(ptimer, 4000000); - ptimer_transaction_commit(ptimer); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 4 : 3); - - qemu_clock_step(4000000 * 2 + 1); - - g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 2 : 0); - g_assert_false(triggered); - - qemu_clock_step(4000000 * 2); - - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); - g_assert_true(triggered); - ptimer_free(ptimer); -} - -static void check_on_the_fly_freq_change(gconstpointer arg) -{ - const uint8_t *policy = arg; - ptimer_state *ptimer = ptimer_init(ptimer_trigger, NULL, *policy); - bool no_round_down = (*policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN); - - triggered = false; - - ptimer_transaction_begin(ptimer); - ptimer_set_freq(ptimer, 500); - ptimer_set_limit(ptimer, 8, 1); - ptimer_run(ptimer, 1); - ptimer_transaction_commit(ptimer); - - qemu_clock_step(2000000 * 4 + 1); - - g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 4 : 3); - g_assert_false(triggered); - - ptimer_transaction_begin(ptimer); - ptimer_set_freq(ptimer, 250); - ptimer_transaction_commit(ptimer); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 4 : 3); - - qemu_clock_step(2000000 * 4 + 1); - - g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 2 : 0); - g_assert_false(triggered); - - qemu_clock_step(2000000 * 4); - - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); - g_assert_true(triggered); - ptimer_free(ptimer); -} - -static void check_run_with_period_0(gconstpointer arg) -{ - const uint8_t *policy = arg; - ptimer_state *ptimer = ptimer_init(ptimer_trigger, NULL, *policy); - - triggered = false; - - ptimer_transaction_begin(ptimer); - ptimer_set_count(ptimer, 99); - ptimer_run(ptimer, 1); - ptimer_transaction_commit(ptimer); - - qemu_clock_step(10 * NANOSECONDS_PER_SECOND); - - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 99); - g_assert_false(triggered); - ptimer_free(ptimer); -} - -static void check_run_with_delta_0(gconstpointer arg) -{ - const uint8_t *policy = arg; - ptimer_state *ptimer = ptimer_init(ptimer_trigger, NULL, *policy); - bool wrap_policy = (*policy & PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD); - bool no_immediate_trigger = (*policy & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER); - bool no_immediate_reload = (*policy & PTIMER_POLICY_NO_IMMEDIATE_RELOAD); - bool no_round_down = (*policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN); - bool trig_only_on_dec = (*policy & PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT); - - triggered = false; - - ptimer_transaction_begin(ptimer); - ptimer_set_period(ptimer, 2000000); - ptimer_set_limit(ptimer, 99, 0); - ptimer_run(ptimer, 1); - ptimer_transaction_commit(ptimer); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, - no_immediate_reload ? 0 : 99); - - if (no_immediate_trigger || trig_only_on_dec) { - g_assert_false(triggered); - } else { - g_assert_true(triggered); - } - - triggered = false; - - if (no_immediate_trigger || no_immediate_reload) { - qemu_clock_step(2000000 + 1); - - g_assert_cmpuint(ptimer_get_count(ptimer), ==, - no_immediate_reload ? 0 : (no_round_down ? 98 : 97)); - - if (no_immediate_trigger && no_immediate_reload) { - g_assert_true(triggered); - - triggered = false; - } else { - g_assert_false(triggered); - } - - ptimer_transaction_begin(ptimer); - ptimer_set_count(ptimer, 99); - ptimer_run(ptimer, 1); - ptimer_transaction_commit(ptimer); - } - - qemu_clock_step(2000000 + 1); - - g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 98 : 97); - g_assert_false(triggered); - - qemu_clock_step(2000000 * 97); - - g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 1 : 0); - g_assert_false(triggered); - - qemu_clock_step(2000000 * 2); - - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); - g_assert_true(triggered); - - triggered = false; - - ptimer_transaction_begin(ptimer); - ptimer_set_count(ptimer, 0); - ptimer_run(ptimer, 0); - ptimer_transaction_commit(ptimer); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, - no_immediate_reload ? 0 : 99); - - if (no_immediate_trigger || trig_only_on_dec) { - g_assert_false(triggered); - } else { - g_assert_true(triggered); - } - - triggered = false; - - qemu_clock_step(1); - - if (no_immediate_reload) { - qemu_clock_step(2000000); - } - - g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 99 : 98); - - if (no_immediate_reload && no_immediate_trigger) { - g_assert_true(triggered); - } else { - g_assert_false(triggered); - } - - triggered = false; - - qemu_clock_step(2000000); - - g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 98 : 97); - g_assert_false(triggered); - - qemu_clock_step(2000000 * 98); - - g_assert_cmpuint(ptimer_get_count(ptimer), ==, - wrap_policy ? 0 : (no_round_down ? 99 : 98)); - g_assert_true(triggered); - - ptimer_transaction_begin(ptimer); - ptimer_stop(ptimer); - ptimer_transaction_commit(ptimer); - ptimer_free(ptimer); -} - -static void check_periodic_with_load_0(gconstpointer arg) -{ - const uint8_t *policy = arg; - ptimer_state *ptimer = ptimer_init(ptimer_trigger, NULL, *policy); - bool continuous_trigger = (*policy & PTIMER_POLICY_CONTINUOUS_TRIGGER); - bool no_immediate_trigger = (*policy & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER); - bool trig_only_on_dec = (*policy & PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT); - - triggered = false; - - ptimer_transaction_begin(ptimer); - ptimer_set_period(ptimer, 2000000); - ptimer_run(ptimer, 0); - ptimer_transaction_commit(ptimer); - - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); - - if (no_immediate_trigger || trig_only_on_dec) { - g_assert_false(triggered); - } else { - g_assert_true(triggered); - } - - triggered = false; - - qemu_clock_step(2000000 + 1); - - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); - - if (continuous_trigger || no_immediate_trigger) { - g_assert_true(triggered); - } else { - g_assert_false(triggered); - } - - triggered = false; - - ptimer_transaction_begin(ptimer); - ptimer_set_count(ptimer, 10); - ptimer_run(ptimer, 0); - ptimer_transaction_commit(ptimer); - - qemu_clock_step(2000000 * 10 + 1); - - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); - g_assert_true(triggered); - - triggered = false; - - qemu_clock_step(2000000 + 1); - - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); - - if (continuous_trigger) { - g_assert_true(triggered); - } else { - g_assert_false(triggered); - } - - ptimer_transaction_begin(ptimer); - ptimer_stop(ptimer); - ptimer_transaction_commit(ptimer); - ptimer_free(ptimer); -} - -static void check_oneshot_with_load_0(gconstpointer arg) -{ - const uint8_t *policy = arg; - ptimer_state *ptimer = ptimer_init(ptimer_trigger, NULL, *policy); - bool no_immediate_trigger = (*policy & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER); - bool trig_only_on_dec = (*policy & PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT); - - triggered = false; - - ptimer_transaction_begin(ptimer); - ptimer_set_period(ptimer, 2000000); - ptimer_run(ptimer, 1); - ptimer_transaction_commit(ptimer); - - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); - - if (no_immediate_trigger || trig_only_on_dec) { - g_assert_false(triggered); - } else { - g_assert_true(triggered); - } - - triggered = false; - - qemu_clock_step(2000000 + 1); - - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); - - if (no_immediate_trigger) { - g_assert_true(triggered); - } else { - g_assert_false(triggered); - } - - ptimer_free(ptimer); -} - -static void add_ptimer_tests(uint8_t policy) -{ - char policy_name[256] = ""; - char *tmp; - - if (policy == PTIMER_POLICY_DEFAULT) { - g_sprintf(policy_name, "default"); - } - - if (policy & PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD) { - g_strlcat(policy_name, "wrap_after_one_period,", 256); - } - - if (policy & PTIMER_POLICY_CONTINUOUS_TRIGGER) { - g_strlcat(policy_name, "continuous_trigger,", 256); - } - - if (policy & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER) { - g_strlcat(policy_name, "no_immediate_trigger,", 256); - } - - if (policy & PTIMER_POLICY_NO_IMMEDIATE_RELOAD) { - g_strlcat(policy_name, "no_immediate_reload,", 256); - } - - if (policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN) { - g_strlcat(policy_name, "no_counter_rounddown,", 256); - } - - if (policy & PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT) { - g_strlcat(policy_name, "trigger_only_on_decrement,", 256); - } - - g_test_add_data_func_full( - tmp = g_strdup_printf("/ptimer/set_count policy=%s", policy_name), - g_memdup(&policy, 1), check_set_count, g_free); - g_free(tmp); - - g_test_add_data_func_full( - tmp = g_strdup_printf("/ptimer/set_limit policy=%s", policy_name), - g_memdup(&policy, 1), check_set_limit, g_free); - g_free(tmp); - - g_test_add_data_func_full( - tmp = g_strdup_printf("/ptimer/oneshot policy=%s", policy_name), - g_memdup(&policy, 1), check_oneshot, g_free); - g_free(tmp); - - g_test_add_data_func_full( - tmp = g_strdup_printf("/ptimer/periodic policy=%s", policy_name), - g_memdup(&policy, 1), check_periodic, g_free); - g_free(tmp); - - g_test_add_data_func_full( - tmp = g_strdup_printf("/ptimer/on_the_fly_mode_change policy=%s", - policy_name), - g_memdup(&policy, 1), check_on_the_fly_mode_change, g_free); - g_free(tmp); - - g_test_add_data_func_full( - tmp = g_strdup_printf("/ptimer/on_the_fly_period_change policy=%s", - policy_name), - g_memdup(&policy, 1), check_on_the_fly_period_change, g_free); - g_free(tmp); - - g_test_add_data_func_full( - tmp = g_strdup_printf("/ptimer/on_the_fly_freq_change policy=%s", - policy_name), - g_memdup(&policy, 1), check_on_the_fly_freq_change, g_free); - g_free(tmp); - - g_test_add_data_func_full( - tmp = g_strdup_printf("/ptimer/run_with_period_0 policy=%s", - policy_name), - g_memdup(&policy, 1), check_run_with_period_0, g_free); - g_free(tmp); - - g_test_add_data_func_full( - tmp = g_strdup_printf("/ptimer/run_with_delta_0 policy=%s", - policy_name), - g_memdup(&policy, 1), check_run_with_delta_0, g_free); - g_free(tmp); - - g_test_add_data_func_full( - tmp = g_strdup_printf("/ptimer/periodic_with_load_0 policy=%s", - policy_name), - g_memdup(&policy, 1), check_periodic_with_load_0, g_free); - g_free(tmp); - - g_test_add_data_func_full( - tmp = g_strdup_printf("/ptimer/oneshot_with_load_0 policy=%s", - policy_name), - g_memdup(&policy, 1), check_oneshot_with_load_0, g_free); - g_free(tmp); -} - -static void add_all_ptimer_policies_comb_tests(void) -{ - int last_policy = PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT; - int policy = PTIMER_POLICY_DEFAULT; - - for (; policy < (last_policy << 1); policy++) { - if ((policy & PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT) && - (policy & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER)) { - /* Incompatible policy flag settings -- don't try to test them */ - continue; - } - add_ptimer_tests(policy); - } -} - -int main(int argc, char **argv) -{ - int i; - - g_test_init(&argc, &argv, NULL); - - for (i = 0; i < QEMU_CLOCK_MAX; i++) { - main_loop_tlg.tl[i] = g_new0(QEMUTimerList, 1); - } - - add_all_ptimer_policies_comb_tests(); - - qtest_allowed = true; - - return g_test_run(); -} diff --git a/tests/ptimer-test.h b/tests/ptimer-test.h deleted file mode 100644 index 09ac56da9e..0000000000 --- a/tests/ptimer-test.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * QTest testcase for the ptimer - * - * Copyright (c) 2016 Dmitry Osipenko - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#ifndef PTIMER_TEST_H -#define PTIMER_TEST_H - -extern bool qtest_allowed; - -extern int64_t ptimer_test_time_ns; - -struct QEMUTimerList { - QEMUTimer active_timers; -}; - -#endif diff --git a/tests/rcutorture.c b/tests/rcutorture.c deleted file mode 100644 index de6f649058..0000000000 --- a/tests/rcutorture.c +++ /dev/null @@ -1,483 +0,0 @@ -/* - * rcutorture.c: simple user-level performance/stress test of RCU. - * - * Usage: - * ./rcu rperf [ ] - * Run a read-side performance test with the specified - * number of readers for seconds. - * ./rcu uperf [ ] - * Run an update-side performance test with the specified - * number of updaters and specified duration. - * ./rcu perf [ ] - * Run a combined read/update performance test with the specified - * number of readers and one updater and specified duration. - * - * The above tests produce output as follows: - * - * n_reads: 46008000 n_updates: 146026 nreaders: 2 nupdaters: 1 duration: 1 - * ns/read: 43.4707 ns/update: 6848.1 - * - * The first line lists the total number of RCU reads and updates executed - * during the test, the number of reader threads, the number of updater - * threads, and the duration of the test in seconds. The second line - * lists the average duration of each type of operation in nanoseconds, - * or "nan" if the corresponding type of operation was not performed. - * - * ./rcu stress [ ] - * Run a stress test with the specified number of readers and - * one updater. - * - * This test produces output as follows: - * - * n_reads: 114633217 n_updates: 3903415 n_mberror: 0 - * rcu_stress_count: 114618391 14826 0 0 0 0 0 0 0 0 0 - * - * The first line lists the number of RCU read and update operations - * executed, followed by the number of memory-ordering violations - * (which will be zero in a correct RCU implementation). The second - * line lists the number of readers observing progressively more stale - * data. A correct RCU implementation will have all but the first two - * numbers non-zero. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - * Copyright (c) 2008 Paul E. McKenney, IBM Corporation. - */ - -/* - * Test variables. - */ - -#include "qemu/osdep.h" -#include "qemu/atomic.h" -#include "qemu/rcu.h" -#include "qemu/thread.h" - -int nthreadsrunning; - -#define GOFLAG_INIT 0 -#define GOFLAG_RUN 1 -#define GOFLAG_STOP 2 - -static volatile int goflag = GOFLAG_INIT; - -#define RCU_READ_RUN 1000 - -#define NR_THREADS 100 -static QemuThread threads[NR_THREADS]; -static struct rcu_reader_data *data[NR_THREADS]; -static int n_threads; - -/* - * Statistical counts - * - * These are the sum of local counters at the end of a run. - * Updates are protected by a mutex. - */ -static QemuMutex counts_mutex; -long long n_reads = 0LL; -long n_updates = 0L; - -static void create_thread(void *(*func)(void *)) -{ - if (n_threads >= NR_THREADS) { - fprintf(stderr, "Thread limit of %d exceeded!\n", NR_THREADS); - exit(-1); - } - qemu_thread_create(&threads[n_threads], "test", func, &data[n_threads], - QEMU_THREAD_JOINABLE); - n_threads++; -} - -static void wait_all_threads(void) -{ - int i; - - for (i = 0; i < n_threads; i++) { - qemu_thread_join(&threads[i]); - } - n_threads = 0; -} - -/* - * Performance test. - */ - -static void *rcu_read_perf_test(void *arg) -{ - int i; - long long n_reads_local = 0; - - rcu_register_thread(); - - *(struct rcu_reader_data **)arg = &rcu_reader; - qatomic_inc(&nthreadsrunning); - while (goflag == GOFLAG_INIT) { - g_usleep(1000); - } - while (goflag == GOFLAG_RUN) { - for (i = 0; i < RCU_READ_RUN; i++) { - rcu_read_lock(); - rcu_read_unlock(); - } - n_reads_local += RCU_READ_RUN; - } - qemu_mutex_lock(&counts_mutex); - n_reads += n_reads_local; - qemu_mutex_unlock(&counts_mutex); - - rcu_unregister_thread(); - return NULL; -} - -static void *rcu_update_perf_test(void *arg) -{ - long long n_updates_local = 0; - - rcu_register_thread(); - - *(struct rcu_reader_data **)arg = &rcu_reader; - qatomic_inc(&nthreadsrunning); - while (goflag == GOFLAG_INIT) { - g_usleep(1000); - } - while (goflag == GOFLAG_RUN) { - synchronize_rcu(); - n_updates_local++; - } - qemu_mutex_lock(&counts_mutex); - n_updates += n_updates_local; - qemu_mutex_unlock(&counts_mutex); - - rcu_unregister_thread(); - return NULL; -} - -static void perftestinit(void) -{ - nthreadsrunning = 0; -} - -static void perftestrun(int nthreads, int duration, int nreaders, int nupdaters) -{ - while (qatomic_read(&nthreadsrunning) < nthreads) { - g_usleep(1000); - } - goflag = GOFLAG_RUN; - g_usleep(duration * G_USEC_PER_SEC); - goflag = GOFLAG_STOP; - wait_all_threads(); - printf("n_reads: %lld n_updates: %ld nreaders: %d nupdaters: %d duration: %d\n", - n_reads, n_updates, nreaders, nupdaters, duration); - printf("ns/read: %g ns/update: %g\n", - ((duration * 1000*1000*1000.*(double)nreaders) / - (double)n_reads), - ((duration * 1000*1000*1000.*(double)nupdaters) / - (double)n_updates)); - exit(0); -} - -static void perftest(int nreaders, int duration) -{ - int i; - - perftestinit(); - for (i = 0; i < nreaders; i++) { - create_thread(rcu_read_perf_test); - } - create_thread(rcu_update_perf_test); - perftestrun(i + 1, duration, nreaders, 1); -} - -static void rperftest(int nreaders, int duration) -{ - int i; - - perftestinit(); - for (i = 0; i < nreaders; i++) { - create_thread(rcu_read_perf_test); - } - perftestrun(i, duration, nreaders, 0); -} - -static void uperftest(int nupdaters, int duration) -{ - int i; - - perftestinit(); - for (i = 0; i < nupdaters; i++) { - create_thread(rcu_update_perf_test); - } - perftestrun(i, duration, 0, nupdaters); -} - -/* - * Stress test. - */ - -#define RCU_STRESS_PIPE_LEN 10 - -struct rcu_stress { - int age; /* how many update cycles while not rcu_stress_current */ - int mbtest; -}; - -struct rcu_stress rcu_stress_array[RCU_STRESS_PIPE_LEN] = { { 0 } }; -struct rcu_stress *rcu_stress_current; -int n_mberror; - -/* Updates protected by counts_mutex */ -long long rcu_stress_count[RCU_STRESS_PIPE_LEN + 1]; - - -static void *rcu_read_stress_test(void *arg) -{ - int i; - struct rcu_stress *p; - int pc; - long long n_reads_local = 0; - long long rcu_stress_local[RCU_STRESS_PIPE_LEN + 1] = { 0 }; - volatile int garbage = 0; - - rcu_register_thread(); - - *(struct rcu_reader_data **)arg = &rcu_reader; - while (goflag == GOFLAG_INIT) { - g_usleep(1000); - } - while (goflag == GOFLAG_RUN) { - rcu_read_lock(); - p = qatomic_rcu_read(&rcu_stress_current); - if (qatomic_read(&p->mbtest) == 0) { - n_mberror++; - } - rcu_read_lock(); - for (i = 0; i < 100; i++) { - garbage++; - } - rcu_read_unlock(); - pc = qatomic_read(&p->age); - rcu_read_unlock(); - if ((pc > RCU_STRESS_PIPE_LEN) || (pc < 0)) { - pc = RCU_STRESS_PIPE_LEN; - } - rcu_stress_local[pc]++; - n_reads_local++; - } - qemu_mutex_lock(&counts_mutex); - n_reads += n_reads_local; - for (i = 0; i <= RCU_STRESS_PIPE_LEN; i++) { - rcu_stress_count[i] += rcu_stress_local[i]; - } - qemu_mutex_unlock(&counts_mutex); - - rcu_unregister_thread(); - return NULL; -} - -/* - * Stress Test Updater - * - * The updater cycles around updating rcu_stress_current to point at - * one of the rcu_stress_array_entries and resets it's age. It - * then increments the age of all the other entries. The age - * will be read under an rcu_read_lock() and distribution of values - * calculated. The final result gives an indication of how many - * previously current rcu_stress entries are in flight until the RCU - * cycle complete. - */ -static void *rcu_update_stress_test(void *arg) -{ - int i, rcu_stress_idx = 0; - struct rcu_stress *cp = qatomic_read(&rcu_stress_current); - - rcu_register_thread(); - *(struct rcu_reader_data **)arg = &rcu_reader; - - while (goflag == GOFLAG_INIT) { - g_usleep(1000); - } - - while (goflag == GOFLAG_RUN) { - struct rcu_stress *p; - rcu_stress_idx++; - if (rcu_stress_idx >= RCU_STRESS_PIPE_LEN) { - rcu_stress_idx = 0; - } - p = &rcu_stress_array[rcu_stress_idx]; - /* catching up with ourselves would be a bug */ - assert(p != cp); - qatomic_set(&p->mbtest, 0); - smp_mb(); - qatomic_set(&p->age, 0); - qatomic_set(&p->mbtest, 1); - qatomic_rcu_set(&rcu_stress_current, p); - cp = p; - /* - * New RCU structure is now live, update pipe counts on old - * ones. - */ - for (i = 0; i < RCU_STRESS_PIPE_LEN; i++) { - if (i != rcu_stress_idx) { - qatomic_set(&rcu_stress_array[i].age, - rcu_stress_array[i].age + 1); - } - } - synchronize_rcu(); - n_updates++; - } - - rcu_unregister_thread(); - return NULL; -} - -static void *rcu_fake_update_stress_test(void *arg) -{ - rcu_register_thread(); - - *(struct rcu_reader_data **)arg = &rcu_reader; - while (goflag == GOFLAG_INIT) { - g_usleep(1000); - } - while (goflag == GOFLAG_RUN) { - synchronize_rcu(); - g_usleep(1000); - } - - rcu_unregister_thread(); - return NULL; -} - -static void stresstest(int nreaders, int duration) -{ - int i; - - rcu_stress_current = &rcu_stress_array[0]; - rcu_stress_current->age = 0; - rcu_stress_current->mbtest = 1; - for (i = 0; i < nreaders; i++) { - create_thread(rcu_read_stress_test); - } - create_thread(rcu_update_stress_test); - for (i = 0; i < 5; i++) { - create_thread(rcu_fake_update_stress_test); - } - goflag = GOFLAG_RUN; - g_usleep(duration * G_USEC_PER_SEC); - goflag = GOFLAG_STOP; - wait_all_threads(); - printf("n_reads: %lld n_updates: %ld n_mberror: %d\n", - n_reads, n_updates, n_mberror); - printf("rcu_stress_count:"); - for (i = 0; i <= RCU_STRESS_PIPE_LEN; i++) { - printf(" %lld", rcu_stress_count[i]); - } - printf("\n"); - exit(0); -} - -/* GTest interface */ - -static void gtest_stress(int nreaders, int duration) -{ - int i; - - rcu_stress_current = &rcu_stress_array[0]; - rcu_stress_current->age = 0; - rcu_stress_current->mbtest = 1; - for (i = 0; i < nreaders; i++) { - create_thread(rcu_read_stress_test); - } - create_thread(rcu_update_stress_test); - for (i = 0; i < 5; i++) { - create_thread(rcu_fake_update_stress_test); - } - goflag = GOFLAG_RUN; - g_usleep(duration * G_USEC_PER_SEC); - goflag = GOFLAG_STOP; - wait_all_threads(); - g_assert_cmpint(n_mberror, ==, 0); - for (i = 2; i <= RCU_STRESS_PIPE_LEN; i++) { - g_assert_cmpint(rcu_stress_count[i], ==, 0); - } -} - -static void gtest_stress_1_1(void) -{ - gtest_stress(1, 1); -} - -static void gtest_stress_10_1(void) -{ - gtest_stress(10, 1); -} - -static void gtest_stress_1_5(void) -{ - gtest_stress(1, 5); -} - -static void gtest_stress_10_5(void) -{ - gtest_stress(10, 5); -} - -/* - * Mainprogram. - */ - -static void usage(int argc, char *argv[]) -{ - fprintf(stderr, "Usage: %s [nreaders [ [r|u]perf | stress [duration]]\n", - argv[0]); - exit(-1); -} - -int main(int argc, char *argv[]) -{ - int nreaders = 1; - int duration = 1; - - qemu_mutex_init(&counts_mutex); - if (argc >= 2 && argv[1][0] == '-') { - g_test_init(&argc, &argv, NULL); - if (g_test_quick()) { - g_test_add_func("/rcu/torture/1reader", gtest_stress_1_1); - g_test_add_func("/rcu/torture/10readers", gtest_stress_10_1); - } else { - g_test_add_func("/rcu/torture/1reader", gtest_stress_1_5); - g_test_add_func("/rcu/torture/10readers", gtest_stress_10_5); - } - return g_test_run(); - } - - if (argc >= 2) { - nreaders = strtoul(argv[1], NULL, 0); - } - if (argc > 3) { - duration = strtoul(argv[3], NULL, 0); - } - if (argc < 3 || strcmp(argv[2], "stress") == 0) { - stresstest(nreaders, duration); - } else if (strcmp(argv[2], "rperf") == 0) { - rperftest(nreaders, duration); - } else if (strcmp(argv[2], "uperf") == 0) { - uperftest(nreaders, duration); - } else if (strcmp(argv[2], "perf") == 0) { - perftest(nreaders, duration); - } - usage(argc, argv); - return 0; -} diff --git a/tests/socket-helpers.c b/tests/socket-helpers.c deleted file mode 100644 index f704fd1a69..0000000000 --- a/tests/socket-helpers.c +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Helper functions for tests using sockets - * - * Copyright 2015-2018 Red Hat, Inc. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - */ - -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "qemu/sockets.h" -#include "socket-helpers.h" - -#ifndef AI_ADDRCONFIG -# define AI_ADDRCONFIG 0 -#endif -#ifndef EAI_ADDRFAMILY -# define EAI_ADDRFAMILY 0 -#endif - -/* - * @hostname: a DNS name or numeric IP address - * - * Check whether it is possible to bind & connect to ports - * on the DNS name or IP address @hostname. If an IP address - * is used, it must not be a wildcard address. - * - * Returns 0 on success, -1 on error with errno set - */ -static int socket_can_bind_connect(const char *hostname, int family) -{ - int lfd = -1, cfd = -1, afd = -1; - struct addrinfo ai, *res = NULL; - struct sockaddr_storage ss; - socklen_t sslen = sizeof(ss); - int soerr; - socklen_t soerrlen = sizeof(soerr); - bool check_soerr = false; - int rc; - int ret = -1; - - memset(&ai, 0, sizeof(ai)); - ai.ai_flags = AI_CANONNAME | AI_ADDRCONFIG; - ai.ai_family = family; - ai.ai_socktype = SOCK_STREAM; - - /* lookup */ - rc = getaddrinfo(hostname, NULL, &ai, &res); - if (rc != 0) { - if (rc == EAI_ADDRFAMILY || rc == EAI_FAMILY || rc == EAI_NONAME) { - errno = EADDRNOTAVAIL; - } else { - errno = EINVAL; - } - goto cleanup; - } - - lfd = qemu_socket(res->ai_family, res->ai_socktype, res->ai_protocol); - if (lfd < 0) { - goto cleanup; - } - - cfd = qemu_socket(res->ai_family, res->ai_socktype, res->ai_protocol); - if (cfd < 0) { - goto cleanup; - } - - if (bind(lfd, res->ai_addr, res->ai_addrlen) < 0) { - goto cleanup; - } - - if (listen(lfd, 1) < 0) { - goto cleanup; - } - - if (getsockname(lfd, (struct sockaddr *)&ss, &sslen) < 0) { - goto cleanup; - } - - qemu_set_nonblock(cfd); - if (connect(cfd, (struct sockaddr *)&ss, sslen) < 0) { - if (errno == EINPROGRESS) { - check_soerr = true; - } else { - goto cleanup; - } - } - - sslen = sizeof(ss); - afd = accept(lfd, (struct sockaddr *)&ss, &sslen); - if (afd < 0) { - goto cleanup; - } - - if (check_soerr) { - if (qemu_getsockopt(cfd, SOL_SOCKET, SO_ERROR, &soerr, &soerrlen) < 0) { - goto cleanup; - } - if (soerr) { - errno = soerr; - goto cleanup; - } - } - - ret = 0; - - cleanup: - if (afd != -1) { - close(afd); - } - if (cfd != -1) { - close(cfd); - } - if (lfd != -1) { - close(lfd); - } - if (res) { - freeaddrinfo(res); - } - return ret; -} - - -int socket_check_protocol_support(bool *has_ipv4, bool *has_ipv6) -{ - *has_ipv4 = *has_ipv6 = false; - - if (socket_can_bind_connect("127.0.0.1", PF_INET) < 0) { - if (errno != EADDRNOTAVAIL) { - return -1; - } - } else { - *has_ipv4 = true; - } - - if (socket_can_bind_connect("::1", PF_INET6) < 0) { - if (errno != EADDRNOTAVAIL) { - return -1; - } - } else { - *has_ipv6 = true; - } - - return 0; -} diff --git a/tests/socket-helpers.h b/tests/socket-helpers.h deleted file mode 100644 index 512a004811..0000000000 --- a/tests/socket-helpers.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Helper functions for tests using sockets - * - * Copyright 2015-2018 Red Hat, Inc. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - */ - -#ifndef TESTS_SOCKET_HELPERS_H -#define TESTS_SOCKET_HELPERS_H - -/* - * @has_ipv4: set to true on return if IPv4 is available - * @has_ipv6: set to true on return if IPv6 is available - * - * Check whether IPv4 and/or IPv6 are available for use. - * On success, @has_ipv4 and @has_ipv6 will be set to - * indicate whether the respective protocols are available. - * - * Returns 0 on success, -1 on fatal error - */ -int socket_check_protocol_support(bool *has_ipv4, bool *has_ipv6); - -#endif diff --git a/tests/test-aio-multithread.c b/tests/test-aio-multithread.c deleted file mode 100644 index a555cc8835..0000000000 --- a/tests/test-aio-multithread.c +++ /dev/null @@ -1,460 +0,0 @@ -/* - * AioContext multithreading tests - * - * Copyright Red Hat, Inc. 2016 - * - * Authors: - * Paolo Bonzini - * - * This work is licensed under the terms of the GNU LGPL, version 2 or later. - * See the COPYING.LIB file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "block/aio.h" -#include "qemu/coroutine.h" -#include "qemu/thread.h" -#include "qemu/error-report.h" -#include "iothread.h" - -/* AioContext management */ - -#define NUM_CONTEXTS 5 - -static IOThread *threads[NUM_CONTEXTS]; -static AioContext *ctx[NUM_CONTEXTS]; -static __thread int id = -1; - -static QemuEvent done_event; - -/* Run a function synchronously on a remote iothread. */ - -typedef struct CtxRunData { - QEMUBHFunc *cb; - void *arg; -} CtxRunData; - -static void ctx_run_bh_cb(void *opaque) -{ - CtxRunData *data = opaque; - - data->cb(data->arg); - qemu_event_set(&done_event); -} - -static void ctx_run(int i, QEMUBHFunc *cb, void *opaque) -{ - CtxRunData data = { - .cb = cb, - .arg = opaque - }; - - qemu_event_reset(&done_event); - aio_bh_schedule_oneshot(ctx[i], ctx_run_bh_cb, &data); - qemu_event_wait(&done_event); -} - -/* Starting the iothreads. */ - -static void set_id_cb(void *opaque) -{ - int *i = opaque; - - id = *i; -} - -static void create_aio_contexts(void) -{ - int i; - - for (i = 0; i < NUM_CONTEXTS; i++) { - threads[i] = iothread_new(); - ctx[i] = iothread_get_aio_context(threads[i]); - } - - qemu_event_init(&done_event, false); - for (i = 0; i < NUM_CONTEXTS; i++) { - ctx_run(i, set_id_cb, &i); - } -} - -/* Stopping the iothreads. */ - -static void join_aio_contexts(void) -{ - int i; - - for (i = 0; i < NUM_CONTEXTS; i++) { - aio_context_ref(ctx[i]); - } - for (i = 0; i < NUM_CONTEXTS; i++) { - iothread_join(threads[i]); - } - for (i = 0; i < NUM_CONTEXTS; i++) { - aio_context_unref(ctx[i]); - } - qemu_event_destroy(&done_event); -} - -/* Basic test for the stuff above. */ - -static void test_lifecycle(void) -{ - create_aio_contexts(); - join_aio_contexts(); -} - -/* aio_co_schedule test. */ - -static Coroutine *to_schedule[NUM_CONTEXTS]; - -static bool now_stopping; - -static int count_retry; -static int count_here; -static int count_other; - -static bool schedule_next(int n) -{ - Coroutine *co; - - co = qatomic_xchg(&to_schedule[n], NULL); - if (!co) { - qatomic_inc(&count_retry); - return false; - } - - if (n == id) { - qatomic_inc(&count_here); - } else { - qatomic_inc(&count_other); - } - - aio_co_schedule(ctx[n], co); - return true; -} - -static void finish_cb(void *opaque) -{ - schedule_next(id); -} - -static coroutine_fn void test_multi_co_schedule_entry(void *opaque) -{ - g_assert(to_schedule[id] == NULL); - - while (!qatomic_mb_read(&now_stopping)) { - int n; - - n = g_test_rand_int_range(0, NUM_CONTEXTS); - schedule_next(n); - - qatomic_mb_set(&to_schedule[id], qemu_coroutine_self()); - qemu_coroutine_yield(); - g_assert(to_schedule[id] == NULL); - } -} - - -static void test_multi_co_schedule(int seconds) -{ - int i; - - count_here = count_other = count_retry = 0; - now_stopping = false; - - create_aio_contexts(); - for (i = 0; i < NUM_CONTEXTS; i++) { - Coroutine *co1 = qemu_coroutine_create(test_multi_co_schedule_entry, NULL); - aio_co_schedule(ctx[i], co1); - } - - g_usleep(seconds * 1000000); - - qatomic_mb_set(&now_stopping, true); - for (i = 0; i < NUM_CONTEXTS; i++) { - ctx_run(i, finish_cb, NULL); - to_schedule[i] = NULL; - } - - join_aio_contexts(); - g_test_message("scheduled %d, queued %d, retry %d, total %d", - count_other, count_here, count_retry, - count_here + count_other + count_retry); -} - -static void test_multi_co_schedule_1(void) -{ - test_multi_co_schedule(1); -} - -static void test_multi_co_schedule_10(void) -{ - test_multi_co_schedule(10); -} - -/* CoMutex thread-safety. */ - -static uint32_t atomic_counter; -static uint32_t running; -static uint32_t counter; -static CoMutex comutex; - -static void coroutine_fn test_multi_co_mutex_entry(void *opaque) -{ - while (!qatomic_mb_read(&now_stopping)) { - qemu_co_mutex_lock(&comutex); - counter++; - qemu_co_mutex_unlock(&comutex); - - /* Increase atomic_counter *after* releasing the mutex. Otherwise - * there is a chance (it happens about 1 in 3 runs) that the iothread - * exits before the coroutine is woken up, causing a spurious - * assertion failure. - */ - qatomic_inc(&atomic_counter); - } - qatomic_dec(&running); -} - -static void test_multi_co_mutex(int threads, int seconds) -{ - int i; - - qemu_co_mutex_init(&comutex); - counter = 0; - atomic_counter = 0; - now_stopping = false; - - create_aio_contexts(); - assert(threads <= NUM_CONTEXTS); - running = threads; - for (i = 0; i < threads; i++) { - Coroutine *co1 = qemu_coroutine_create(test_multi_co_mutex_entry, NULL); - aio_co_schedule(ctx[i], co1); - } - - g_usleep(seconds * 1000000); - - qatomic_mb_set(&now_stopping, true); - while (running > 0) { - g_usleep(100000); - } - - join_aio_contexts(); - g_test_message("%d iterations/second", counter / seconds); - g_assert_cmpint(counter, ==, atomic_counter); -} - -/* Testing with NUM_CONTEXTS threads focuses on the queue. The mutex however - * is too contended (and the threads spend too much time in aio_poll) - * to actually stress the handoff protocol. - */ -static void test_multi_co_mutex_1(void) -{ - test_multi_co_mutex(NUM_CONTEXTS, 1); -} - -static void test_multi_co_mutex_10(void) -{ - test_multi_co_mutex(NUM_CONTEXTS, 10); -} - -/* Testing with fewer threads stresses the handoff protocol too. Still, the - * case where the locker _can_ pick up a handoff is very rare, happening - * about 10 times in 1 million, so increase the runtime a bit compared to - * other "quick" testcases that only run for 1 second. - */ -static void test_multi_co_mutex_2_3(void) -{ - test_multi_co_mutex(2, 3); -} - -static void test_multi_co_mutex_2_30(void) -{ - test_multi_co_mutex(2, 30); -} - -/* Same test with fair mutexes, for performance comparison. */ - -#ifdef CONFIG_LINUX -#include "qemu/futex.h" - -/* The nodes for the mutex reside in this structure (on which we try to avoid - * false sharing). The head of the mutex is in the "mutex_head" variable. - */ -static struct { - int next, locked; - int padding[14]; -} nodes[NUM_CONTEXTS] __attribute__((__aligned__(64))); - -static int mutex_head = -1; - -static void mcs_mutex_lock(void) -{ - int prev; - - nodes[id].next = -1; - nodes[id].locked = 1; - prev = qatomic_xchg(&mutex_head, id); - if (prev != -1) { - qatomic_set(&nodes[prev].next, id); - qemu_futex_wait(&nodes[id].locked, 1); - } -} - -static void mcs_mutex_unlock(void) -{ - int next; - if (qatomic_read(&nodes[id].next) == -1) { - if (qatomic_read(&mutex_head) == id && - qatomic_cmpxchg(&mutex_head, id, -1) == id) { - /* Last item in the list, exit. */ - return; - } - while (qatomic_read(&nodes[id].next) == -1) { - /* mcs_mutex_lock did the xchg, but has not updated - * nodes[prev].next yet. - */ - } - } - - /* Wake up the next in line. */ - next = qatomic_read(&nodes[id].next); - nodes[next].locked = 0; - qemu_futex_wake(&nodes[next].locked, 1); -} - -static void test_multi_fair_mutex_entry(void *opaque) -{ - while (!qatomic_mb_read(&now_stopping)) { - mcs_mutex_lock(); - counter++; - mcs_mutex_unlock(); - qatomic_inc(&atomic_counter); - } - qatomic_dec(&running); -} - -static void test_multi_fair_mutex(int threads, int seconds) -{ - int i; - - assert(mutex_head == -1); - counter = 0; - atomic_counter = 0; - now_stopping = false; - - create_aio_contexts(); - assert(threads <= NUM_CONTEXTS); - running = threads; - for (i = 0; i < threads; i++) { - Coroutine *co1 = qemu_coroutine_create(test_multi_fair_mutex_entry, NULL); - aio_co_schedule(ctx[i], co1); - } - - g_usleep(seconds * 1000000); - - qatomic_mb_set(&now_stopping, true); - while (running > 0) { - g_usleep(100000); - } - - join_aio_contexts(); - g_test_message("%d iterations/second", counter / seconds); - g_assert_cmpint(counter, ==, atomic_counter); -} - -static void test_multi_fair_mutex_1(void) -{ - test_multi_fair_mutex(NUM_CONTEXTS, 1); -} - -static void test_multi_fair_mutex_10(void) -{ - test_multi_fair_mutex(NUM_CONTEXTS, 10); -} -#endif - -/* Same test with pthread mutexes, for performance comparison and - * portability. */ - -static QemuMutex mutex; - -static void test_multi_mutex_entry(void *opaque) -{ - while (!qatomic_mb_read(&now_stopping)) { - qemu_mutex_lock(&mutex); - counter++; - qemu_mutex_unlock(&mutex); - qatomic_inc(&atomic_counter); - } - qatomic_dec(&running); -} - -static void test_multi_mutex(int threads, int seconds) -{ - int i; - - qemu_mutex_init(&mutex); - counter = 0; - atomic_counter = 0; - now_stopping = false; - - create_aio_contexts(); - assert(threads <= NUM_CONTEXTS); - running = threads; - for (i = 0; i < threads; i++) { - Coroutine *co1 = qemu_coroutine_create(test_multi_mutex_entry, NULL); - aio_co_schedule(ctx[i], co1); - } - - g_usleep(seconds * 1000000); - - qatomic_mb_set(&now_stopping, true); - while (running > 0) { - g_usleep(100000); - } - - join_aio_contexts(); - g_test_message("%d iterations/second", counter / seconds); - g_assert_cmpint(counter, ==, atomic_counter); -} - -static void test_multi_mutex_1(void) -{ - test_multi_mutex(NUM_CONTEXTS, 1); -} - -static void test_multi_mutex_10(void) -{ - test_multi_mutex(NUM_CONTEXTS, 10); -} - -/* End of tests. */ - -int main(int argc, char **argv) -{ - init_clocks(NULL); - - g_test_init(&argc, &argv, NULL); - g_test_add_func("/aio/multi/lifecycle", test_lifecycle); - if (g_test_quick()) { - g_test_add_func("/aio/multi/schedule", test_multi_co_schedule_1); - g_test_add_func("/aio/multi/mutex/contended", test_multi_co_mutex_1); - g_test_add_func("/aio/multi/mutex/handoff", test_multi_co_mutex_2_3); -#ifdef CONFIG_LINUX - g_test_add_func("/aio/multi/mutex/mcs", test_multi_fair_mutex_1); -#endif - g_test_add_func("/aio/multi/mutex/pthread", test_multi_mutex_1); - } else { - g_test_add_func("/aio/multi/schedule", test_multi_co_schedule_10); - g_test_add_func("/aio/multi/mutex/contended", test_multi_co_mutex_10); - g_test_add_func("/aio/multi/mutex/handoff", test_multi_co_mutex_2_30); -#ifdef CONFIG_LINUX - g_test_add_func("/aio/multi/mutex/mcs", test_multi_fair_mutex_10); -#endif - g_test_add_func("/aio/multi/mutex/pthread", test_multi_mutex_10); - } - return g_test_run(); -} diff --git a/tests/test-aio.c b/tests/test-aio.c deleted file mode 100644 index 8a46078463..0000000000 --- a/tests/test-aio.c +++ /dev/null @@ -1,921 +0,0 @@ -/* - * AioContext tests - * - * Copyright Red Hat, Inc. 2012 - * - * Authors: - * Paolo Bonzini - * - * This work is licensed under the terms of the GNU LGPL, version 2 or later. - * See the COPYING.LIB file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "block/aio.h" -#include "qapi/error.h" -#include "qemu/timer.h" -#include "qemu/sockets.h" -#include "qemu/error-report.h" -#include "qemu/coroutine.h" -#include "qemu/main-loop.h" - -static AioContext *ctx; - -typedef struct { - EventNotifier e; - int n; - int active; - bool auto_set; -} EventNotifierTestData; - -/* Wait until event notifier becomes inactive */ -static void wait_until_inactive(EventNotifierTestData *data) -{ - while (data->active > 0) { - aio_poll(ctx, true); - } -} - -/* Simple callbacks for testing. */ - -typedef struct { - QEMUBH *bh; - int n; - int max; -} BHTestData; - -typedef struct { - QEMUTimer timer; - QEMUClockType clock_type; - int n; - int max; - int64_t ns; - AioContext *ctx; -} TimerTestData; - -static void bh_test_cb(void *opaque) -{ - BHTestData *data = opaque; - if (++data->n < data->max) { - qemu_bh_schedule(data->bh); - } -} - -static void timer_test_cb(void *opaque) -{ - TimerTestData *data = opaque; - if (++data->n < data->max) { - timer_mod(&data->timer, - qemu_clock_get_ns(data->clock_type) + data->ns); - } -} - -static void dummy_io_handler_read(EventNotifier *e) -{ -} - -static void bh_delete_cb(void *opaque) -{ - BHTestData *data = opaque; - if (++data->n < data->max) { - qemu_bh_schedule(data->bh); - } else { - qemu_bh_delete(data->bh); - data->bh = NULL; - } -} - -static void event_ready_cb(EventNotifier *e) -{ - EventNotifierTestData *data = container_of(e, EventNotifierTestData, e); - g_assert(event_notifier_test_and_clear(e)); - data->n++; - if (data->active > 0) { - data->active--; - } - if (data->auto_set && data->active) { - event_notifier_set(e); - } -} - -/* Tests using aio_*. */ - -typedef struct { - QemuMutex start_lock; - EventNotifier notifier; - bool thread_acquired; -} AcquireTestData; - -static void *test_acquire_thread(void *opaque) -{ - AcquireTestData *data = opaque; - - /* Wait for other thread to let us start */ - qemu_mutex_lock(&data->start_lock); - qemu_mutex_unlock(&data->start_lock); - - /* event_notifier_set might be called either before or after - * the main thread's call to poll(). The test case's outcome - * should be the same in either case. - */ - event_notifier_set(&data->notifier); - aio_context_acquire(ctx); - aio_context_release(ctx); - - data->thread_acquired = true; /* success, we got here */ - - return NULL; -} - -static void set_event_notifier(AioContext *ctx, EventNotifier *notifier, - EventNotifierHandler *handler) -{ - aio_set_event_notifier(ctx, notifier, false, handler, NULL); -} - -static void dummy_notifier_read(EventNotifier *n) -{ - event_notifier_test_and_clear(n); -} - -static void test_acquire(void) -{ - QemuThread thread; - AcquireTestData data; - - /* Dummy event notifier ensures aio_poll() will block */ - event_notifier_init(&data.notifier, false); - set_event_notifier(ctx, &data.notifier, dummy_notifier_read); - g_assert(!aio_poll(ctx, false)); /* consume aio_notify() */ - - qemu_mutex_init(&data.start_lock); - qemu_mutex_lock(&data.start_lock); - data.thread_acquired = false; - - qemu_thread_create(&thread, "test_acquire_thread", - test_acquire_thread, - &data, QEMU_THREAD_JOINABLE); - - /* Block in aio_poll(), let other thread kick us and acquire context */ - aio_context_acquire(ctx); - qemu_mutex_unlock(&data.start_lock); /* let the thread run */ - g_assert(aio_poll(ctx, true)); - g_assert(!data.thread_acquired); - aio_context_release(ctx); - - qemu_thread_join(&thread); - set_event_notifier(ctx, &data.notifier, NULL); - event_notifier_cleanup(&data.notifier); - - g_assert(data.thread_acquired); -} - -static void test_bh_schedule(void) -{ - BHTestData data = { .n = 0 }; - data.bh = aio_bh_new(ctx, bh_test_cb, &data); - - qemu_bh_schedule(data.bh); - g_assert_cmpint(data.n, ==, 0); - - g_assert(aio_poll(ctx, true)); - g_assert_cmpint(data.n, ==, 1); - - g_assert(!aio_poll(ctx, false)); - g_assert_cmpint(data.n, ==, 1); - qemu_bh_delete(data.bh); -} - -static void test_bh_schedule10(void) -{ - BHTestData data = { .n = 0, .max = 10 }; - data.bh = aio_bh_new(ctx, bh_test_cb, &data); - - qemu_bh_schedule(data.bh); - g_assert_cmpint(data.n, ==, 0); - - g_assert(aio_poll(ctx, false)); - g_assert_cmpint(data.n, ==, 1); - - g_assert(aio_poll(ctx, true)); - g_assert_cmpint(data.n, ==, 2); - - while (data.n < 10) { - aio_poll(ctx, true); - } - g_assert_cmpint(data.n, ==, 10); - - g_assert(!aio_poll(ctx, false)); - g_assert_cmpint(data.n, ==, 10); - qemu_bh_delete(data.bh); -} - -static void test_bh_cancel(void) -{ - BHTestData data = { .n = 0 }; - data.bh = aio_bh_new(ctx, bh_test_cb, &data); - - qemu_bh_schedule(data.bh); - g_assert_cmpint(data.n, ==, 0); - - qemu_bh_cancel(data.bh); - g_assert_cmpint(data.n, ==, 0); - - g_assert(!aio_poll(ctx, false)); - g_assert_cmpint(data.n, ==, 0); - qemu_bh_delete(data.bh); -} - -static void test_bh_delete(void) -{ - BHTestData data = { .n = 0 }; - data.bh = aio_bh_new(ctx, bh_test_cb, &data); - - qemu_bh_schedule(data.bh); - g_assert_cmpint(data.n, ==, 0); - - qemu_bh_delete(data.bh); - g_assert_cmpint(data.n, ==, 0); - - g_assert(!aio_poll(ctx, false)); - g_assert_cmpint(data.n, ==, 0); -} - -static void test_bh_delete_from_cb(void) -{ - BHTestData data1 = { .n = 0, .max = 1 }; - - data1.bh = aio_bh_new(ctx, bh_delete_cb, &data1); - - qemu_bh_schedule(data1.bh); - g_assert_cmpint(data1.n, ==, 0); - - while (data1.n < data1.max) { - aio_poll(ctx, true); - } - g_assert_cmpint(data1.n, ==, data1.max); - g_assert(data1.bh == NULL); - - g_assert(!aio_poll(ctx, false)); -} - -static void test_bh_delete_from_cb_many(void) -{ - BHTestData data1 = { .n = 0, .max = 1 }; - BHTestData data2 = { .n = 0, .max = 3 }; - BHTestData data3 = { .n = 0, .max = 2 }; - BHTestData data4 = { .n = 0, .max = 4 }; - - data1.bh = aio_bh_new(ctx, bh_delete_cb, &data1); - data2.bh = aio_bh_new(ctx, bh_delete_cb, &data2); - data3.bh = aio_bh_new(ctx, bh_delete_cb, &data3); - data4.bh = aio_bh_new(ctx, bh_delete_cb, &data4); - - qemu_bh_schedule(data1.bh); - qemu_bh_schedule(data2.bh); - qemu_bh_schedule(data3.bh); - qemu_bh_schedule(data4.bh); - g_assert_cmpint(data1.n, ==, 0); - g_assert_cmpint(data2.n, ==, 0); - g_assert_cmpint(data3.n, ==, 0); - g_assert_cmpint(data4.n, ==, 0); - - g_assert(aio_poll(ctx, false)); - g_assert_cmpint(data1.n, ==, 1); - g_assert_cmpint(data2.n, ==, 1); - g_assert_cmpint(data3.n, ==, 1); - g_assert_cmpint(data4.n, ==, 1); - g_assert(data1.bh == NULL); - - while (data1.n < data1.max || - data2.n < data2.max || - data3.n < data3.max || - data4.n < data4.max) { - aio_poll(ctx, true); - } - g_assert_cmpint(data1.n, ==, data1.max); - g_assert_cmpint(data2.n, ==, data2.max); - g_assert_cmpint(data3.n, ==, data3.max); - g_assert_cmpint(data4.n, ==, data4.max); - g_assert(data1.bh == NULL); - g_assert(data2.bh == NULL); - g_assert(data3.bh == NULL); - g_assert(data4.bh == NULL); -} - -static void test_bh_flush(void) -{ - BHTestData data = { .n = 0 }; - data.bh = aio_bh_new(ctx, bh_test_cb, &data); - - qemu_bh_schedule(data.bh); - g_assert_cmpint(data.n, ==, 0); - - g_assert(aio_poll(ctx, true)); - g_assert_cmpint(data.n, ==, 1); - - g_assert(!aio_poll(ctx, false)); - g_assert_cmpint(data.n, ==, 1); - qemu_bh_delete(data.bh); -} - -static void test_set_event_notifier(void) -{ - EventNotifierTestData data = { .n = 0, .active = 0 }; - event_notifier_init(&data.e, false); - set_event_notifier(ctx, &data.e, event_ready_cb); - g_assert(!aio_poll(ctx, false)); - g_assert_cmpint(data.n, ==, 0); - - set_event_notifier(ctx, &data.e, NULL); - g_assert(!aio_poll(ctx, false)); - g_assert_cmpint(data.n, ==, 0); - event_notifier_cleanup(&data.e); -} - -static void test_wait_event_notifier(void) -{ - EventNotifierTestData data = { .n = 0, .active = 1 }; - event_notifier_init(&data.e, false); - set_event_notifier(ctx, &data.e, event_ready_cb); - while (aio_poll(ctx, false)); - g_assert_cmpint(data.n, ==, 0); - g_assert_cmpint(data.active, ==, 1); - - event_notifier_set(&data.e); - g_assert(aio_poll(ctx, false)); - g_assert_cmpint(data.n, ==, 1); - g_assert_cmpint(data.active, ==, 0); - - g_assert(!aio_poll(ctx, false)); - g_assert_cmpint(data.n, ==, 1); - g_assert_cmpint(data.active, ==, 0); - - set_event_notifier(ctx, &data.e, NULL); - g_assert(!aio_poll(ctx, false)); - g_assert_cmpint(data.n, ==, 1); - - event_notifier_cleanup(&data.e); -} - -static void test_flush_event_notifier(void) -{ - EventNotifierTestData data = { .n = 0, .active = 10, .auto_set = true }; - event_notifier_init(&data.e, false); - set_event_notifier(ctx, &data.e, event_ready_cb); - while (aio_poll(ctx, false)); - g_assert_cmpint(data.n, ==, 0); - g_assert_cmpint(data.active, ==, 10); - - event_notifier_set(&data.e); - g_assert(aio_poll(ctx, false)); - g_assert_cmpint(data.n, ==, 1); - g_assert_cmpint(data.active, ==, 9); - g_assert(aio_poll(ctx, false)); - - wait_until_inactive(&data); - g_assert_cmpint(data.n, ==, 10); - g_assert_cmpint(data.active, ==, 0); - g_assert(!aio_poll(ctx, false)); - - set_event_notifier(ctx, &data.e, NULL); - g_assert(!aio_poll(ctx, false)); - event_notifier_cleanup(&data.e); -} - -static void test_aio_external_client(void) -{ - int i, j; - - for (i = 1; i < 3; i++) { - EventNotifierTestData data = { .n = 0, .active = 10, .auto_set = true }; - event_notifier_init(&data.e, false); - aio_set_event_notifier(ctx, &data.e, true, event_ready_cb, NULL); - event_notifier_set(&data.e); - for (j = 0; j < i; j++) { - aio_disable_external(ctx); - } - for (j = 0; j < i; j++) { - assert(!aio_poll(ctx, false)); - assert(event_notifier_test_and_clear(&data.e)); - event_notifier_set(&data.e); - aio_enable_external(ctx); - } - assert(aio_poll(ctx, false)); - set_event_notifier(ctx, &data.e, NULL); - event_notifier_cleanup(&data.e); - } -} - -static void test_wait_event_notifier_noflush(void) -{ - EventNotifierTestData data = { .n = 0 }; - EventNotifierTestData dummy = { .n = 0, .active = 1 }; - - event_notifier_init(&data.e, false); - set_event_notifier(ctx, &data.e, event_ready_cb); - - g_assert(!aio_poll(ctx, false)); - g_assert_cmpint(data.n, ==, 0); - - /* Until there is an active descriptor, aio_poll may or may not call - * event_ready_cb. Still, it must not block. */ - event_notifier_set(&data.e); - g_assert(aio_poll(ctx, true)); - data.n = 0; - - /* An active event notifier forces aio_poll to look at EventNotifiers. */ - event_notifier_init(&dummy.e, false); - set_event_notifier(ctx, &dummy.e, event_ready_cb); - - event_notifier_set(&data.e); - g_assert(aio_poll(ctx, false)); - g_assert_cmpint(data.n, ==, 1); - g_assert(!aio_poll(ctx, false)); - g_assert_cmpint(data.n, ==, 1); - - event_notifier_set(&data.e); - g_assert(aio_poll(ctx, false)); - g_assert_cmpint(data.n, ==, 2); - g_assert(!aio_poll(ctx, false)); - g_assert_cmpint(data.n, ==, 2); - - event_notifier_set(&dummy.e); - wait_until_inactive(&dummy); - g_assert_cmpint(data.n, ==, 2); - g_assert_cmpint(dummy.n, ==, 1); - g_assert_cmpint(dummy.active, ==, 0); - - set_event_notifier(ctx, &dummy.e, NULL); - event_notifier_cleanup(&dummy.e); - - set_event_notifier(ctx, &data.e, NULL); - g_assert(!aio_poll(ctx, false)); - g_assert_cmpint(data.n, ==, 2); - - event_notifier_cleanup(&data.e); -} - -static void test_timer_schedule(void) -{ - TimerTestData data = { .n = 0, .ctx = ctx, .ns = SCALE_MS * 750LL, - .max = 2, - .clock_type = QEMU_CLOCK_REALTIME }; - EventNotifier e; - - /* aio_poll will not block to wait for timers to complete unless it has - * an fd to wait on. Fixing this breaks other tests. So create a dummy one. - */ - event_notifier_init(&e, false); - set_event_notifier(ctx, &e, dummy_io_handler_read); - aio_poll(ctx, false); - - aio_timer_init(ctx, &data.timer, data.clock_type, - SCALE_NS, timer_test_cb, &data); - timer_mod(&data.timer, - qemu_clock_get_ns(data.clock_type) + - data.ns); - - g_assert_cmpint(data.n, ==, 0); - - /* timer_mod may well cause an event notifer to have gone off, - * so clear that - */ - do {} while (aio_poll(ctx, false)); - - g_assert(!aio_poll(ctx, false)); - g_assert_cmpint(data.n, ==, 0); - - g_usleep(1 * G_USEC_PER_SEC); - g_assert_cmpint(data.n, ==, 0); - - g_assert(aio_poll(ctx, false)); - g_assert_cmpint(data.n, ==, 1); - - /* timer_mod called by our callback */ - do {} while (aio_poll(ctx, false)); - - g_assert(!aio_poll(ctx, false)); - g_assert_cmpint(data.n, ==, 1); - - g_assert(aio_poll(ctx, true)); - g_assert_cmpint(data.n, ==, 2); - - /* As max is now 2, an event notifier should not have gone off */ - - g_assert(!aio_poll(ctx, false)); - g_assert_cmpint(data.n, ==, 2); - - set_event_notifier(ctx, &e, NULL); - event_notifier_cleanup(&e); - - timer_del(&data.timer); -} - -/* Now the same tests, using the context as a GSource. They are - * very similar to the ones above, with g_main_context_iteration - * replacing aio_poll. However: - * - sometimes both the AioContext and the glib main loop wake - * themselves up. Hence, some "g_assert(!aio_poll(ctx, false));" - * are replaced by "while (g_main_context_iteration(NULL, false));". - * - there is no exact replacement for a blocking wait. - * "while (g_main_context_iteration(NULL, true)" seems to work, - * but it is not documented _why_ it works. For these tests a - * non-blocking loop like "while (g_main_context_iteration(NULL, false)" - * works well, and that's what I am using. - */ - -static void test_source_flush(void) -{ - g_assert(!g_main_context_iteration(NULL, false)); - aio_notify(ctx); - while (g_main_context_iteration(NULL, false)); - g_assert(!g_main_context_iteration(NULL, false)); -} - -static void test_source_bh_schedule(void) -{ - BHTestData data = { .n = 0 }; - data.bh = aio_bh_new(ctx, bh_test_cb, &data); - - qemu_bh_schedule(data.bh); - g_assert_cmpint(data.n, ==, 0); - - g_assert(g_main_context_iteration(NULL, true)); - g_assert_cmpint(data.n, ==, 1); - - g_assert(!g_main_context_iteration(NULL, false)); - g_assert_cmpint(data.n, ==, 1); - qemu_bh_delete(data.bh); -} - -static void test_source_bh_schedule10(void) -{ - BHTestData data = { .n = 0, .max = 10 }; - data.bh = aio_bh_new(ctx, bh_test_cb, &data); - - qemu_bh_schedule(data.bh); - g_assert_cmpint(data.n, ==, 0); - - g_assert(g_main_context_iteration(NULL, false)); - g_assert_cmpint(data.n, ==, 1); - - g_assert(g_main_context_iteration(NULL, true)); - g_assert_cmpint(data.n, ==, 2); - - while (g_main_context_iteration(NULL, false)); - g_assert_cmpint(data.n, ==, 10); - - g_assert(!g_main_context_iteration(NULL, false)); - g_assert_cmpint(data.n, ==, 10); - qemu_bh_delete(data.bh); -} - -static void test_source_bh_cancel(void) -{ - BHTestData data = { .n = 0 }; - data.bh = aio_bh_new(ctx, bh_test_cb, &data); - - qemu_bh_schedule(data.bh); - g_assert_cmpint(data.n, ==, 0); - - qemu_bh_cancel(data.bh); - g_assert_cmpint(data.n, ==, 0); - - while (g_main_context_iteration(NULL, false)); - g_assert_cmpint(data.n, ==, 0); - qemu_bh_delete(data.bh); -} - -static void test_source_bh_delete(void) -{ - BHTestData data = { .n = 0 }; - data.bh = aio_bh_new(ctx, bh_test_cb, &data); - - qemu_bh_schedule(data.bh); - g_assert_cmpint(data.n, ==, 0); - - qemu_bh_delete(data.bh); - g_assert_cmpint(data.n, ==, 0); - - while (g_main_context_iteration(NULL, false)); - g_assert_cmpint(data.n, ==, 0); -} - -static void test_source_bh_delete_from_cb(void) -{ - BHTestData data1 = { .n = 0, .max = 1 }; - - data1.bh = aio_bh_new(ctx, bh_delete_cb, &data1); - - qemu_bh_schedule(data1.bh); - g_assert_cmpint(data1.n, ==, 0); - - g_main_context_iteration(NULL, true); - g_assert_cmpint(data1.n, ==, data1.max); - g_assert(data1.bh == NULL); - - assert(g_main_context_iteration(NULL, false)); - assert(!g_main_context_iteration(NULL, false)); -} - -static void test_source_bh_delete_from_cb_many(void) -{ - BHTestData data1 = { .n = 0, .max = 1 }; - BHTestData data2 = { .n = 0, .max = 3 }; - BHTestData data3 = { .n = 0, .max = 2 }; - BHTestData data4 = { .n = 0, .max = 4 }; - - data1.bh = aio_bh_new(ctx, bh_delete_cb, &data1); - data2.bh = aio_bh_new(ctx, bh_delete_cb, &data2); - data3.bh = aio_bh_new(ctx, bh_delete_cb, &data3); - data4.bh = aio_bh_new(ctx, bh_delete_cb, &data4); - - qemu_bh_schedule(data1.bh); - qemu_bh_schedule(data2.bh); - qemu_bh_schedule(data3.bh); - qemu_bh_schedule(data4.bh); - g_assert_cmpint(data1.n, ==, 0); - g_assert_cmpint(data2.n, ==, 0); - g_assert_cmpint(data3.n, ==, 0); - g_assert_cmpint(data4.n, ==, 0); - - g_assert(g_main_context_iteration(NULL, false)); - g_assert_cmpint(data1.n, ==, 1); - g_assert_cmpint(data2.n, ==, 1); - g_assert_cmpint(data3.n, ==, 1); - g_assert_cmpint(data4.n, ==, 1); - g_assert(data1.bh == NULL); - - while (g_main_context_iteration(NULL, false)); - g_assert_cmpint(data1.n, ==, data1.max); - g_assert_cmpint(data2.n, ==, data2.max); - g_assert_cmpint(data3.n, ==, data3.max); - g_assert_cmpint(data4.n, ==, data4.max); - g_assert(data1.bh == NULL); - g_assert(data2.bh == NULL); - g_assert(data3.bh == NULL); - g_assert(data4.bh == NULL); -} - -static void test_source_bh_flush(void) -{ - BHTestData data = { .n = 0 }; - data.bh = aio_bh_new(ctx, bh_test_cb, &data); - - qemu_bh_schedule(data.bh); - g_assert_cmpint(data.n, ==, 0); - - g_assert(g_main_context_iteration(NULL, true)); - g_assert_cmpint(data.n, ==, 1); - - g_assert(!g_main_context_iteration(NULL, false)); - g_assert_cmpint(data.n, ==, 1); - qemu_bh_delete(data.bh); -} - -static void test_source_set_event_notifier(void) -{ - EventNotifierTestData data = { .n = 0, .active = 0 }; - event_notifier_init(&data.e, false); - set_event_notifier(ctx, &data.e, event_ready_cb); - while (g_main_context_iteration(NULL, false)); - g_assert_cmpint(data.n, ==, 0); - - set_event_notifier(ctx, &data.e, NULL); - while (g_main_context_iteration(NULL, false)); - g_assert_cmpint(data.n, ==, 0); - event_notifier_cleanup(&data.e); -} - -static void test_source_wait_event_notifier(void) -{ - EventNotifierTestData data = { .n = 0, .active = 1 }; - event_notifier_init(&data.e, false); - set_event_notifier(ctx, &data.e, event_ready_cb); - while (g_main_context_iteration(NULL, false)); - g_assert_cmpint(data.n, ==, 0); - g_assert_cmpint(data.active, ==, 1); - - event_notifier_set(&data.e); - g_assert(g_main_context_iteration(NULL, false)); - g_assert_cmpint(data.n, ==, 1); - g_assert_cmpint(data.active, ==, 0); - - while (g_main_context_iteration(NULL, false)); - g_assert_cmpint(data.n, ==, 1); - g_assert_cmpint(data.active, ==, 0); - - set_event_notifier(ctx, &data.e, NULL); - while (g_main_context_iteration(NULL, false)); - g_assert_cmpint(data.n, ==, 1); - - event_notifier_cleanup(&data.e); -} - -static void test_source_flush_event_notifier(void) -{ - EventNotifierTestData data = { .n = 0, .active = 10, .auto_set = true }; - event_notifier_init(&data.e, false); - set_event_notifier(ctx, &data.e, event_ready_cb); - while (g_main_context_iteration(NULL, false)); - g_assert_cmpint(data.n, ==, 0); - g_assert_cmpint(data.active, ==, 10); - - event_notifier_set(&data.e); - g_assert(g_main_context_iteration(NULL, false)); - g_assert_cmpint(data.n, ==, 1); - g_assert_cmpint(data.active, ==, 9); - g_assert(g_main_context_iteration(NULL, false)); - - while (g_main_context_iteration(NULL, false)); - g_assert_cmpint(data.n, ==, 10); - g_assert_cmpint(data.active, ==, 0); - g_assert(!g_main_context_iteration(NULL, false)); - - set_event_notifier(ctx, &data.e, NULL); - while (g_main_context_iteration(NULL, false)); - event_notifier_cleanup(&data.e); -} - -static void test_source_wait_event_notifier_noflush(void) -{ - EventNotifierTestData data = { .n = 0 }; - EventNotifierTestData dummy = { .n = 0, .active = 1 }; - - event_notifier_init(&data.e, false); - set_event_notifier(ctx, &data.e, event_ready_cb); - - while (g_main_context_iteration(NULL, false)); - g_assert_cmpint(data.n, ==, 0); - - /* Until there is an active descriptor, glib may or may not call - * event_ready_cb. Still, it must not block. */ - event_notifier_set(&data.e); - g_main_context_iteration(NULL, true); - data.n = 0; - - /* An active event notifier forces aio_poll to look at EventNotifiers. */ - event_notifier_init(&dummy.e, false); - set_event_notifier(ctx, &dummy.e, event_ready_cb); - - event_notifier_set(&data.e); - g_assert(g_main_context_iteration(NULL, false)); - g_assert_cmpint(data.n, ==, 1); - g_assert(!g_main_context_iteration(NULL, false)); - g_assert_cmpint(data.n, ==, 1); - - event_notifier_set(&data.e); - g_assert(g_main_context_iteration(NULL, false)); - g_assert_cmpint(data.n, ==, 2); - g_assert(!g_main_context_iteration(NULL, false)); - g_assert_cmpint(data.n, ==, 2); - - event_notifier_set(&dummy.e); - while (g_main_context_iteration(NULL, false)); - g_assert_cmpint(data.n, ==, 2); - g_assert_cmpint(dummy.n, ==, 1); - g_assert_cmpint(dummy.active, ==, 0); - - set_event_notifier(ctx, &dummy.e, NULL); - event_notifier_cleanup(&dummy.e); - - set_event_notifier(ctx, &data.e, NULL); - while (g_main_context_iteration(NULL, false)); - g_assert_cmpint(data.n, ==, 2); - - event_notifier_cleanup(&data.e); -} - -static void test_source_timer_schedule(void) -{ - TimerTestData data = { .n = 0, .ctx = ctx, .ns = SCALE_MS * 750LL, - .max = 2, - .clock_type = QEMU_CLOCK_REALTIME }; - EventNotifier e; - int64_t expiry; - - /* aio_poll will not block to wait for timers to complete unless it has - * an fd to wait on. Fixing this breaks other tests. So create a dummy one. - */ - event_notifier_init(&e, false); - set_event_notifier(ctx, &e, dummy_io_handler_read); - do {} while (g_main_context_iteration(NULL, false)); - - aio_timer_init(ctx, &data.timer, data.clock_type, - SCALE_NS, timer_test_cb, &data); - expiry = qemu_clock_get_ns(data.clock_type) + - data.ns; - timer_mod(&data.timer, expiry); - - g_assert_cmpint(data.n, ==, 0); - - g_usleep(1 * G_USEC_PER_SEC); - g_assert_cmpint(data.n, ==, 0); - - g_assert(g_main_context_iteration(NULL, true)); - g_assert_cmpint(data.n, ==, 1); - expiry += data.ns; - - while (data.n < 2) { - g_main_context_iteration(NULL, true); - } - - g_assert_cmpint(data.n, ==, 2); - g_assert(qemu_clock_get_ns(data.clock_type) > expiry); - - set_event_notifier(ctx, &e, NULL); - event_notifier_cleanup(&e); - - timer_del(&data.timer); -} - -/* - * Check that aio_co_enter() can chain many times - * - * Two coroutines should be able to invoke each other via aio_co_enter() many - * times without hitting a limit like stack exhaustion. In other words, the - * calls should be chained instead of nested. - */ - -typedef struct { - Coroutine *other; - unsigned i; - unsigned max; -} ChainData; - -static void coroutine_fn chain(void *opaque) -{ - ChainData *data = opaque; - - for (data->i = 0; data->i < data->max; data->i++) { - /* Queue up the other coroutine... */ - aio_co_enter(ctx, data->other); - - /* ...and give control to it */ - qemu_coroutine_yield(); - } -} - -static void test_queue_chaining(void) -{ - /* This number of iterations hit stack exhaustion in the past: */ - ChainData data_a = { .max = 25000 }; - ChainData data_b = { .max = 25000 }; - - data_b.other = qemu_coroutine_create(chain, &data_a); - data_a.other = qemu_coroutine_create(chain, &data_b); - - qemu_coroutine_enter(data_b.other); - - g_assert_cmpint(data_a.i, ==, data_a.max); - g_assert_cmpint(data_b.i, ==, data_b.max - 1); - - /* Allow the second coroutine to terminate */ - qemu_coroutine_enter(data_a.other); - - g_assert_cmpint(data_b.i, ==, data_b.max); -} - -/* End of tests. */ - -int main(int argc, char **argv) -{ - qemu_init_main_loop(&error_fatal); - ctx = qemu_get_aio_context(); - - while (g_main_context_iteration(NULL, false)); - - g_test_init(&argc, &argv, NULL); - g_test_add_func("/aio/acquire", test_acquire); - g_test_add_func("/aio/bh/schedule", test_bh_schedule); - g_test_add_func("/aio/bh/schedule10", test_bh_schedule10); - g_test_add_func("/aio/bh/cancel", test_bh_cancel); - g_test_add_func("/aio/bh/delete", test_bh_delete); - g_test_add_func("/aio/bh/callback-delete/one", test_bh_delete_from_cb); - g_test_add_func("/aio/bh/callback-delete/many", test_bh_delete_from_cb_many); - g_test_add_func("/aio/bh/flush", test_bh_flush); - g_test_add_func("/aio/event/add-remove", test_set_event_notifier); - g_test_add_func("/aio/event/wait", test_wait_event_notifier); - g_test_add_func("/aio/event/wait/no-flush-cb", test_wait_event_notifier_noflush); - g_test_add_func("/aio/event/flush", test_flush_event_notifier); - g_test_add_func("/aio/external-client", test_aio_external_client); - g_test_add_func("/aio/timer/schedule", test_timer_schedule); - - g_test_add_func("/aio/coroutine/queue-chaining", test_queue_chaining); - - g_test_add_func("/aio-gsource/flush", test_source_flush); - g_test_add_func("/aio-gsource/bh/schedule", test_source_bh_schedule); - g_test_add_func("/aio-gsource/bh/schedule10", test_source_bh_schedule10); - g_test_add_func("/aio-gsource/bh/cancel", test_source_bh_cancel); - g_test_add_func("/aio-gsource/bh/delete", test_source_bh_delete); - g_test_add_func("/aio-gsource/bh/callback-delete/one", test_source_bh_delete_from_cb); - g_test_add_func("/aio-gsource/bh/callback-delete/many", test_source_bh_delete_from_cb_many); - g_test_add_func("/aio-gsource/bh/flush", test_source_bh_flush); - g_test_add_func("/aio-gsource/event/add-remove", test_source_set_event_notifier); - g_test_add_func("/aio-gsource/event/wait", test_source_wait_event_notifier); - g_test_add_func("/aio-gsource/event/wait/no-flush-cb", test_source_wait_event_notifier_noflush); - g_test_add_func("/aio-gsource/event/flush", test_source_flush_event_notifier); - g_test_add_func("/aio-gsource/timer/schedule", test_source_timer_schedule); - return g_test_run(); -} diff --git a/tests/test-authz-list.c b/tests/test-authz-list.c deleted file mode 100644 index 5351992a01..0000000000 --- a/tests/test-authz-list.c +++ /dev/null @@ -1,160 +0,0 @@ -/* - * QEMU list file authorization object tests - * - * Copyright (c) 2018 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - * - */ - -#include "qemu/osdep.h" - -#include "authz/list.h" -#include "qemu/module.h" - -static void test_authz_default_deny(void) -{ - QAuthZList *auth = qauthz_list_new("auth0", - QAUTHZ_LIST_POLICY_DENY, - &error_abort); - - g_assert(!qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort)); - - object_unparent(OBJECT(auth)); -} - -static void test_authz_default_allow(void) -{ - QAuthZList *auth = qauthz_list_new("auth0", - QAUTHZ_LIST_POLICY_ALLOW, - &error_abort); - - g_assert(qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort)); - - object_unparent(OBJECT(auth)); -} - -static void test_authz_explicit_deny(void) -{ - QAuthZList *auth = qauthz_list_new("auth0", - QAUTHZ_LIST_POLICY_ALLOW, - &error_abort); - - qauthz_list_append_rule(auth, "fred", QAUTHZ_LIST_POLICY_DENY, - QAUTHZ_LIST_FORMAT_EXACT, &error_abort); - - g_assert(!qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort)); - - object_unparent(OBJECT(auth)); -} - -static void test_authz_explicit_allow(void) -{ - QAuthZList *auth = qauthz_list_new("auth0", - QAUTHZ_LIST_POLICY_DENY, - &error_abort); - - qauthz_list_append_rule(auth, "fred", QAUTHZ_LIST_POLICY_ALLOW, - QAUTHZ_LIST_FORMAT_EXACT, &error_abort); - - g_assert(qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort)); - - object_unparent(OBJECT(auth)); -} - - -static void test_authz_complex(void) -{ - QAuthZList *auth = qauthz_list_new("auth0", - QAUTHZ_LIST_POLICY_DENY, - &error_abort); - - qauthz_list_append_rule(auth, "fred", QAUTHZ_LIST_POLICY_ALLOW, - QAUTHZ_LIST_FORMAT_EXACT, &error_abort); - qauthz_list_append_rule(auth, "bob", QAUTHZ_LIST_POLICY_ALLOW, - QAUTHZ_LIST_FORMAT_EXACT, &error_abort); - qauthz_list_append_rule(auth, "dan", QAUTHZ_LIST_POLICY_DENY, - QAUTHZ_LIST_FORMAT_EXACT, &error_abort); - qauthz_list_append_rule(auth, "dan*", QAUTHZ_LIST_POLICY_ALLOW, - QAUTHZ_LIST_FORMAT_GLOB, &error_abort); - - g_assert(qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort)); - g_assert(qauthz_is_allowed(QAUTHZ(auth), "bob", &error_abort)); - g_assert(!qauthz_is_allowed(QAUTHZ(auth), "dan", &error_abort)); - g_assert(qauthz_is_allowed(QAUTHZ(auth), "danb", &error_abort)); - - object_unparent(OBJECT(auth)); -} - -static void test_authz_add_remove(void) -{ - QAuthZList *auth = qauthz_list_new("auth0", - QAUTHZ_LIST_POLICY_ALLOW, - &error_abort); - - g_assert_cmpint(qauthz_list_append_rule(auth, "fred", - QAUTHZ_LIST_POLICY_ALLOW, - QAUTHZ_LIST_FORMAT_EXACT, - &error_abort), - ==, 0); - g_assert_cmpint(qauthz_list_append_rule(auth, "bob", - QAUTHZ_LIST_POLICY_ALLOW, - QAUTHZ_LIST_FORMAT_EXACT, - &error_abort), - ==, 1); - g_assert_cmpint(qauthz_list_append_rule(auth, "dan", - QAUTHZ_LIST_POLICY_DENY, - QAUTHZ_LIST_FORMAT_EXACT, - &error_abort), - ==, 2); - g_assert_cmpint(qauthz_list_append_rule(auth, "frank", - QAUTHZ_LIST_POLICY_DENY, - QAUTHZ_LIST_FORMAT_EXACT, - &error_abort), - ==, 3); - - g_assert(!qauthz_is_allowed(QAUTHZ(auth), "dan", &error_abort)); - - g_assert_cmpint(qauthz_list_delete_rule(auth, "dan"), - ==, 2); - - g_assert(qauthz_is_allowed(QAUTHZ(auth), "dan", &error_abort)); - - g_assert_cmpint(qauthz_list_insert_rule(auth, "dan", - QAUTHZ_LIST_POLICY_DENY, - QAUTHZ_LIST_FORMAT_EXACT, - 2, - &error_abort), - ==, 2); - - g_assert(!qauthz_is_allowed(QAUTHZ(auth), "dan", &error_abort)); - - object_unparent(OBJECT(auth)); -} - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - - module_call_init(MODULE_INIT_QOM); - - g_test_add_func("/auth/list/default/deny", test_authz_default_deny); - g_test_add_func("/auth/list/default/allow", test_authz_default_allow); - g_test_add_func("/auth/list/explicit/deny", test_authz_explicit_deny); - g_test_add_func("/auth/list/explicit/allow", test_authz_explicit_allow); - g_test_add_func("/auth/list/complex", test_authz_complex); - g_test_add_func("/auth/list/add-remove", test_authz_add_remove); - - return g_test_run(); -} diff --git a/tests/test-authz-listfile.c b/tests/test-authz-listfile.c deleted file mode 100644 index 64d0e1500f..0000000000 --- a/tests/test-authz-listfile.c +++ /dev/null @@ -1,196 +0,0 @@ -/* - * QEMU list authorization object tests - * - * Copyright (c) 2018 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - * - */ - -#include "qemu/osdep.h" -#include "qemu/main-loop.h" -#include "qemu/module.h" -#include "authz/listfile.h" - -static char *workdir; - -static gchar *qemu_authz_listfile_test_save(const gchar *name, - const gchar *cfg) -{ - gchar *path = g_strdup_printf("%s/default-deny.cfg", workdir); - GError *gerr = NULL; - - if (!g_file_set_contents(path, cfg, -1, &gerr)) { - g_printerr("Unable to save config %s: %s\n", - path, gerr->message); - g_error_free(gerr); - g_free(path); - rmdir(workdir); - abort(); - } - - return path; -} - -static void test_authz_default_deny(void) -{ - gchar *file = qemu_authz_listfile_test_save( - "default-deny.cfg", - "{ \"policy\": \"deny\" }"); - Error *local_err = NULL; - - QAuthZListFile *auth = qauthz_list_file_new("auth0", - file, false, - &local_err); - unlink(file); - g_free(file); - g_assert(local_err == NULL); - g_assert(!qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort)); - - object_unparent(OBJECT(auth)); -} - -static void test_authz_default_allow(void) -{ - gchar *file = qemu_authz_listfile_test_save( - "default-allow.cfg", - "{ \"policy\": \"allow\" }"); - Error *local_err = NULL; - - QAuthZListFile *auth = qauthz_list_file_new("auth0", - file, false, - &local_err); - unlink(file); - g_free(file); - g_assert(local_err == NULL); - g_assert(qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort)); - - object_unparent(OBJECT(auth)); -} - -static void test_authz_explicit_deny(void) -{ - gchar *file = qemu_authz_listfile_test_save( - "explicit-deny.cfg", - "{ \"rules\": [ " - " { \"match\": \"fred\"," - " \"policy\": \"deny\"," - " \"format\": \"exact\" } ]," - " \"policy\": \"allow\" }"); - Error *local_err = NULL; - - QAuthZListFile *auth = qauthz_list_file_new("auth0", - file, false, - &local_err); - unlink(file); - g_free(file); - g_assert(local_err == NULL); - - g_assert(!qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort)); - - object_unparent(OBJECT(auth)); -} - -static void test_authz_explicit_allow(void) -{ - gchar *file = qemu_authz_listfile_test_save( - "explicit-allow.cfg", - "{ \"rules\": [ " - " { \"match\": \"fred\"," - " \"policy\": \"allow\"," - " \"format\": \"exact\" } ]," - " \"policy\": \"deny\" }"); - Error *local_err = NULL; - - QAuthZListFile *auth = qauthz_list_file_new("auth0", - file, false, - &local_err); - unlink(file); - g_free(file); - g_assert(local_err == NULL); - - g_assert(qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort)); - - object_unparent(OBJECT(auth)); -} - - -static void test_authz_complex(void) -{ - gchar *file = qemu_authz_listfile_test_save( - "complex.cfg", - "{ \"rules\": [ " - " { \"match\": \"fred\"," - " \"policy\": \"allow\"," - " \"format\": \"exact\" }," - " { \"match\": \"bob\"," - " \"policy\": \"allow\"," - " \"format\": \"exact\" }," - " { \"match\": \"dan\"," - " \"policy\": \"deny\"," - " \"format\": \"exact\" }," - " { \"match\": \"dan*\"," - " \"policy\": \"allow\"," - " \"format\": \"glob\" } ]," - " \"policy\": \"deny\" }"); - - Error *local_err = NULL; - - QAuthZListFile *auth = qauthz_list_file_new("auth0", - file, false, - &local_err); - unlink(file); - g_free(file); - g_assert(local_err == NULL); - - g_assert(qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort)); - g_assert(qauthz_is_allowed(QAUTHZ(auth), "bob", &error_abort)); - g_assert(!qauthz_is_allowed(QAUTHZ(auth), "dan", &error_abort)); - g_assert(qauthz_is_allowed(QAUTHZ(auth), "danb", &error_abort)); - - object_unparent(OBJECT(auth)); -} - - -int main(int argc, char **argv) -{ - int ret; - GError *gerr = NULL; - - g_test_init(&argc, &argv, NULL); - - module_call_init(MODULE_INIT_QOM); - - workdir = g_dir_make_tmp("qemu-test-authz-listfile-XXXXXX", - &gerr); - if (!workdir) { - g_printerr("Unable to create temporary dir: %s\n", - gerr->message); - g_error_free(gerr); - abort(); - } - - g_test_add_func("/auth/list/default/deny", test_authz_default_deny); - g_test_add_func("/auth/list/default/allow", test_authz_default_allow); - g_test_add_func("/auth/list/explicit/deny", test_authz_explicit_deny); - g_test_add_func("/auth/list/explicit/allow", test_authz_explicit_allow); - g_test_add_func("/auth/list/complex", test_authz_complex); - - ret = g_test_run(); - - rmdir(workdir); - g_free(workdir); - - return ret; -} diff --git a/tests/test-authz-pam.c b/tests/test-authz-pam.c deleted file mode 100644 index 4fe1ef2603..0000000000 --- a/tests/test-authz-pam.c +++ /dev/null @@ -1,133 +0,0 @@ -/* - * QEMU PAM authorization object tests - * - * Copyright (c) 2018 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - * - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "qemu/module.h" -#include "authz/pamacct.h" - -#include - -static bool failauth; - -/* - * These three functions are exported by libpam.so. - * - * By defining them again here, our impls are resolved - * by the linker instead of those in libpam.so - * - * The test suite is thus isolated from the host system - * PAM setup, so we can do predictable test scenarios - */ -int -pam_start(const char *service_name, const char *user, - const struct pam_conv *pam_conversation, - pam_handle_t **pamh) -{ - failauth = true; - if (!g_str_equal(service_name, "qemu-vnc")) { - return PAM_AUTH_ERR; - } - - if (g_str_equal(user, "fred")) { - failauth = false; - } - - *pamh = (pam_handle_t *)0xbadeaffe; - return PAM_SUCCESS; -} - - -int -pam_acct_mgmt(pam_handle_t *pamh, int flags) -{ - if (failauth) { - return PAM_AUTH_ERR; - } - - return PAM_SUCCESS; -} - - -int -pam_end(pam_handle_t *pamh, int status) -{ - return PAM_SUCCESS; -} - - -static void test_authz_unknown_service(void) -{ - Error *local_err = NULL; - QAuthZPAM *auth = qauthz_pam_new("auth0", - "qemu-does-not-exist", - &error_abort); - - g_assert_nonnull(auth); - - g_assert_false(qauthz_is_allowed(QAUTHZ(auth), "fred", &local_err)); - - error_free_or_abort(&local_err); - object_unparent(OBJECT(auth)); -} - - -static void test_authz_good_user(void) -{ - QAuthZPAM *auth = qauthz_pam_new("auth0", - "qemu-vnc", - &error_abort); - - g_assert_nonnull(auth); - - g_assert_true(qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort)); - - object_unparent(OBJECT(auth)); -} - - -static void test_authz_bad_user(void) -{ - Error *local_err = NULL; - QAuthZPAM *auth = qauthz_pam_new("auth0", - "qemu-vnc", - &error_abort); - - g_assert_nonnull(auth); - - g_assert_false(qauthz_is_allowed(QAUTHZ(auth), "bob", &local_err)); - - error_free_or_abort(&local_err); - object_unparent(OBJECT(auth)); -} - - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - - module_call_init(MODULE_INIT_QOM); - - g_test_add_func("/auth/pam/unknown-service", test_authz_unknown_service); - g_test_add_func("/auth/pam/good-user", test_authz_good_user); - g_test_add_func("/auth/pam/bad-user", test_authz_bad_user); - - return g_test_run(); -} diff --git a/tests/test-authz-simple.c b/tests/test-authz-simple.c deleted file mode 100644 index 6f9034d8ff..0000000000 --- a/tests/test-authz-simple.c +++ /dev/null @@ -1,51 +0,0 @@ -/* - * QEMU simple authorization object testing - * - * Copyright (c) 2018 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - * - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "qemu/module.h" - -#include "authz/simple.h" - - -static void test_authz_simple(void) -{ - QAuthZSimple *authz = qauthz_simple_new("authz0", - "cthulu", - &error_abort); - - g_assert(!qauthz_is_allowed(QAUTHZ(authz), "cthul", &error_abort)); - g_assert(qauthz_is_allowed(QAUTHZ(authz), "cthulu", &error_abort)); - g_assert(!qauthz_is_allowed(QAUTHZ(authz), "cthuluu", &error_abort)); - g_assert(!qauthz_is_allowed(QAUTHZ(authz), "fred", &error_abort)); - - object_unparent(OBJECT(authz)); -} - - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - module_call_init(MODULE_INIT_QOM); - - g_test_add_func("/authz/simple", test_authz_simple); - - return g_test_run(); -} diff --git a/tests/test-base64.c b/tests/test-base64.c deleted file mode 100644 index 3012d7be26..0000000000 --- a/tests/test-base64.c +++ /dev/null @@ -1,108 +0,0 @@ -/* - * QEMU base64 helper test - * - * Copyright (c) 2015 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - * - */ - -#include "qemu/osdep.h" - -#include "qapi/error.h" -#include "qemu/base64.h" - -static void test_base64_good(void) -{ - const char input[] = - "QmVjYXVzZSB3ZSBmb2N1c2VkIG9uIHRoZSBzbmFrZSwgd2UgbW\n" - "lzc2VkIHRoZSBzY29ycGlvbi4="; - const char expect[] = "Because we focused on the snake, " - "we missed the scorpion."; - - size_t len; - uint8_t *actual = qbase64_decode(input, - -1, - &len, - &error_abort); - - g_assert(actual != NULL); - g_assert_cmpint(len, ==, strlen(expect)); - g_assert_cmpstr((char *)actual, ==, expect); - g_free(actual); -} - - -static void test_base64_bad(const char *input, - size_t input_len) -{ - size_t len; - Error *err = NULL; - uint8_t *actual = qbase64_decode(input, - input_len, - &len, - &err); - - error_free_or_abort(&err); - g_assert(actual == NULL); - g_assert_cmpint(len, ==, 0); -} - - -static void test_base64_embedded_nul(void) -{ - /* We put a NUL character in the middle of the base64 - * text which is invalid data, given the expected length */ - const char input[] = - "QmVjYXVzZSB3ZSBmb2N1c2VkIG9uIHRoZSBzbmFrZSwgd2UgbW\0" - "lzc2VkIHRoZSBzY29ycGlvbi4="; - - test_base64_bad(input, G_N_ELEMENTS(input) - 1); -} - - -static void test_base64_not_nul_terminated(void) -{ - const char input[] = - "QmVjYXVzZSB3ZSBmb2N1c2VkIG9uIHRoZSBzbmFrZSwgd2UgbW\n" - "lzc2VkIHRoZSBzY29ycGlvbi4="; - - /* Using '-2' to make us drop the trailing NUL, thus - * creating an invalid base64 sequence for decoding */ - test_base64_bad(input, G_N_ELEMENTS(input) - 2); -} - - -static void test_base64_invalid_chars(void) -{ - /* We put a single quote character in the middle - * of the base64 text which is invalid data */ - const char input[] = - "QmVjYXVzZSB3ZSBmb2N1c2VkIG9uIHRoZSBzbmFrZSwgd2UgbW'" - "lzc2VkIHRoZSBzY29ycGlvbi4="; - - test_base64_bad(input, strlen(input)); -} - - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - g_test_add_func("/util/base64/good", test_base64_good); - g_test_add_func("/util/base64/embedded-nul", test_base64_embedded_nul); - g_test_add_func("/util/base64/not-nul-terminated", - test_base64_not_nul_terminated); - g_test_add_func("/util/base64/invalid-chars", test_base64_invalid_chars); - return g_test_run(); -} diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c deleted file mode 100644 index 8a29e33e00..0000000000 --- a/tests/test-bdrv-drain.c +++ /dev/null @@ -1,2230 +0,0 @@ -/* - * Block node draining tests - * - * Copyright (c) 2017 Kevin Wolf - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "block/block.h" -#include "block/blockjob_int.h" -#include "sysemu/block-backend.h" -#include "qapi/error.h" -#include "qemu/main-loop.h" -#include "iothread.h" - -static QemuEvent done_event; - -typedef struct BDRVTestState { - int drain_count; - AioContext *bh_indirection_ctx; - bool sleep_in_drain_begin; -} BDRVTestState; - -static void coroutine_fn bdrv_test_co_drain_begin(BlockDriverState *bs) -{ - BDRVTestState *s = bs->opaque; - s->drain_count++; - if (s->sleep_in_drain_begin) { - qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 100000); - } -} - -static void coroutine_fn bdrv_test_co_drain_end(BlockDriverState *bs) -{ - BDRVTestState *s = bs->opaque; - s->drain_count--; -} - -static void bdrv_test_close(BlockDriverState *bs) -{ - BDRVTestState *s = bs->opaque; - g_assert_cmpint(s->drain_count, >, 0); -} - -static void co_reenter_bh(void *opaque) -{ - aio_co_wake(opaque); -} - -static int coroutine_fn bdrv_test_co_preadv(BlockDriverState *bs, - uint64_t offset, uint64_t bytes, - QEMUIOVector *qiov, int flags) -{ - BDRVTestState *s = bs->opaque; - - /* We want this request to stay until the polling loop in drain waits for - * it to complete. We need to sleep a while as bdrv_drain_invoke() comes - * first and polls its result, too, but it shouldn't accidentally complete - * this request yet. */ - qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 100000); - - if (s->bh_indirection_ctx) { - aio_bh_schedule_oneshot(s->bh_indirection_ctx, co_reenter_bh, - qemu_coroutine_self()); - qemu_coroutine_yield(); - } - - return 0; -} - -static int bdrv_test_change_backing_file(BlockDriverState *bs, - const char *backing_file, - const char *backing_fmt) -{ - return 0; -} - -static BlockDriver bdrv_test = { - .format_name = "test", - .instance_size = sizeof(BDRVTestState), - - .bdrv_close = bdrv_test_close, - .bdrv_co_preadv = bdrv_test_co_preadv, - - .bdrv_co_drain_begin = bdrv_test_co_drain_begin, - .bdrv_co_drain_end = bdrv_test_co_drain_end, - - .bdrv_child_perm = bdrv_default_perms, - - .bdrv_change_backing_file = bdrv_test_change_backing_file, -}; - -static void aio_ret_cb(void *opaque, int ret) -{ - int *aio_ret = opaque; - *aio_ret = ret; -} - -typedef struct CallInCoroutineData { - void (*entry)(void); - bool done; -} CallInCoroutineData; - -static coroutine_fn void call_in_coroutine_entry(void *opaque) -{ - CallInCoroutineData *data = opaque; - - data->entry(); - data->done = true; -} - -static void call_in_coroutine(void (*entry)(void)) -{ - Coroutine *co; - CallInCoroutineData data = { - .entry = entry, - .done = false, - }; - - co = qemu_coroutine_create(call_in_coroutine_entry, &data); - qemu_coroutine_enter(co); - while (!data.done) { - aio_poll(qemu_get_aio_context(), true); - } -} - -enum drain_type { - BDRV_DRAIN_ALL, - BDRV_DRAIN, - BDRV_SUBTREE_DRAIN, - DRAIN_TYPE_MAX, -}; - -static void do_drain_begin(enum drain_type drain_type, BlockDriverState *bs) -{ - switch (drain_type) { - case BDRV_DRAIN_ALL: bdrv_drain_all_begin(); break; - case BDRV_DRAIN: bdrv_drained_begin(bs); break; - case BDRV_SUBTREE_DRAIN: bdrv_subtree_drained_begin(bs); break; - default: g_assert_not_reached(); - } -} - -static void do_drain_end(enum drain_type drain_type, BlockDriverState *bs) -{ - switch (drain_type) { - case BDRV_DRAIN_ALL: bdrv_drain_all_end(); break; - case BDRV_DRAIN: bdrv_drained_end(bs); break; - case BDRV_SUBTREE_DRAIN: bdrv_subtree_drained_end(bs); break; - default: g_assert_not_reached(); - } -} - -static void do_drain_begin_unlocked(enum drain_type drain_type, BlockDriverState *bs) -{ - if (drain_type != BDRV_DRAIN_ALL) { - aio_context_acquire(bdrv_get_aio_context(bs)); - } - do_drain_begin(drain_type, bs); - if (drain_type != BDRV_DRAIN_ALL) { - aio_context_release(bdrv_get_aio_context(bs)); - } -} - -static void do_drain_end_unlocked(enum drain_type drain_type, BlockDriverState *bs) -{ - if (drain_type != BDRV_DRAIN_ALL) { - aio_context_acquire(bdrv_get_aio_context(bs)); - } - do_drain_end(drain_type, bs); - if (drain_type != BDRV_DRAIN_ALL) { - aio_context_release(bdrv_get_aio_context(bs)); - } -} - -static void test_drv_cb_common(enum drain_type drain_type, bool recursive) -{ - BlockBackend *blk; - BlockDriverState *bs, *backing; - BDRVTestState *s, *backing_s; - BlockAIOCB *acb; - int aio_ret; - - QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, NULL, 0); - - blk = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL); - bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR, - &error_abort); - s = bs->opaque; - blk_insert_bs(blk, bs, &error_abort); - - backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort); - backing_s = backing->opaque; - bdrv_set_backing_hd(bs, backing, &error_abort); - - /* Simple bdrv_drain_all_begin/end pair, check that CBs are called */ - g_assert_cmpint(s->drain_count, ==, 0); - g_assert_cmpint(backing_s->drain_count, ==, 0); - - do_drain_begin(drain_type, bs); - - g_assert_cmpint(s->drain_count, ==, 1); - g_assert_cmpint(backing_s->drain_count, ==, !!recursive); - - do_drain_end(drain_type, bs); - - g_assert_cmpint(s->drain_count, ==, 0); - g_assert_cmpint(backing_s->drain_count, ==, 0); - - /* Now do the same while a request is pending */ - aio_ret = -EINPROGRESS; - acb = blk_aio_preadv(blk, 0, &qiov, 0, aio_ret_cb, &aio_ret); - g_assert(acb != NULL); - g_assert_cmpint(aio_ret, ==, -EINPROGRESS); - - g_assert_cmpint(s->drain_count, ==, 0); - g_assert_cmpint(backing_s->drain_count, ==, 0); - - do_drain_begin(drain_type, bs); - - g_assert_cmpint(aio_ret, ==, 0); - g_assert_cmpint(s->drain_count, ==, 1); - g_assert_cmpint(backing_s->drain_count, ==, !!recursive); - - do_drain_end(drain_type, bs); - - g_assert_cmpint(s->drain_count, ==, 0); - g_assert_cmpint(backing_s->drain_count, ==, 0); - - bdrv_unref(backing); - bdrv_unref(bs); - blk_unref(blk); -} - -static void test_drv_cb_drain_all(void) -{ - test_drv_cb_common(BDRV_DRAIN_ALL, true); -} - -static void test_drv_cb_drain(void) -{ - test_drv_cb_common(BDRV_DRAIN, false); -} - -static void test_drv_cb_drain_subtree(void) -{ - test_drv_cb_common(BDRV_SUBTREE_DRAIN, true); -} - -static void test_drv_cb_co_drain_all(void) -{ - call_in_coroutine(test_drv_cb_drain_all); -} - -static void test_drv_cb_co_drain(void) -{ - call_in_coroutine(test_drv_cb_drain); -} - -static void test_drv_cb_co_drain_subtree(void) -{ - call_in_coroutine(test_drv_cb_drain_subtree); -} - -static void test_quiesce_common(enum drain_type drain_type, bool recursive) -{ - BlockBackend *blk; - BlockDriverState *bs, *backing; - - blk = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL); - bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR, - &error_abort); - blk_insert_bs(blk, bs, &error_abort); - - backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort); - bdrv_set_backing_hd(bs, backing, &error_abort); - - g_assert_cmpint(bs->quiesce_counter, ==, 0); - g_assert_cmpint(backing->quiesce_counter, ==, 0); - - do_drain_begin(drain_type, bs); - - g_assert_cmpint(bs->quiesce_counter, ==, 1); - g_assert_cmpint(backing->quiesce_counter, ==, !!recursive); - - do_drain_end(drain_type, bs); - - g_assert_cmpint(bs->quiesce_counter, ==, 0); - g_assert_cmpint(backing->quiesce_counter, ==, 0); - - bdrv_unref(backing); - bdrv_unref(bs); - blk_unref(blk); -} - -static void test_quiesce_drain_all(void) -{ - test_quiesce_common(BDRV_DRAIN_ALL, true); -} - -static void test_quiesce_drain(void) -{ - test_quiesce_common(BDRV_DRAIN, false); -} - -static void test_quiesce_drain_subtree(void) -{ - test_quiesce_common(BDRV_SUBTREE_DRAIN, true); -} - -static void test_quiesce_co_drain_all(void) -{ - call_in_coroutine(test_quiesce_drain_all); -} - -static void test_quiesce_co_drain(void) -{ - call_in_coroutine(test_quiesce_drain); -} - -static void test_quiesce_co_drain_subtree(void) -{ - call_in_coroutine(test_quiesce_drain_subtree); -} - -static void test_nested(void) -{ - BlockBackend *blk; - BlockDriverState *bs, *backing; - BDRVTestState *s, *backing_s; - enum drain_type outer, inner; - - blk = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL); - bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR, - &error_abort); - s = bs->opaque; - blk_insert_bs(blk, bs, &error_abort); - - backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort); - backing_s = backing->opaque; - bdrv_set_backing_hd(bs, backing, &error_abort); - - for (outer = 0; outer < DRAIN_TYPE_MAX; outer++) { - for (inner = 0; inner < DRAIN_TYPE_MAX; inner++) { - int backing_quiesce = (outer != BDRV_DRAIN) + - (inner != BDRV_DRAIN); - - g_assert_cmpint(bs->quiesce_counter, ==, 0); - g_assert_cmpint(backing->quiesce_counter, ==, 0); - g_assert_cmpint(s->drain_count, ==, 0); - g_assert_cmpint(backing_s->drain_count, ==, 0); - - do_drain_begin(outer, bs); - do_drain_begin(inner, bs); - - g_assert_cmpint(bs->quiesce_counter, ==, 2); - g_assert_cmpint(backing->quiesce_counter, ==, backing_quiesce); - g_assert_cmpint(s->drain_count, ==, 2); - g_assert_cmpint(backing_s->drain_count, ==, backing_quiesce); - - do_drain_end(inner, bs); - do_drain_end(outer, bs); - - g_assert_cmpint(bs->quiesce_counter, ==, 0); - g_assert_cmpint(backing->quiesce_counter, ==, 0); - g_assert_cmpint(s->drain_count, ==, 0); - g_assert_cmpint(backing_s->drain_count, ==, 0); - } - } - - bdrv_unref(backing); - bdrv_unref(bs); - blk_unref(blk); -} - -static void test_multiparent(void) -{ - BlockBackend *blk_a, *blk_b; - BlockDriverState *bs_a, *bs_b, *backing; - BDRVTestState *a_s, *b_s, *backing_s; - - blk_a = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL); - bs_a = bdrv_new_open_driver(&bdrv_test, "test-node-a", BDRV_O_RDWR, - &error_abort); - a_s = bs_a->opaque; - blk_insert_bs(blk_a, bs_a, &error_abort); - - blk_b = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL); - bs_b = bdrv_new_open_driver(&bdrv_test, "test-node-b", BDRV_O_RDWR, - &error_abort); - b_s = bs_b->opaque; - blk_insert_bs(blk_b, bs_b, &error_abort); - - backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort); - backing_s = backing->opaque; - bdrv_set_backing_hd(bs_a, backing, &error_abort); - bdrv_set_backing_hd(bs_b, backing, &error_abort); - - g_assert_cmpint(bs_a->quiesce_counter, ==, 0); - g_assert_cmpint(bs_b->quiesce_counter, ==, 0); - g_assert_cmpint(backing->quiesce_counter, ==, 0); - g_assert_cmpint(a_s->drain_count, ==, 0); - g_assert_cmpint(b_s->drain_count, ==, 0); - g_assert_cmpint(backing_s->drain_count, ==, 0); - - do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a); - - g_assert_cmpint(bs_a->quiesce_counter, ==, 1); - g_assert_cmpint(bs_b->quiesce_counter, ==, 1); - g_assert_cmpint(backing->quiesce_counter, ==, 1); - g_assert_cmpint(a_s->drain_count, ==, 1); - g_assert_cmpint(b_s->drain_count, ==, 1); - g_assert_cmpint(backing_s->drain_count, ==, 1); - - do_drain_begin(BDRV_SUBTREE_DRAIN, bs_b); - - g_assert_cmpint(bs_a->quiesce_counter, ==, 2); - g_assert_cmpint(bs_b->quiesce_counter, ==, 2); - g_assert_cmpint(backing->quiesce_counter, ==, 2); - g_assert_cmpint(a_s->drain_count, ==, 2); - g_assert_cmpint(b_s->drain_count, ==, 2); - g_assert_cmpint(backing_s->drain_count, ==, 2); - - do_drain_end(BDRV_SUBTREE_DRAIN, bs_b); - - g_assert_cmpint(bs_a->quiesce_counter, ==, 1); - g_assert_cmpint(bs_b->quiesce_counter, ==, 1); - g_assert_cmpint(backing->quiesce_counter, ==, 1); - g_assert_cmpint(a_s->drain_count, ==, 1); - g_assert_cmpint(b_s->drain_count, ==, 1); - g_assert_cmpint(backing_s->drain_count, ==, 1); - - do_drain_end(BDRV_SUBTREE_DRAIN, bs_a); - - g_assert_cmpint(bs_a->quiesce_counter, ==, 0); - g_assert_cmpint(bs_b->quiesce_counter, ==, 0); - g_assert_cmpint(backing->quiesce_counter, ==, 0); - g_assert_cmpint(a_s->drain_count, ==, 0); - g_assert_cmpint(b_s->drain_count, ==, 0); - g_assert_cmpint(backing_s->drain_count, ==, 0); - - bdrv_unref(backing); - bdrv_unref(bs_a); - bdrv_unref(bs_b); - blk_unref(blk_a); - blk_unref(blk_b); -} - -static void test_graph_change_drain_subtree(void) -{ - BlockBackend *blk_a, *blk_b; - BlockDriverState *bs_a, *bs_b, *backing; - BDRVTestState *a_s, *b_s, *backing_s; - - blk_a = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL); - bs_a = bdrv_new_open_driver(&bdrv_test, "test-node-a", BDRV_O_RDWR, - &error_abort); - a_s = bs_a->opaque; - blk_insert_bs(blk_a, bs_a, &error_abort); - - blk_b = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL); - bs_b = bdrv_new_open_driver(&bdrv_test, "test-node-b", BDRV_O_RDWR, - &error_abort); - b_s = bs_b->opaque; - blk_insert_bs(blk_b, bs_b, &error_abort); - - backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort); - backing_s = backing->opaque; - bdrv_set_backing_hd(bs_a, backing, &error_abort); - - g_assert_cmpint(bs_a->quiesce_counter, ==, 0); - g_assert_cmpint(bs_b->quiesce_counter, ==, 0); - g_assert_cmpint(backing->quiesce_counter, ==, 0); - g_assert_cmpint(a_s->drain_count, ==, 0); - g_assert_cmpint(b_s->drain_count, ==, 0); - g_assert_cmpint(backing_s->drain_count, ==, 0); - - do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a); - do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a); - do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a); - do_drain_begin(BDRV_SUBTREE_DRAIN, bs_b); - do_drain_begin(BDRV_SUBTREE_DRAIN, bs_b); - - bdrv_set_backing_hd(bs_b, backing, &error_abort); - g_assert_cmpint(bs_a->quiesce_counter, ==, 5); - g_assert_cmpint(bs_b->quiesce_counter, ==, 5); - g_assert_cmpint(backing->quiesce_counter, ==, 5); - g_assert_cmpint(a_s->drain_count, ==, 5); - g_assert_cmpint(b_s->drain_count, ==, 5); - g_assert_cmpint(backing_s->drain_count, ==, 5); - - bdrv_set_backing_hd(bs_b, NULL, &error_abort); - g_assert_cmpint(bs_a->quiesce_counter, ==, 3); - g_assert_cmpint(bs_b->quiesce_counter, ==, 2); - g_assert_cmpint(backing->quiesce_counter, ==, 3); - g_assert_cmpint(a_s->drain_count, ==, 3); - g_assert_cmpint(b_s->drain_count, ==, 2); - g_assert_cmpint(backing_s->drain_count, ==, 3); - - bdrv_set_backing_hd(bs_b, backing, &error_abort); - g_assert_cmpint(bs_a->quiesce_counter, ==, 5); - g_assert_cmpint(bs_b->quiesce_counter, ==, 5); - g_assert_cmpint(backing->quiesce_counter, ==, 5); - g_assert_cmpint(a_s->drain_count, ==, 5); - g_assert_cmpint(b_s->drain_count, ==, 5); - g_assert_cmpint(backing_s->drain_count, ==, 5); - - do_drain_end(BDRV_SUBTREE_DRAIN, bs_b); - do_drain_end(BDRV_SUBTREE_DRAIN, bs_b); - do_drain_end(BDRV_SUBTREE_DRAIN, bs_a); - do_drain_end(BDRV_SUBTREE_DRAIN, bs_a); - do_drain_end(BDRV_SUBTREE_DRAIN, bs_a); - - g_assert_cmpint(bs_a->quiesce_counter, ==, 0); - g_assert_cmpint(bs_b->quiesce_counter, ==, 0); - g_assert_cmpint(backing->quiesce_counter, ==, 0); - g_assert_cmpint(a_s->drain_count, ==, 0); - g_assert_cmpint(b_s->drain_count, ==, 0); - g_assert_cmpint(backing_s->drain_count, ==, 0); - - bdrv_unref(backing); - bdrv_unref(bs_a); - bdrv_unref(bs_b); - blk_unref(blk_a); - blk_unref(blk_b); -} - -static void test_graph_change_drain_all(void) -{ - BlockBackend *blk_a, *blk_b; - BlockDriverState *bs_a, *bs_b; - BDRVTestState *a_s, *b_s; - - /* Create node A with a BlockBackend */ - blk_a = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL); - bs_a = bdrv_new_open_driver(&bdrv_test, "test-node-a", BDRV_O_RDWR, - &error_abort); - a_s = bs_a->opaque; - blk_insert_bs(blk_a, bs_a, &error_abort); - - g_assert_cmpint(bs_a->quiesce_counter, ==, 0); - g_assert_cmpint(a_s->drain_count, ==, 0); - - /* Call bdrv_drain_all_begin() */ - bdrv_drain_all_begin(); - - g_assert_cmpint(bs_a->quiesce_counter, ==, 1); - g_assert_cmpint(a_s->drain_count, ==, 1); - - /* Create node B with a BlockBackend */ - blk_b = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL); - bs_b = bdrv_new_open_driver(&bdrv_test, "test-node-b", BDRV_O_RDWR, - &error_abort); - b_s = bs_b->opaque; - blk_insert_bs(blk_b, bs_b, &error_abort); - - g_assert_cmpint(bs_a->quiesce_counter, ==, 1); - g_assert_cmpint(bs_b->quiesce_counter, ==, 1); - g_assert_cmpint(a_s->drain_count, ==, 1); - g_assert_cmpint(b_s->drain_count, ==, 1); - - /* Unref and finally delete node A */ - blk_unref(blk_a); - - g_assert_cmpint(bs_a->quiesce_counter, ==, 1); - g_assert_cmpint(bs_b->quiesce_counter, ==, 1); - g_assert_cmpint(a_s->drain_count, ==, 1); - g_assert_cmpint(b_s->drain_count, ==, 1); - - bdrv_unref(bs_a); - - g_assert_cmpint(bs_b->quiesce_counter, ==, 1); - g_assert_cmpint(b_s->drain_count, ==, 1); - - /* End the drained section */ - bdrv_drain_all_end(); - - g_assert_cmpint(bs_b->quiesce_counter, ==, 0); - g_assert_cmpint(b_s->drain_count, ==, 0); - g_assert_cmpint(qemu_get_aio_context()->external_disable_cnt, ==, 0); - - bdrv_unref(bs_b); - blk_unref(blk_b); -} - -struct test_iothread_data { - BlockDriverState *bs; - enum drain_type drain_type; - int *aio_ret; -}; - -static void test_iothread_drain_entry(void *opaque) -{ - struct test_iothread_data *data = opaque; - - aio_context_acquire(bdrv_get_aio_context(data->bs)); - do_drain_begin(data->drain_type, data->bs); - g_assert_cmpint(*data->aio_ret, ==, 0); - do_drain_end(data->drain_type, data->bs); - aio_context_release(bdrv_get_aio_context(data->bs)); - - qemu_event_set(&done_event); -} - -static void test_iothread_aio_cb(void *opaque, int ret) -{ - int *aio_ret = opaque; - *aio_ret = ret; - qemu_event_set(&done_event); -} - -static void test_iothread_main_thread_bh(void *opaque) -{ - struct test_iothread_data *data = opaque; - - /* Test that the AioContext is not yet locked in a random BH that is - * executed during drain, otherwise this would deadlock. */ - aio_context_acquire(bdrv_get_aio_context(data->bs)); - bdrv_flush(data->bs); - aio_context_release(bdrv_get_aio_context(data->bs)); -} - -/* - * Starts an AIO request on a BDS that runs in the AioContext of iothread 1. - * The request involves a BH on iothread 2 before it can complete. - * - * @drain_thread = 0 means that do_drain_begin/end are called from the main - * thread, @drain_thread = 1 means that they are called from iothread 1. Drain - * for this BDS cannot be called from iothread 2 because only the main thread - * may do cross-AioContext polling. - */ -static void test_iothread_common(enum drain_type drain_type, int drain_thread) -{ - BlockBackend *blk; - BlockDriverState *bs; - BDRVTestState *s; - BlockAIOCB *acb; - int aio_ret; - struct test_iothread_data data; - - IOThread *a = iothread_new(); - IOThread *b = iothread_new(); - AioContext *ctx_a = iothread_get_aio_context(a); - AioContext *ctx_b = iothread_get_aio_context(b); - - QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, NULL, 0); - - /* bdrv_drain_all() may only be called from the main loop thread */ - if (drain_type == BDRV_DRAIN_ALL && drain_thread != 0) { - goto out; - } - - blk = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL); - bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR, - &error_abort); - s = bs->opaque; - blk_insert_bs(blk, bs, &error_abort); - blk_set_disable_request_queuing(blk, true); - - blk_set_aio_context(blk, ctx_a, &error_abort); - aio_context_acquire(ctx_a); - - s->bh_indirection_ctx = ctx_b; - - aio_ret = -EINPROGRESS; - qemu_event_reset(&done_event); - - if (drain_thread == 0) { - acb = blk_aio_preadv(blk, 0, &qiov, 0, test_iothread_aio_cb, &aio_ret); - } else { - acb = blk_aio_preadv(blk, 0, &qiov, 0, aio_ret_cb, &aio_ret); - } - g_assert(acb != NULL); - g_assert_cmpint(aio_ret, ==, -EINPROGRESS); - - aio_context_release(ctx_a); - - data = (struct test_iothread_data) { - .bs = bs, - .drain_type = drain_type, - .aio_ret = &aio_ret, - }; - - switch (drain_thread) { - case 0: - if (drain_type != BDRV_DRAIN_ALL) { - aio_context_acquire(ctx_a); - } - - aio_bh_schedule_oneshot(ctx_a, test_iothread_main_thread_bh, &data); - - /* The request is running on the IOThread a. Draining its block device - * will make sure that it has completed as far as the BDS is concerned, - * but the drain in this thread can continue immediately after - * bdrv_dec_in_flight() and aio_ret might be assigned only slightly - * later. */ - do_drain_begin(drain_type, bs); - g_assert_cmpint(bs->in_flight, ==, 0); - - if (drain_type != BDRV_DRAIN_ALL) { - aio_context_release(ctx_a); - } - qemu_event_wait(&done_event); - if (drain_type != BDRV_DRAIN_ALL) { - aio_context_acquire(ctx_a); - } - - g_assert_cmpint(aio_ret, ==, 0); - do_drain_end(drain_type, bs); - - if (drain_type != BDRV_DRAIN_ALL) { - aio_context_release(ctx_a); - } - break; - case 1: - aio_bh_schedule_oneshot(ctx_a, test_iothread_drain_entry, &data); - qemu_event_wait(&done_event); - break; - default: - g_assert_not_reached(); - } - - aio_context_acquire(ctx_a); - blk_set_aio_context(blk, qemu_get_aio_context(), &error_abort); - aio_context_release(ctx_a); - - bdrv_unref(bs); - blk_unref(blk); - -out: - iothread_join(a); - iothread_join(b); -} - -static void test_iothread_drain_all(void) -{ - test_iothread_common(BDRV_DRAIN_ALL, 0); - test_iothread_common(BDRV_DRAIN_ALL, 1); -} - -static void test_iothread_drain(void) -{ - test_iothread_common(BDRV_DRAIN, 0); - test_iothread_common(BDRV_DRAIN, 1); -} - -static void test_iothread_drain_subtree(void) -{ - test_iothread_common(BDRV_SUBTREE_DRAIN, 0); - test_iothread_common(BDRV_SUBTREE_DRAIN, 1); -} - - -typedef struct TestBlockJob { - BlockJob common; - int run_ret; - int prepare_ret; - bool running; - bool should_complete; -} TestBlockJob; - -static int test_job_prepare(Job *job) -{ - TestBlockJob *s = container_of(job, TestBlockJob, common.job); - - /* Provoke an AIO_WAIT_WHILE() call to verify there is no deadlock */ - blk_flush(s->common.blk); - return s->prepare_ret; -} - -static void test_job_commit(Job *job) -{ - TestBlockJob *s = container_of(job, TestBlockJob, common.job); - - /* Provoke an AIO_WAIT_WHILE() call to verify there is no deadlock */ - blk_flush(s->common.blk); -} - -static void test_job_abort(Job *job) -{ - TestBlockJob *s = container_of(job, TestBlockJob, common.job); - - /* Provoke an AIO_WAIT_WHILE() call to verify there is no deadlock */ - blk_flush(s->common.blk); -} - -static int coroutine_fn test_job_run(Job *job, Error **errp) -{ - TestBlockJob *s = container_of(job, TestBlockJob, common.job); - - /* We are running the actual job code past the pause point in - * job_co_entry(). */ - s->running = true; - - job_transition_to_ready(&s->common.job); - while (!s->should_complete) { - /* Avoid job_sleep_ns() because it marks the job as !busy. We want to - * emulate some actual activity (probably some I/O) here so that drain - * has to wait for this activity to stop. */ - qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 1000000); - - job_pause_point(&s->common.job); - } - - return s->run_ret; -} - -static void test_job_complete(Job *job, Error **errp) -{ - TestBlockJob *s = container_of(job, TestBlockJob, common.job); - s->should_complete = true; -} - -BlockJobDriver test_job_driver = { - .job_driver = { - .instance_size = sizeof(TestBlockJob), - .free = block_job_free, - .user_resume = block_job_user_resume, - .run = test_job_run, - .complete = test_job_complete, - .prepare = test_job_prepare, - .commit = test_job_commit, - .abort = test_job_abort, - }, -}; - -enum test_job_result { - TEST_JOB_SUCCESS, - TEST_JOB_FAIL_RUN, - TEST_JOB_FAIL_PREPARE, -}; - -enum test_job_drain_node { - TEST_JOB_DRAIN_SRC, - TEST_JOB_DRAIN_SRC_CHILD, - TEST_JOB_DRAIN_SRC_PARENT, -}; - -static void test_blockjob_common_drain_node(enum drain_type drain_type, - bool use_iothread, - enum test_job_result result, - enum test_job_drain_node drain_node) -{ - BlockBackend *blk_src, *blk_target; - BlockDriverState *src, *src_backing, *src_overlay, *target, *drain_bs; - BlockJob *job; - TestBlockJob *tjob; - IOThread *iothread = NULL; - AioContext *ctx; - int ret; - - src = bdrv_new_open_driver(&bdrv_test, "source", BDRV_O_RDWR, - &error_abort); - src_backing = bdrv_new_open_driver(&bdrv_test, "source-backing", - BDRV_O_RDWR, &error_abort); - src_overlay = bdrv_new_open_driver(&bdrv_test, "source-overlay", - BDRV_O_RDWR, &error_abort); - - bdrv_set_backing_hd(src_overlay, src, &error_abort); - bdrv_unref(src); - bdrv_set_backing_hd(src, src_backing, &error_abort); - bdrv_unref(src_backing); - - blk_src = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL); - blk_insert_bs(blk_src, src_overlay, &error_abort); - - switch (drain_node) { - case TEST_JOB_DRAIN_SRC: - drain_bs = src; - break; - case TEST_JOB_DRAIN_SRC_CHILD: - drain_bs = src_backing; - break; - case TEST_JOB_DRAIN_SRC_PARENT: - drain_bs = src_overlay; - break; - default: - g_assert_not_reached(); - } - - if (use_iothread) { - iothread = iothread_new(); - ctx = iothread_get_aio_context(iothread); - blk_set_aio_context(blk_src, ctx, &error_abort); - } else { - ctx = qemu_get_aio_context(); - } - - target = bdrv_new_open_driver(&bdrv_test, "target", BDRV_O_RDWR, - &error_abort); - blk_target = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL); - blk_insert_bs(blk_target, target, &error_abort); - blk_set_allow_aio_context_change(blk_target, true); - - aio_context_acquire(ctx); - tjob = block_job_create("job0", &test_job_driver, NULL, src, - 0, BLK_PERM_ALL, - 0, 0, NULL, NULL, &error_abort); - job = &tjob->common; - block_job_add_bdrv(job, "target", target, 0, BLK_PERM_ALL, &error_abort); - - switch (result) { - case TEST_JOB_SUCCESS: - break; - case TEST_JOB_FAIL_RUN: - tjob->run_ret = -EIO; - break; - case TEST_JOB_FAIL_PREPARE: - tjob->prepare_ret = -EIO; - break; - } - - job_start(&job->job); - aio_context_release(ctx); - - if (use_iothread) { - /* job_co_entry() is run in the I/O thread, wait for the actual job - * code to start (we don't want to catch the job in the pause point in - * job_co_entry(). */ - while (!tjob->running) { - aio_poll(qemu_get_aio_context(), false); - } - } - - g_assert_cmpint(job->job.pause_count, ==, 0); - g_assert_false(job->job.paused); - g_assert_true(tjob->running); - g_assert_true(job->job.busy); /* We're in qemu_co_sleep_ns() */ - - do_drain_begin_unlocked(drain_type, drain_bs); - - if (drain_type == BDRV_DRAIN_ALL) { - /* bdrv_drain_all() drains both src and target */ - g_assert_cmpint(job->job.pause_count, ==, 2); - } else { - g_assert_cmpint(job->job.pause_count, ==, 1); - } - g_assert_true(job->job.paused); - g_assert_false(job->job.busy); /* The job is paused */ - - do_drain_end_unlocked(drain_type, drain_bs); - - if (use_iothread) { - /* paused is reset in the I/O thread, wait for it */ - while (job->job.paused) { - aio_poll(qemu_get_aio_context(), false); - } - } - - g_assert_cmpint(job->job.pause_count, ==, 0); - g_assert_false(job->job.paused); - g_assert_true(job->job.busy); /* We're in qemu_co_sleep_ns() */ - - do_drain_begin_unlocked(drain_type, target); - - if (drain_type == BDRV_DRAIN_ALL) { - /* bdrv_drain_all() drains both src and target */ - g_assert_cmpint(job->job.pause_count, ==, 2); - } else { - g_assert_cmpint(job->job.pause_count, ==, 1); - } - g_assert_true(job->job.paused); - g_assert_false(job->job.busy); /* The job is paused */ - - do_drain_end_unlocked(drain_type, target); - - if (use_iothread) { - /* paused is reset in the I/O thread, wait for it */ - while (job->job.paused) { - aio_poll(qemu_get_aio_context(), false); - } - } - - g_assert_cmpint(job->job.pause_count, ==, 0); - g_assert_false(job->job.paused); - g_assert_true(job->job.busy); /* We're in qemu_co_sleep_ns() */ - - aio_context_acquire(ctx); - ret = job_complete_sync(&job->job, &error_abort); - g_assert_cmpint(ret, ==, (result == TEST_JOB_SUCCESS ? 0 : -EIO)); - - if (use_iothread) { - blk_set_aio_context(blk_src, qemu_get_aio_context(), &error_abort); - assert(blk_get_aio_context(blk_target) == qemu_get_aio_context()); - } - aio_context_release(ctx); - - blk_unref(blk_src); - blk_unref(blk_target); - bdrv_unref(src_overlay); - bdrv_unref(target); - - if (iothread) { - iothread_join(iothread); - } -} - -static void test_blockjob_common(enum drain_type drain_type, bool use_iothread, - enum test_job_result result) -{ - test_blockjob_common_drain_node(drain_type, use_iothread, result, - TEST_JOB_DRAIN_SRC); - test_blockjob_common_drain_node(drain_type, use_iothread, result, - TEST_JOB_DRAIN_SRC_CHILD); - if (drain_type == BDRV_SUBTREE_DRAIN) { - test_blockjob_common_drain_node(drain_type, use_iothread, result, - TEST_JOB_DRAIN_SRC_PARENT); - } -} - -static void test_blockjob_drain_all(void) -{ - test_blockjob_common(BDRV_DRAIN_ALL, false, TEST_JOB_SUCCESS); -} - -static void test_blockjob_drain(void) -{ - test_blockjob_common(BDRV_DRAIN, false, TEST_JOB_SUCCESS); -} - -static void test_blockjob_drain_subtree(void) -{ - test_blockjob_common(BDRV_SUBTREE_DRAIN, false, TEST_JOB_SUCCESS); -} - -static void test_blockjob_error_drain_all(void) -{ - test_blockjob_common(BDRV_DRAIN_ALL, false, TEST_JOB_FAIL_RUN); - test_blockjob_common(BDRV_DRAIN_ALL, false, TEST_JOB_FAIL_PREPARE); -} - -static void test_blockjob_error_drain(void) -{ - test_blockjob_common(BDRV_DRAIN, false, TEST_JOB_FAIL_RUN); - test_blockjob_common(BDRV_DRAIN, false, TEST_JOB_FAIL_PREPARE); -} - -static void test_blockjob_error_drain_subtree(void) -{ - test_blockjob_common(BDRV_SUBTREE_DRAIN, false, TEST_JOB_FAIL_RUN); - test_blockjob_common(BDRV_SUBTREE_DRAIN, false, TEST_JOB_FAIL_PREPARE); -} - -static void test_blockjob_iothread_drain_all(void) -{ - test_blockjob_common(BDRV_DRAIN_ALL, true, TEST_JOB_SUCCESS); -} - -static void test_blockjob_iothread_drain(void) -{ - test_blockjob_common(BDRV_DRAIN, true, TEST_JOB_SUCCESS); -} - -static void test_blockjob_iothread_drain_subtree(void) -{ - test_blockjob_common(BDRV_SUBTREE_DRAIN, true, TEST_JOB_SUCCESS); -} - -static void test_blockjob_iothread_error_drain_all(void) -{ - test_blockjob_common(BDRV_DRAIN_ALL, true, TEST_JOB_FAIL_RUN); - test_blockjob_common(BDRV_DRAIN_ALL, true, TEST_JOB_FAIL_PREPARE); -} - -static void test_blockjob_iothread_error_drain(void) -{ - test_blockjob_common(BDRV_DRAIN, true, TEST_JOB_FAIL_RUN); - test_blockjob_common(BDRV_DRAIN, true, TEST_JOB_FAIL_PREPARE); -} - -static void test_blockjob_iothread_error_drain_subtree(void) -{ - test_blockjob_common(BDRV_SUBTREE_DRAIN, true, TEST_JOB_FAIL_RUN); - test_blockjob_common(BDRV_SUBTREE_DRAIN, true, TEST_JOB_FAIL_PREPARE); -} - - -typedef struct BDRVTestTopState { - BdrvChild *wait_child; -} BDRVTestTopState; - -static void bdrv_test_top_close(BlockDriverState *bs) -{ - BdrvChild *c, *next_c; - QLIST_FOREACH_SAFE(c, &bs->children, next, next_c) { - bdrv_unref_child(bs, c); - } -} - -static int coroutine_fn bdrv_test_top_co_preadv(BlockDriverState *bs, - uint64_t offset, uint64_t bytes, - QEMUIOVector *qiov, int flags) -{ - BDRVTestTopState *tts = bs->opaque; - return bdrv_co_preadv(tts->wait_child, offset, bytes, qiov, flags); -} - -static BlockDriver bdrv_test_top_driver = { - .format_name = "test_top_driver", - .instance_size = sizeof(BDRVTestTopState), - - .bdrv_close = bdrv_test_top_close, - .bdrv_co_preadv = bdrv_test_top_co_preadv, - - .bdrv_child_perm = bdrv_default_perms, -}; - -typedef struct TestCoDeleteByDrainData { - BlockBackend *blk; - bool detach_instead_of_delete; - bool done; -} TestCoDeleteByDrainData; - -static void coroutine_fn test_co_delete_by_drain(void *opaque) -{ - TestCoDeleteByDrainData *dbdd = opaque; - BlockBackend *blk = dbdd->blk; - BlockDriverState *bs = blk_bs(blk); - BDRVTestTopState *tts = bs->opaque; - void *buffer = g_malloc(65536); - QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buffer, 65536); - - /* Pretend some internal write operation from parent to child. - * Important: We have to read from the child, not from the parent! - * Draining works by first propagating it all up the tree to the - * root and then waiting for drainage from root to the leaves - * (protocol nodes). If we have a request waiting on the root, - * everything will be drained before we go back down the tree, but - * we do not want that. We want to be in the middle of draining - * when this following requests returns. */ - bdrv_co_preadv(tts->wait_child, 0, 65536, &qiov, 0); - - g_assert_cmpint(bs->refcnt, ==, 1); - - if (!dbdd->detach_instead_of_delete) { - blk_unref(blk); - } else { - BdrvChild *c, *next_c; - QLIST_FOREACH_SAFE(c, &bs->children, next, next_c) { - bdrv_unref_child(bs, c); - } - } - - dbdd->done = true; - g_free(buffer); -} - -/** - * Test what happens when some BDS has some children, you drain one of - * them and this results in the BDS being deleted. - * - * If @detach_instead_of_delete is set, the BDS is not going to be - * deleted but will only detach all of its children. - */ -static void do_test_delete_by_drain(bool detach_instead_of_delete, - enum drain_type drain_type) -{ - BlockBackend *blk; - BlockDriverState *bs, *child_bs, *null_bs; - BDRVTestTopState *tts; - TestCoDeleteByDrainData dbdd; - Coroutine *co; - - bs = bdrv_new_open_driver(&bdrv_test_top_driver, "top", BDRV_O_RDWR, - &error_abort); - bs->total_sectors = 65536 >> BDRV_SECTOR_BITS; - tts = bs->opaque; - - null_bs = bdrv_open("null-co://", NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL, - &error_abort); - bdrv_attach_child(bs, null_bs, "null-child", &child_of_bds, - BDRV_CHILD_DATA, &error_abort); - - /* This child will be the one to pass to requests through to, and - * it will stall until a drain occurs */ - child_bs = bdrv_new_open_driver(&bdrv_test, "child", BDRV_O_RDWR, - &error_abort); - child_bs->total_sectors = 65536 >> BDRV_SECTOR_BITS; - /* Takes our reference to child_bs */ - tts->wait_child = bdrv_attach_child(bs, child_bs, "wait-child", - &child_of_bds, - BDRV_CHILD_DATA | BDRV_CHILD_PRIMARY, - &error_abort); - - /* This child is just there to be deleted - * (for detach_instead_of_delete == true) */ - null_bs = bdrv_open("null-co://", NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL, - &error_abort); - bdrv_attach_child(bs, null_bs, "null-child", &child_of_bds, BDRV_CHILD_DATA, - &error_abort); - - blk = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL); - blk_insert_bs(blk, bs, &error_abort); - - /* Referenced by blk now */ - bdrv_unref(bs); - - g_assert_cmpint(bs->refcnt, ==, 1); - g_assert_cmpint(child_bs->refcnt, ==, 1); - g_assert_cmpint(null_bs->refcnt, ==, 1); - - - dbdd = (TestCoDeleteByDrainData){ - .blk = blk, - .detach_instead_of_delete = detach_instead_of_delete, - .done = false, - }; - co = qemu_coroutine_create(test_co_delete_by_drain, &dbdd); - qemu_coroutine_enter(co); - - /* Drain the child while the read operation is still pending. - * This should result in the operation finishing and - * test_co_delete_by_drain() resuming. Thus, @bs will be deleted - * and the coroutine will exit while this drain operation is still - * in progress. */ - switch (drain_type) { - case BDRV_DRAIN: - bdrv_ref(child_bs); - bdrv_drain(child_bs); - bdrv_unref(child_bs); - break; - case BDRV_SUBTREE_DRAIN: - /* Would have to ref/unref bs here for !detach_instead_of_delete, but - * then the whole test becomes pointless because the graph changes - * don't occur during the drain any more. */ - assert(detach_instead_of_delete); - bdrv_subtree_drained_begin(bs); - bdrv_subtree_drained_end(bs); - break; - case BDRV_DRAIN_ALL: - bdrv_drain_all_begin(); - bdrv_drain_all_end(); - break; - default: - g_assert_not_reached(); - } - - while (!dbdd.done) { - aio_poll(qemu_get_aio_context(), true); - } - - if (detach_instead_of_delete) { - /* Here, the reference has not passed over to the coroutine, - * so we have to delete the BB ourselves */ - blk_unref(blk); - } -} - -static void test_delete_by_drain(void) -{ - do_test_delete_by_drain(false, BDRV_DRAIN); -} - -static void test_detach_by_drain_all(void) -{ - do_test_delete_by_drain(true, BDRV_DRAIN_ALL); -} - -static void test_detach_by_drain(void) -{ - do_test_delete_by_drain(true, BDRV_DRAIN); -} - -static void test_detach_by_drain_subtree(void) -{ - do_test_delete_by_drain(true, BDRV_SUBTREE_DRAIN); -} - - -struct detach_by_parent_data { - BlockDriverState *parent_b; - BdrvChild *child_b; - BlockDriverState *c; - BdrvChild *child_c; - bool by_parent_cb; -}; -static struct detach_by_parent_data detach_by_parent_data; - -static void detach_indirect_bh(void *opaque) -{ - struct detach_by_parent_data *data = opaque; - - bdrv_unref_child(data->parent_b, data->child_b); - - bdrv_ref(data->c); - data->child_c = bdrv_attach_child(data->parent_b, data->c, "PB-C", - &child_of_bds, BDRV_CHILD_DATA, - &error_abort); -} - -static void detach_by_parent_aio_cb(void *opaque, int ret) -{ - struct detach_by_parent_data *data = &detach_by_parent_data; - - g_assert_cmpint(ret, ==, 0); - if (data->by_parent_cb) { - detach_indirect_bh(data); - } -} - -static void detach_by_driver_cb_drained_begin(BdrvChild *child) -{ - aio_bh_schedule_oneshot(qemu_get_current_aio_context(), - detach_indirect_bh, &detach_by_parent_data); - child_of_bds.drained_begin(child); -} - -static BdrvChildClass detach_by_driver_cb_class; - -/* - * Initial graph: - * - * PA PB - * \ / \ - * A B C - * - * by_parent_cb == true: Test that parent callbacks don't poll - * - * PA has a pending write request whose callback changes the child nodes of - * PB: It removes B and adds C instead. The subtree of PB is drained, which - * will indirectly drain the write request, too. - * - * by_parent_cb == false: Test that bdrv_drain_invoke() doesn't poll - * - * PA's BdrvChildClass has a .drained_begin callback that schedules a BH - * that does the same graph change. If bdrv_drain_invoke() calls it, the - * state is messed up, but if it is only polled in the single - * BDRV_POLL_WHILE() at the end of the drain, this should work fine. - */ -static void test_detach_indirect(bool by_parent_cb) -{ - BlockBackend *blk; - BlockDriverState *parent_a, *parent_b, *a, *b, *c; - BdrvChild *child_a, *child_b; - BlockAIOCB *acb; - - QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, NULL, 0); - - if (!by_parent_cb) { - detach_by_driver_cb_class = child_of_bds; - detach_by_driver_cb_class.drained_begin = - detach_by_driver_cb_drained_begin; - } - - /* Create all involved nodes */ - parent_a = bdrv_new_open_driver(&bdrv_test, "parent-a", BDRV_O_RDWR, - &error_abort); - parent_b = bdrv_new_open_driver(&bdrv_test, "parent-b", 0, - &error_abort); - - a = bdrv_new_open_driver(&bdrv_test, "a", BDRV_O_RDWR, &error_abort); - b = bdrv_new_open_driver(&bdrv_test, "b", BDRV_O_RDWR, &error_abort); - c = bdrv_new_open_driver(&bdrv_test, "c", BDRV_O_RDWR, &error_abort); - - /* blk is a BB for parent-a */ - blk = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL); - blk_insert_bs(blk, parent_a, &error_abort); - bdrv_unref(parent_a); - - /* If we want to get bdrv_drain_invoke() to call aio_poll(), the driver - * callback must not return immediately. */ - if (!by_parent_cb) { - BDRVTestState *s = parent_a->opaque; - s->sleep_in_drain_begin = true; - } - - /* Set child relationships */ - bdrv_ref(b); - bdrv_ref(a); - child_b = bdrv_attach_child(parent_b, b, "PB-B", &child_of_bds, - BDRV_CHILD_DATA, &error_abort); - child_a = bdrv_attach_child(parent_b, a, "PB-A", &child_of_bds, - BDRV_CHILD_COW, &error_abort); - - bdrv_ref(a); - bdrv_attach_child(parent_a, a, "PA-A", - by_parent_cb ? &child_of_bds : &detach_by_driver_cb_class, - BDRV_CHILD_DATA, &error_abort); - - g_assert_cmpint(parent_a->refcnt, ==, 1); - g_assert_cmpint(parent_b->refcnt, ==, 1); - g_assert_cmpint(a->refcnt, ==, 3); - g_assert_cmpint(b->refcnt, ==, 2); - g_assert_cmpint(c->refcnt, ==, 1); - - g_assert(QLIST_FIRST(&parent_b->children) == child_a); - g_assert(QLIST_NEXT(child_a, next) == child_b); - g_assert(QLIST_NEXT(child_b, next) == NULL); - - /* Start the evil write request */ - detach_by_parent_data = (struct detach_by_parent_data) { - .parent_b = parent_b, - .child_b = child_b, - .c = c, - .by_parent_cb = by_parent_cb, - }; - acb = blk_aio_preadv(blk, 0, &qiov, 0, detach_by_parent_aio_cb, NULL); - g_assert(acb != NULL); - - /* Drain and check the expected result */ - bdrv_subtree_drained_begin(parent_b); - - g_assert(detach_by_parent_data.child_c != NULL); - - g_assert_cmpint(parent_a->refcnt, ==, 1); - g_assert_cmpint(parent_b->refcnt, ==, 1); - g_assert_cmpint(a->refcnt, ==, 3); - g_assert_cmpint(b->refcnt, ==, 1); - g_assert_cmpint(c->refcnt, ==, 2); - - g_assert(QLIST_FIRST(&parent_b->children) == detach_by_parent_data.child_c); - g_assert(QLIST_NEXT(detach_by_parent_data.child_c, next) == child_a); - g_assert(QLIST_NEXT(child_a, next) == NULL); - - g_assert_cmpint(parent_a->quiesce_counter, ==, 1); - g_assert_cmpint(parent_b->quiesce_counter, ==, 1); - g_assert_cmpint(a->quiesce_counter, ==, 1); - g_assert_cmpint(b->quiesce_counter, ==, 0); - g_assert_cmpint(c->quiesce_counter, ==, 1); - - bdrv_subtree_drained_end(parent_b); - - bdrv_unref(parent_b); - blk_unref(blk); - - g_assert_cmpint(a->refcnt, ==, 1); - g_assert_cmpint(b->refcnt, ==, 1); - g_assert_cmpint(c->refcnt, ==, 1); - bdrv_unref(a); - bdrv_unref(b); - bdrv_unref(c); -} - -static void test_detach_by_parent_cb(void) -{ - test_detach_indirect(true); -} - -static void test_detach_by_driver_cb(void) -{ - test_detach_indirect(false); -} - -static void test_append_to_drained(void) -{ - BlockBackend *blk; - BlockDriverState *base, *overlay; - BDRVTestState *base_s, *overlay_s; - - blk = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL); - base = bdrv_new_open_driver(&bdrv_test, "base", BDRV_O_RDWR, &error_abort); - base_s = base->opaque; - blk_insert_bs(blk, base, &error_abort); - - overlay = bdrv_new_open_driver(&bdrv_test, "overlay", BDRV_O_RDWR, - &error_abort); - overlay_s = overlay->opaque; - - do_drain_begin(BDRV_DRAIN, base); - g_assert_cmpint(base->quiesce_counter, ==, 1); - g_assert_cmpint(base_s->drain_count, ==, 1); - g_assert_cmpint(base->in_flight, ==, 0); - - /* Takes ownership of overlay, so we don't have to unref it later */ - bdrv_append(overlay, base, &error_abort); - g_assert_cmpint(base->in_flight, ==, 0); - g_assert_cmpint(overlay->in_flight, ==, 0); - - g_assert_cmpint(base->quiesce_counter, ==, 1); - g_assert_cmpint(base_s->drain_count, ==, 1); - g_assert_cmpint(overlay->quiesce_counter, ==, 1); - g_assert_cmpint(overlay_s->drain_count, ==, 1); - - do_drain_end(BDRV_DRAIN, base); - - g_assert_cmpint(base->quiesce_counter, ==, 0); - g_assert_cmpint(base_s->drain_count, ==, 0); - g_assert_cmpint(overlay->quiesce_counter, ==, 0); - g_assert_cmpint(overlay_s->drain_count, ==, 0); - - bdrv_unref(base); - blk_unref(blk); -} - -static void test_set_aio_context(void) -{ - BlockDriverState *bs; - IOThread *a = iothread_new(); - IOThread *b = iothread_new(); - AioContext *ctx_a = iothread_get_aio_context(a); - AioContext *ctx_b = iothread_get_aio_context(b); - - bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR, - &error_abort); - - bdrv_drained_begin(bs); - bdrv_try_set_aio_context(bs, ctx_a, &error_abort); - - aio_context_acquire(ctx_a); - bdrv_drained_end(bs); - - bdrv_drained_begin(bs); - bdrv_try_set_aio_context(bs, ctx_b, &error_abort); - aio_context_release(ctx_a); - aio_context_acquire(ctx_b); - bdrv_try_set_aio_context(bs, qemu_get_aio_context(), &error_abort); - aio_context_release(ctx_b); - bdrv_drained_end(bs); - - bdrv_unref(bs); - iothread_join(a); - iothread_join(b); -} - - -typedef struct TestDropBackingBlockJob { - BlockJob common; - bool should_complete; - bool *did_complete; - BlockDriverState *detach_also; -} TestDropBackingBlockJob; - -static int coroutine_fn test_drop_backing_job_run(Job *job, Error **errp) -{ - TestDropBackingBlockJob *s = - container_of(job, TestDropBackingBlockJob, common.job); - - while (!s->should_complete) { - job_sleep_ns(job, 0); - } - - return 0; -} - -static void test_drop_backing_job_commit(Job *job) -{ - TestDropBackingBlockJob *s = - container_of(job, TestDropBackingBlockJob, common.job); - - bdrv_set_backing_hd(blk_bs(s->common.blk), NULL, &error_abort); - bdrv_set_backing_hd(s->detach_also, NULL, &error_abort); - - *s->did_complete = true; -} - -static const BlockJobDriver test_drop_backing_job_driver = { - .job_driver = { - .instance_size = sizeof(TestDropBackingBlockJob), - .free = block_job_free, - .user_resume = block_job_user_resume, - .run = test_drop_backing_job_run, - .commit = test_drop_backing_job_commit, - } -}; - -/** - * Creates a child node with three parent nodes on it, and then runs a - * block job on the final one, parent-node-2. - * - * The job is then asked to complete before a section where the child - * is drained. - * - * Ending this section will undrain the child's parents, first - * parent-node-2, then parent-node-1, then parent-node-0 -- the parent - * list is in reverse order of how they were added. Ending the drain - * on parent-node-2 will resume the job, thus completing it and - * scheduling job_exit(). - * - * Ending the drain on parent-node-1 will poll the AioContext, which - * lets job_exit() and thus test_drop_backing_job_commit() run. That - * function first removes the child as parent-node-2's backing file. - * - * In old (and buggy) implementations, there are two problems with - * that: - * (A) bdrv_drain_invoke() polls for every node that leaves the - * drained section. This means that job_exit() is scheduled - * before the child has left the drained section. Its - * quiesce_counter is therefore still 1 when it is removed from - * parent-node-2. - * - * (B) bdrv_replace_child_noperm() calls drained_end() on the old - * child's parents as many times as the child is quiesced. This - * means it will call drained_end() on parent-node-2 once. - * Because parent-node-2 is no longer quiesced at this point, this - * will fail. - * - * bdrv_replace_child_noperm() therefore must call drained_end() on - * the parent only if it really is still drained because the child is - * drained. - * - * If removing child from parent-node-2 was successful (as it should - * be), test_drop_backing_job_commit() will then also remove the child - * from parent-node-0. - * - * With an old version of our drain infrastructure ((A) above), that - * resulted in the following flow: - * - * 1. child attempts to leave its drained section. The call recurses - * to its parents. - * - * 2. parent-node-2 leaves the drained section. Polling in - * bdrv_drain_invoke() will schedule job_exit(). - * - * 3. parent-node-1 leaves the drained section. Polling in - * bdrv_drain_invoke() will run job_exit(), thus disconnecting - * parent-node-0 from the child node. - * - * 4. bdrv_parent_drained_end() uses a QLIST_FOREACH_SAFE() loop to - * iterate over the parents. Thus, it now accesses the BdrvChild - * object that used to connect parent-node-0 and the child node. - * However, that object no longer exists, so it accesses a dangling - * pointer. - * - * The solution is to only poll once when running a bdrv_drained_end() - * operation, specifically at the end when all drained_end() - * operations for all involved nodes have been scheduled. - * Note that this also solves (A) above, thus hiding (B). - */ -static void test_blockjob_commit_by_drained_end(void) -{ - BlockDriverState *bs_child, *bs_parents[3]; - TestDropBackingBlockJob *job; - bool job_has_completed = false; - int i; - - bs_child = bdrv_new_open_driver(&bdrv_test, "child-node", BDRV_O_RDWR, - &error_abort); - - for (i = 0; i < 3; i++) { - char name[32]; - snprintf(name, sizeof(name), "parent-node-%i", i); - bs_parents[i] = bdrv_new_open_driver(&bdrv_test, name, BDRV_O_RDWR, - &error_abort); - bdrv_set_backing_hd(bs_parents[i], bs_child, &error_abort); - } - - job = block_job_create("job", &test_drop_backing_job_driver, NULL, - bs_parents[2], 0, BLK_PERM_ALL, 0, 0, NULL, NULL, - &error_abort); - - job->detach_also = bs_parents[0]; - job->did_complete = &job_has_completed; - - job_start(&job->common.job); - - job->should_complete = true; - bdrv_drained_begin(bs_child); - g_assert(!job_has_completed); - bdrv_drained_end(bs_child); - g_assert(job_has_completed); - - bdrv_unref(bs_parents[0]); - bdrv_unref(bs_parents[1]); - bdrv_unref(bs_parents[2]); - bdrv_unref(bs_child); -} - - -typedef struct TestSimpleBlockJob { - BlockJob common; - bool should_complete; - bool *did_complete; -} TestSimpleBlockJob; - -static int coroutine_fn test_simple_job_run(Job *job, Error **errp) -{ - TestSimpleBlockJob *s = container_of(job, TestSimpleBlockJob, common.job); - - while (!s->should_complete) { - job_sleep_ns(job, 0); - } - - return 0; -} - -static void test_simple_job_clean(Job *job) -{ - TestSimpleBlockJob *s = container_of(job, TestSimpleBlockJob, common.job); - *s->did_complete = true; -} - -static const BlockJobDriver test_simple_job_driver = { - .job_driver = { - .instance_size = sizeof(TestSimpleBlockJob), - .free = block_job_free, - .user_resume = block_job_user_resume, - .run = test_simple_job_run, - .clean = test_simple_job_clean, - }, -}; - -static int drop_intermediate_poll_update_filename(BdrvChild *child, - BlockDriverState *new_base, - const char *filename, - Error **errp) -{ - /* - * We are free to poll here, which may change the block graph, if - * it is not drained. - */ - - /* If the job is not drained: Complete it, schedule job_exit() */ - aio_poll(qemu_get_current_aio_context(), false); - /* If the job is not drained: Run job_exit(), finish the job */ - aio_poll(qemu_get_current_aio_context(), false); - - return 0; -} - -/** - * Test a poll in the midst of bdrv_drop_intermediate(). - * - * bdrv_drop_intermediate() calls BdrvChildClass.update_filename(), - * which can yield or poll. This may lead to graph changes, unless - * the whole subtree in question is drained. - * - * We test this on the following graph: - * - * Job - * - * | - * job-node - * | - * v - * - * job-node - * - * | - * backing - * | - * v - * - * node-2 --chain--> node-1 --chain--> node-0 - * - * We drop node-1 with bdrv_drop_intermediate(top=node-1, base=node-0). - * - * This first updates node-2's backing filename by invoking - * drop_intermediate_poll_update_filename(), which polls twice. This - * causes the job to finish, which in turns causes the job-node to be - * deleted. - * - * bdrv_drop_intermediate() uses a QLIST_FOREACH_SAFE() loop, so it - * already has a pointer to the BdrvChild edge between job-node and - * node-1. When it tries to handle that edge, we probably get a - * segmentation fault because the object no longer exists. - * - * - * The solution is for bdrv_drop_intermediate() to drain top's - * subtree. This prevents graph changes from happening just because - * BdrvChildClass.update_filename() yields or polls. Thus, the block - * job is paused during that drained section and must finish before or - * after. - * - * (In addition, bdrv_replace_child() must keep the job paused.) - */ -static void test_drop_intermediate_poll(void) -{ - static BdrvChildClass chain_child_class; - BlockDriverState *chain[3]; - TestSimpleBlockJob *job; - BlockDriverState *job_node; - bool job_has_completed = false; - int i; - int ret; - - chain_child_class = child_of_bds; - chain_child_class.update_filename = drop_intermediate_poll_update_filename; - - for (i = 0; i < 3; i++) { - char name[32]; - snprintf(name, 32, "node-%i", i); - - chain[i] = bdrv_new_open_driver(&bdrv_test, name, 0, &error_abort); - } - - job_node = bdrv_new_open_driver(&bdrv_test, "job-node", BDRV_O_RDWR, - &error_abort); - bdrv_set_backing_hd(job_node, chain[1], &error_abort); - - /* - * Establish the chain last, so the chain links are the first - * elements in the BDS.parents lists - */ - for (i = 0; i < 3; i++) { - if (i) { - /* Takes the reference to chain[i - 1] */ - chain[i]->backing = bdrv_attach_child(chain[i], chain[i - 1], - "chain", &chain_child_class, - BDRV_CHILD_COW, &error_abort); - } - } - - job = block_job_create("job", &test_simple_job_driver, NULL, job_node, - 0, BLK_PERM_ALL, 0, 0, NULL, NULL, &error_abort); - - /* The job has a reference now */ - bdrv_unref(job_node); - - job->did_complete = &job_has_completed; - - job_start(&job->common.job); - job->should_complete = true; - - g_assert(!job_has_completed); - ret = bdrv_drop_intermediate(chain[1], chain[0], NULL); - g_assert(ret == 0); - g_assert(job_has_completed); - - bdrv_unref(chain[2]); -} - - -typedef struct BDRVReplaceTestState { - bool was_drained; - bool was_undrained; - bool has_read; - - int drain_count; - - bool yield_before_read; - Coroutine *io_co; - Coroutine *drain_co; -} BDRVReplaceTestState; - -static void bdrv_replace_test_close(BlockDriverState *bs) -{ -} - -/** - * If @bs has a backing file: - * Yield if .yield_before_read is true (and wait for drain_begin to - * wake us up). - * Forward the read to bs->backing. Set .has_read to true. - * If drain_begin has woken us, wake it in turn. - * - * Otherwise: - * Set .has_read to true and return success. - */ -static int coroutine_fn bdrv_replace_test_co_preadv(BlockDriverState *bs, - uint64_t offset, - uint64_t bytes, - QEMUIOVector *qiov, - int flags) -{ - BDRVReplaceTestState *s = bs->opaque; - - if (bs->backing) { - int ret; - - g_assert(!s->drain_count); - - s->io_co = qemu_coroutine_self(); - if (s->yield_before_read) { - s->yield_before_read = false; - qemu_coroutine_yield(); - } - s->io_co = NULL; - - ret = bdrv_co_preadv(bs->backing, offset, bytes, qiov, 0); - s->has_read = true; - - /* Wake up drain_co if it runs */ - if (s->drain_co) { - aio_co_wake(s->drain_co); - } - - return ret; - } - - s->has_read = true; - return 0; -} - -/** - * If .drain_count is 0, wake up .io_co if there is one; and set - * .was_drained. - * Increment .drain_count. - */ -static void coroutine_fn bdrv_replace_test_co_drain_begin(BlockDriverState *bs) -{ - BDRVReplaceTestState *s = bs->opaque; - - if (!s->drain_count) { - /* Keep waking io_co up until it is done */ - s->drain_co = qemu_coroutine_self(); - while (s->io_co) { - aio_co_wake(s->io_co); - s->io_co = NULL; - qemu_coroutine_yield(); - } - s->drain_co = NULL; - - s->was_drained = true; - } - s->drain_count++; -} - -/** - * Reduce .drain_count, set .was_undrained once it reaches 0. - * If .drain_count reaches 0 and the node has a backing file, issue a - * read request. - */ -static void coroutine_fn bdrv_replace_test_co_drain_end(BlockDriverState *bs) -{ - BDRVReplaceTestState *s = bs->opaque; - - g_assert(s->drain_count > 0); - if (!--s->drain_count) { - int ret; - - s->was_undrained = true; - - if (bs->backing) { - char data; - QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, &data, 1); - - /* Queue a read request post-drain */ - ret = bdrv_replace_test_co_preadv(bs, 0, 1, &qiov, 0); - g_assert(ret >= 0); - } - } -} - -static BlockDriver bdrv_replace_test = { - .format_name = "replace_test", - .instance_size = sizeof(BDRVReplaceTestState), - - .bdrv_close = bdrv_replace_test_close, - .bdrv_co_preadv = bdrv_replace_test_co_preadv, - - .bdrv_co_drain_begin = bdrv_replace_test_co_drain_begin, - .bdrv_co_drain_end = bdrv_replace_test_co_drain_end, - - .bdrv_child_perm = bdrv_default_perms, -}; - -static void coroutine_fn test_replace_child_mid_drain_read_co(void *opaque) -{ - int ret; - char data; - - ret = blk_co_pread(opaque, 0, 1, &data, 0); - g_assert(ret >= 0); -} - -/** - * We test two things: - * (1) bdrv_replace_child_noperm() must not undrain the parent if both - * children are drained. - * (2) bdrv_replace_child_noperm() must never flush I/O requests to a - * drained child. If the old child is drained, it must flush I/O - * requests after the new one has been attached. If the new child - * is drained, it must flush I/O requests before the old one is - * detached. - * - * To do so, we create one parent node and two child nodes; then - * attach one of the children (old_child_bs) to the parent, then - * drain both old_child_bs and new_child_bs according to - * old_drain_count and new_drain_count, respectively, and finally - * we invoke bdrv_replace_node() to replace old_child_bs by - * new_child_bs. - * - * The test block driver we use here (bdrv_replace_test) has a read - * function that: - * - For the parent node, can optionally yield, and then forwards the - * read to bdrv_preadv(), - * - For the child node, just returns immediately. - * - * If the read yields, the drain_begin function will wake it up. - * - * The drain_end function issues a read on the parent once it is fully - * undrained (which simulates requests starting to come in again). - */ -static void do_test_replace_child_mid_drain(int old_drain_count, - int new_drain_count) -{ - BlockBackend *parent_blk; - BlockDriverState *parent_bs; - BlockDriverState *old_child_bs, *new_child_bs; - BDRVReplaceTestState *parent_s; - BDRVReplaceTestState *old_child_s, *new_child_s; - Coroutine *io_co; - int i; - - parent_bs = bdrv_new_open_driver(&bdrv_replace_test, "parent", 0, - &error_abort); - parent_s = parent_bs->opaque; - - parent_blk = blk_new(qemu_get_aio_context(), - BLK_PERM_CONSISTENT_READ, BLK_PERM_ALL); - blk_insert_bs(parent_blk, parent_bs, &error_abort); - - old_child_bs = bdrv_new_open_driver(&bdrv_replace_test, "old-child", 0, - &error_abort); - new_child_bs = bdrv_new_open_driver(&bdrv_replace_test, "new-child", 0, - &error_abort); - old_child_s = old_child_bs->opaque; - new_child_s = new_child_bs->opaque; - - /* So that we can read something */ - parent_bs->total_sectors = 1; - old_child_bs->total_sectors = 1; - new_child_bs->total_sectors = 1; - - bdrv_ref(old_child_bs); - parent_bs->backing = bdrv_attach_child(parent_bs, old_child_bs, "child", - &child_of_bds, BDRV_CHILD_COW, - &error_abort); - - for (i = 0; i < old_drain_count; i++) { - bdrv_drained_begin(old_child_bs); - } - for (i = 0; i < new_drain_count; i++) { - bdrv_drained_begin(new_child_bs); - } - - if (!old_drain_count) { - /* - * Start a read operation that will yield, so it will not - * complete before the node is drained. - */ - parent_s->yield_before_read = true; - io_co = qemu_coroutine_create(test_replace_child_mid_drain_read_co, - parent_blk); - qemu_coroutine_enter(io_co); - } - - /* If we have started a read operation, it should have yielded */ - g_assert(!parent_s->has_read); - - /* Reset drained status so we can see what bdrv_replace_node() does */ - parent_s->was_drained = false; - parent_s->was_undrained = false; - - g_assert(parent_bs->quiesce_counter == old_drain_count); - bdrv_replace_node(old_child_bs, new_child_bs, &error_abort); - g_assert(parent_bs->quiesce_counter == new_drain_count); - - if (!old_drain_count && !new_drain_count) { - /* - * From undrained to undrained drains and undrains the parent, - * because bdrv_replace_node() contains a drained section for - * @old_child_bs. - */ - g_assert(parent_s->was_drained && parent_s->was_undrained); - } else if (!old_drain_count && new_drain_count) { - /* - * From undrained to drained should drain the parent and keep - * it that way. - */ - g_assert(parent_s->was_drained && !parent_s->was_undrained); - } else if (old_drain_count && !new_drain_count) { - /* - * From drained to undrained should undrain the parent and - * keep it that way. - */ - g_assert(!parent_s->was_drained && parent_s->was_undrained); - } else /* if (old_drain_count && new_drain_count) */ { - /* - * From drained to drained must not undrain the parent at any - * point - */ - g_assert(!parent_s->was_drained && !parent_s->was_undrained); - } - - if (!old_drain_count || !new_drain_count) { - /* - * If !old_drain_count, we have started a read request before - * bdrv_replace_node(). If !new_drain_count, the parent must - * have been undrained at some point, and - * bdrv_replace_test_co_drain_end() starts a read request - * then. - */ - g_assert(parent_s->has_read); - } else { - /* - * If the parent was never undrained, there is no way to start - * a read request. - */ - g_assert(!parent_s->has_read); - } - - /* A drained child must have not received any request */ - g_assert(!(old_drain_count && old_child_s->has_read)); - g_assert(!(new_drain_count && new_child_s->has_read)); - - for (i = 0; i < new_drain_count; i++) { - bdrv_drained_end(new_child_bs); - } - for (i = 0; i < old_drain_count; i++) { - bdrv_drained_end(old_child_bs); - } - - /* - * By now, bdrv_replace_test_co_drain_end() must have been called - * at some point while the new child was attached to the parent. - */ - g_assert(parent_s->has_read); - g_assert(new_child_s->has_read); - - blk_unref(parent_blk); - bdrv_unref(parent_bs); - bdrv_unref(old_child_bs); - bdrv_unref(new_child_bs); -} - -static void test_replace_child_mid_drain(void) -{ - int old_drain_count, new_drain_count; - - for (old_drain_count = 0; old_drain_count < 2; old_drain_count++) { - for (new_drain_count = 0; new_drain_count < 2; new_drain_count++) { - do_test_replace_child_mid_drain(old_drain_count, new_drain_count); - } - } -} - -int main(int argc, char **argv) -{ - int ret; - - bdrv_init(); - qemu_init_main_loop(&error_abort); - - g_test_init(&argc, &argv, NULL); - qemu_event_init(&done_event, false); - - g_test_add_func("/bdrv-drain/driver-cb/drain_all", test_drv_cb_drain_all); - g_test_add_func("/bdrv-drain/driver-cb/drain", test_drv_cb_drain); - g_test_add_func("/bdrv-drain/driver-cb/drain_subtree", - test_drv_cb_drain_subtree); - - g_test_add_func("/bdrv-drain/driver-cb/co/drain_all", - test_drv_cb_co_drain_all); - g_test_add_func("/bdrv-drain/driver-cb/co/drain", test_drv_cb_co_drain); - g_test_add_func("/bdrv-drain/driver-cb/co/drain_subtree", - test_drv_cb_co_drain_subtree); - - - g_test_add_func("/bdrv-drain/quiesce/drain_all", test_quiesce_drain_all); - g_test_add_func("/bdrv-drain/quiesce/drain", test_quiesce_drain); - g_test_add_func("/bdrv-drain/quiesce/drain_subtree", - test_quiesce_drain_subtree); - - g_test_add_func("/bdrv-drain/quiesce/co/drain_all", - test_quiesce_co_drain_all); - g_test_add_func("/bdrv-drain/quiesce/co/drain", test_quiesce_co_drain); - g_test_add_func("/bdrv-drain/quiesce/co/drain_subtree", - test_quiesce_co_drain_subtree); - - g_test_add_func("/bdrv-drain/nested", test_nested); - g_test_add_func("/bdrv-drain/multiparent", test_multiparent); - - g_test_add_func("/bdrv-drain/graph-change/drain_subtree", - test_graph_change_drain_subtree); - g_test_add_func("/bdrv-drain/graph-change/drain_all", - test_graph_change_drain_all); - - g_test_add_func("/bdrv-drain/iothread/drain_all", test_iothread_drain_all); - g_test_add_func("/bdrv-drain/iothread/drain", test_iothread_drain); - g_test_add_func("/bdrv-drain/iothread/drain_subtree", - test_iothread_drain_subtree); - - g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all); - g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain); - g_test_add_func("/bdrv-drain/blockjob/drain_subtree", - test_blockjob_drain_subtree); - - g_test_add_func("/bdrv-drain/blockjob/error/drain_all", - test_blockjob_error_drain_all); - g_test_add_func("/bdrv-drain/blockjob/error/drain", - test_blockjob_error_drain); - g_test_add_func("/bdrv-drain/blockjob/error/drain_subtree", - test_blockjob_error_drain_subtree); - - g_test_add_func("/bdrv-drain/blockjob/iothread/drain_all", - test_blockjob_iothread_drain_all); - g_test_add_func("/bdrv-drain/blockjob/iothread/drain", - test_blockjob_iothread_drain); - g_test_add_func("/bdrv-drain/blockjob/iothread/drain_subtree", - test_blockjob_iothread_drain_subtree); - - g_test_add_func("/bdrv-drain/blockjob/iothread/error/drain_all", - test_blockjob_iothread_error_drain_all); - g_test_add_func("/bdrv-drain/blockjob/iothread/error/drain", - test_blockjob_iothread_error_drain); - g_test_add_func("/bdrv-drain/blockjob/iothread/error/drain_subtree", - test_blockjob_iothread_error_drain_subtree); - - g_test_add_func("/bdrv-drain/deletion/drain", test_delete_by_drain); - g_test_add_func("/bdrv-drain/detach/drain_all", test_detach_by_drain_all); - g_test_add_func("/bdrv-drain/detach/drain", test_detach_by_drain); - g_test_add_func("/bdrv-drain/detach/drain_subtree", test_detach_by_drain_subtree); - g_test_add_func("/bdrv-drain/detach/parent_cb", test_detach_by_parent_cb); - g_test_add_func("/bdrv-drain/detach/driver_cb", test_detach_by_driver_cb); - - g_test_add_func("/bdrv-drain/attach/drain", test_append_to_drained); - - g_test_add_func("/bdrv-drain/set_aio_context", test_set_aio_context); - - g_test_add_func("/bdrv-drain/blockjob/commit_by_drained_end", - test_blockjob_commit_by_drained_end); - - g_test_add_func("/bdrv-drain/bdrv_drop_intermediate/poll", - test_drop_intermediate_poll); - - g_test_add_func("/bdrv-drain/replace_child/mid-drain", - test_replace_child_mid_drain); - - ret = g_test_run(); - qemu_event_destroy(&done_event); - return ret; -} diff --git a/tests/test-bdrv-graph-mod.c b/tests/test-bdrv-graph-mod.c deleted file mode 100644 index c4f7d16039..0000000000 --- a/tests/test-bdrv-graph-mod.c +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Block node graph modifications tests - * - * Copyright (c) 2019 Virtuozzo International GmbH. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "qemu/main-loop.h" -#include "block/block_int.h" -#include "sysemu/block-backend.h" - -static BlockDriver bdrv_pass_through = { - .format_name = "pass-through", - .bdrv_child_perm = bdrv_default_perms, -}; - -static void no_perm_default_perms(BlockDriverState *bs, BdrvChild *c, - BdrvChildRole role, - BlockReopenQueue *reopen_queue, - uint64_t perm, uint64_t shared, - uint64_t *nperm, uint64_t *nshared) -{ - *nperm = 0; - *nshared = BLK_PERM_ALL; -} - -static BlockDriver bdrv_no_perm = { - .format_name = "no-perm", - .bdrv_child_perm = no_perm_default_perms, -}; - -static BlockDriverState *no_perm_node(const char *name) -{ - return bdrv_new_open_driver(&bdrv_no_perm, name, BDRV_O_RDWR, &error_abort); -} - -static BlockDriverState *pass_through_node(const char *name) -{ - return bdrv_new_open_driver(&bdrv_pass_through, name, - BDRV_O_RDWR, &error_abort); -} - -/* - * test_update_perm_tree - * - * When checking node for a possibility to update permissions, it's subtree - * should be correctly checked too. New permissions for each node should be - * calculated and checked in context of permissions of other nodes. If we - * check new permissions of the node only in context of old permissions of - * its neighbors, we can finish up with wrong permission graph. - * - * This test firstly create the following graph: - * +--------+ - * | root | - * +--------+ - * | - * | perm: write, read - * | shared: except write - * v - * +-------------------+ +----------------+ - * | passtrough filter |---------->| null-co node | - * +-------------------+ +----------------+ - * - * - * and then, tries to append filter under node. Expected behavior: fail. - * Otherwise we'll get the following picture, with two BdrvChild'ren, having - * write permission to one node, without actually sharing it. - * - * +--------+ - * | root | - * +--------+ - * | - * | perm: write, read - * | shared: except write - * v - * +-------------------+ - * | passtrough filter | - * +-------------------+ - * | | - * perm: write, read | | perm: write, read - * shared: except write | | shared: except write - * v v - * +----------------+ - * | null co node | - * +----------------+ - */ -static void test_update_perm_tree(void) -{ - int ret; - - BlockBackend *root = blk_new(qemu_get_aio_context(), - BLK_PERM_WRITE | BLK_PERM_CONSISTENT_READ, - BLK_PERM_ALL & ~BLK_PERM_WRITE); - BlockDriverState *bs = no_perm_node("node"); - BlockDriverState *filter = pass_through_node("filter"); - - blk_insert_bs(root, bs, &error_abort); - - bdrv_attach_child(filter, bs, "child", &child_of_bds, - BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, &error_abort); - - ret = bdrv_append(filter, bs, NULL); - g_assert_cmpint(ret, <, 0); - - blk_unref(root); -} - -/* - * test_should_update_child - * - * Test that bdrv_replace_node, and concretely should_update_child - * do the right thing, i.e. not creating loops on the graph. - * - * The test does the following: - * 1. initial graph: - * - * +------+ +--------+ - * | root | | filter | - * +------+ +--------+ - * | | - * root| target| - * v v - * +------+ +--------+ - * | node |<---------| target | - * +------+ backing +--------+ - * - * 2. Append @filter above @node. If should_update_child works correctly, - * it understands, that backing child of @target should not be updated, - * as it will create a loop on node graph. Resulting picture should - * be the left one, not the right: - * - * +------+ +------+ - * | root | | root | - * +------+ +------+ - * | | - * root| root| - * v v - * +--------+ target +--------+ target - * | filter |--------------+ | filter |--------------+ - * +--------+ | +--------+ | - * | | | ^ v - * backing| | backing| | +--------+ - * v v | +-----------| target | - * +------+ +--------+ v backing +--------+ - * | node |<---------| target | +------+ - * +------+ backing +--------+ | node | - * +------+ - * - * (good picture) (bad picture) - * - */ -static void test_should_update_child(void) -{ - BlockBackend *root = blk_new(qemu_get_aio_context(), 0, BLK_PERM_ALL); - BlockDriverState *bs = no_perm_node("node"); - BlockDriverState *filter = no_perm_node("filter"); - BlockDriverState *target = no_perm_node("target"); - - blk_insert_bs(root, bs, &error_abort); - - bdrv_set_backing_hd(target, bs, &error_abort); - - g_assert(target->backing->bs == bs); - bdrv_attach_child(filter, target, "target", &child_of_bds, - BDRV_CHILD_DATA, &error_abort); - bdrv_append(filter, bs, &error_abort); - g_assert(target->backing->bs == bs); - - bdrv_unref(bs); - blk_unref(root); -} - -int main(int argc, char *argv[]) -{ - bdrv_init(); - qemu_init_main_loop(&error_abort); - - g_test_init(&argc, &argv, NULL); - - g_test_add_func("/bdrv-graph-mod/update-perm-tree", test_update_perm_tree); - g_test_add_func("/bdrv-graph-mod/should-update-child", - test_should_update_child); - - return g_test_run(); -} diff --git a/tests/test-bitcnt.c b/tests/test-bitcnt.c deleted file mode 100644 index e153dcb8a2..0000000000 --- a/tests/test-bitcnt.c +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Test bit count routines - * - * This work is licensed under the terms of the GNU LGPL, version 2 or later. - * See the COPYING.LIB file in the top-level directory. - * - */ - -#include "qemu/osdep.h" -#include "qemu/host-utils.h" - -struct bitcnt_test_data { - /* value to count */ - union { - uint8_t w8; - uint16_t w16; - uint32_t w32; - uint64_t w64; - } value; - /* expected result */ - int popct; -}; - -struct bitcnt_test_data eight_bit_data[] = { - { { .w8 = 0x00 }, .popct=0 }, - { { .w8 = 0x01 }, .popct=1 }, - { { .w8 = 0x03 }, .popct=2 }, - { { .w8 = 0x04 }, .popct=1 }, - { { .w8 = 0x0f }, .popct=4 }, - { { .w8 = 0x3f }, .popct=6 }, - { { .w8 = 0x40 }, .popct=1 }, - { { .w8 = 0xf0 }, .popct=4 }, - { { .w8 = 0x7f }, .popct=7 }, - { { .w8 = 0x80 }, .popct=1 }, - { { .w8 = 0xf1 }, .popct=5 }, - { { .w8 = 0xfe }, .popct=7 }, - { { .w8 = 0xff }, .popct=8 }, -}; - -static void test_ctpop8(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(eight_bit_data); i++) { - struct bitcnt_test_data *d = &eight_bit_data[i]; - g_assert(ctpop8(d->value.w8)==d->popct); - } -} - -struct bitcnt_test_data sixteen_bit_data[] = { - { { .w16 = 0x0000 }, .popct=0 }, - { { .w16 = 0x0001 }, .popct=1 }, - { { .w16 = 0x0003 }, .popct=2 }, - { { .w16 = 0x000f }, .popct=4 }, - { { .w16 = 0x003f }, .popct=6 }, - { { .w16 = 0x00f0 }, .popct=4 }, - { { .w16 = 0x0f0f }, .popct=8 }, - { { .w16 = 0x1f1f }, .popct=10 }, - { { .w16 = 0x4000 }, .popct=1 }, - { { .w16 = 0x4001 }, .popct=2 }, - { { .w16 = 0x7000 }, .popct=3 }, - { { .w16 = 0x7fff }, .popct=15 }, -}; - -static void test_ctpop16(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(sixteen_bit_data); i++) { - struct bitcnt_test_data *d = &sixteen_bit_data[i]; - g_assert(ctpop16(d->value.w16)==d->popct); - } -} - -struct bitcnt_test_data thirtytwo_bit_data[] = { - { { .w32 = 0x00000000 }, .popct=0 }, - { { .w32 = 0x00000001 }, .popct=1 }, - { { .w32 = 0x0000000f }, .popct=4 }, - { { .w32 = 0x00000f0f }, .popct=8 }, - { { .w32 = 0x00001f1f }, .popct=10 }, - { { .w32 = 0x00004001 }, .popct=2 }, - { { .w32 = 0x00007000 }, .popct=3 }, - { { .w32 = 0x00007fff }, .popct=15 }, - { { .w32 = 0x55555555 }, .popct=16 }, - { { .w32 = 0xaaaaaaaa }, .popct=16 }, - { { .w32 = 0xff000000 }, .popct=8 }, - { { .w32 = 0xc0c0c0c0 }, .popct=8 }, - { { .w32 = 0x0ffffff0 }, .popct=24 }, - { { .w32 = 0x80000000 }, .popct=1 }, - { { .w32 = 0xffffffff }, .popct=32 }, -}; - -static void test_ctpop32(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(thirtytwo_bit_data); i++) { - struct bitcnt_test_data *d = &thirtytwo_bit_data[i]; - g_assert(ctpop32(d->value.w32)==d->popct); - } -} - -struct bitcnt_test_data sixtyfour_bit_data[] = { - { { .w64 = 0x0000000000000000ULL }, .popct=0 }, - { { .w64 = 0x0000000000000001ULL }, .popct=1 }, - { { .w64 = 0x000000000000000fULL }, .popct=4 }, - { { .w64 = 0x0000000000000f0fULL }, .popct=8 }, - { { .w64 = 0x0000000000001f1fULL }, .popct=10 }, - { { .w64 = 0x0000000000004001ULL }, .popct=2 }, - { { .w64 = 0x0000000000007000ULL }, .popct=3 }, - { { .w64 = 0x0000000000007fffULL }, .popct=15 }, - { { .w64 = 0x0000005500555555ULL }, .popct=16 }, - { { .w64 = 0x00aa0000aaaa00aaULL }, .popct=16 }, - { { .w64 = 0x000f000000f00000ULL }, .popct=8 }, - { { .w64 = 0x0c0c0000c0c0c0c0ULL }, .popct=12 }, - { { .w64 = 0xf00f00f0f0f0f000ULL }, .popct=24 }, - { { .w64 = 0x8000000000000000ULL }, .popct=1 }, - { { .w64 = 0xf0f0f0f0f0f0f0f0ULL }, .popct=32 }, - { { .w64 = 0xffffffffffffffffULL }, .popct=64 }, -}; - -static void test_ctpop64(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(sixtyfour_bit_data); i++) { - struct bitcnt_test_data *d = &sixtyfour_bit_data[i]; - g_assert(ctpop64(d->value.w64)==d->popct); - } -} - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - g_test_add_func("/bitcnt/ctpop8", test_ctpop8); - g_test_add_func("/bitcnt/ctpop16", test_ctpop16); - g_test_add_func("/bitcnt/ctpop32", test_ctpop32); - g_test_add_func("/bitcnt/ctpop64", test_ctpop64); - return g_test_run(); -} diff --git a/tests/test-bitmap.c b/tests/test-bitmap.c deleted file mode 100644 index 8db4f67883..0000000000 --- a/tests/test-bitmap.c +++ /dev/null @@ -1,138 +0,0 @@ -/* - * SPDX-License-Identifier: GPL-2.0-or-later - * - * Bitmap.c unit-tests. - * - * Copyright (C) 2019, Red Hat, Inc. - * - * Author: Peter Xu - */ - -#include "qemu/osdep.h" -#include "qemu/bitmap.h" - -#define BMAP_SIZE 1024 - -static void check_bitmap_copy_with_offset(void) -{ - unsigned long *bmap1, *bmap2, *bmap3, total; - - bmap1 = bitmap_new(BMAP_SIZE); - bmap2 = bitmap_new(BMAP_SIZE); - bmap3 = bitmap_new(BMAP_SIZE); - - bmap1[0] = g_test_rand_int(); - bmap1[1] = g_test_rand_int(); - bmap1[2] = g_test_rand_int(); - bmap1[3] = g_test_rand_int(); - total = BITS_PER_LONG * 4; - - /* Shift 115 bits into bmap2 */ - bitmap_copy_with_dst_offset(bmap2, bmap1, 115, total); - /* Shift another 85 bits into bmap3 */ - bitmap_copy_with_dst_offset(bmap3, bmap2, 85, total + 115); - /* Shift back 200 bits back */ - bitmap_copy_with_src_offset(bmap2, bmap3, 200, total); - - g_assert_cmpmem(bmap1, total / BITS_PER_LONG, - bmap2, total / BITS_PER_LONG); - - bitmap_clear(bmap1, 0, BMAP_SIZE); - /* Set bits in bmap1 are 100-245 */ - bitmap_set(bmap1, 100, 145); - - /* Set bits in bmap2 are 60-205 */ - bitmap_copy_with_src_offset(bmap2, bmap1, 40, 250); - g_assert_cmpint(find_first_bit(bmap2, 60), ==, 60); - g_assert_cmpint(find_next_zero_bit(bmap2, 205, 60), ==, 205); - g_assert(test_bit(205, bmap2) == 0); - - /* Set bits in bmap3 are 135-280 */ - bitmap_copy_with_dst_offset(bmap3, bmap1, 35, 250); - g_assert_cmpint(find_first_bit(bmap3, 135), ==, 135); - g_assert_cmpint(find_next_zero_bit(bmap3, 280, 135), ==, 280); - g_assert(test_bit(280, bmap3) == 0); - - g_free(bmap1); - g_free(bmap2); - g_free(bmap3); -} - -typedef void (*bmap_set_func)(unsigned long *map, long i, long len); -static void bitmap_set_case(bmap_set_func set_func) -{ - unsigned long *bmap; - int offset; - - bmap = bitmap_new(BMAP_SIZE); - - /* Set one bit at offset in second word */ - for (offset = 0; offset <= BITS_PER_LONG; offset++) { - bitmap_clear(bmap, 0, BMAP_SIZE); - set_func(bmap, BITS_PER_LONG + offset, 1); - g_assert_cmpint(find_first_bit(bmap, 2 * BITS_PER_LONG), - ==, BITS_PER_LONG + offset); - g_assert_cmpint(find_next_zero_bit(bmap, - 3 * BITS_PER_LONG, - BITS_PER_LONG + offset), - ==, BITS_PER_LONG + offset + 1); - } - - /* Both Aligned, set bits [BITS_PER_LONG, 3*BITS_PER_LONG] */ - set_func(bmap, BITS_PER_LONG, 2 * BITS_PER_LONG); - g_assert_cmpuint(bmap[1], ==, -1ul); - g_assert_cmpuint(bmap[2], ==, -1ul); - g_assert_cmpint(find_first_bit(bmap, BITS_PER_LONG), ==, BITS_PER_LONG); - g_assert_cmpint(find_next_zero_bit(bmap, 3 * BITS_PER_LONG, BITS_PER_LONG), - ==, 3 * BITS_PER_LONG); - - for (offset = 0; offset <= BITS_PER_LONG; offset++) { - bitmap_clear(bmap, 0, BMAP_SIZE); - /* End Aligned, set bits [BITS_PER_LONG - offset, 3*BITS_PER_LONG] */ - set_func(bmap, BITS_PER_LONG - offset, 2 * BITS_PER_LONG + offset); - g_assert_cmpuint(bmap[1], ==, -1ul); - g_assert_cmpuint(bmap[2], ==, -1ul); - g_assert_cmpint(find_first_bit(bmap, BITS_PER_LONG), - ==, BITS_PER_LONG - offset); - g_assert_cmpint(find_next_zero_bit(bmap, - 3 * BITS_PER_LONG, - BITS_PER_LONG - offset), - ==, 3 * BITS_PER_LONG); - } - - for (offset = 0; offset <= BITS_PER_LONG; offset++) { - bitmap_clear(bmap, 0, BMAP_SIZE); - /* Start Aligned, set bits [BITS_PER_LONG, 3*BITS_PER_LONG + offset] */ - set_func(bmap, BITS_PER_LONG, 2 * BITS_PER_LONG + offset); - g_assert_cmpuint(bmap[1], ==, -1ul); - g_assert_cmpuint(bmap[2], ==, -1ul); - g_assert_cmpint(find_first_bit(bmap, BITS_PER_LONG), - ==, BITS_PER_LONG); - g_assert_cmpint(find_next_zero_bit(bmap, - 3 * BITS_PER_LONG + offset, - BITS_PER_LONG), - ==, 3 * BITS_PER_LONG + offset); - } - - g_free(bmap); -} - -static void check_bitmap_set(void) -{ - bitmap_set_case(bitmap_set); - bitmap_set_case(bitmap_set_atomic); -} - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - - g_test_add_func("/bitmap/bitmap_copy_with_offset", - check_bitmap_copy_with_offset); - g_test_add_func("/bitmap/bitmap_set", - check_bitmap_set); - - g_test_run(); - - return 0; -} diff --git a/tests/test-bitops.c b/tests/test-bitops.c deleted file mode 100644 index 5a791d2e17..0000000000 --- a/tests/test-bitops.c +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Test bitops routines - * - * This work is licensed under the terms of the GNU LGPL, version 2 or later. - * See the COPYING.LIB file in the top-level directory. - * - */ - -#include "qemu/osdep.h" -#include "qemu/bitops.h" - -typedef struct { - uint32_t value; - int start; - int length; - int32_t result; -} S32Test; - -typedef struct { - uint64_t value; - int start; - int length; - int64_t result; -} S64Test; - -static const S32Test test_s32_data[] = { - { 0x38463983, 4, 4, -8 }, - { 0x38463983, 12, 8, 0x63 }, - { 0x38463983, 0, 32, 0x38463983 }, -}; - -static const S64Test test_s64_data[] = { - { 0x8459826734967223ULL, 60, 4, -8 }, - { 0x8459826734967223ULL, 0, 64, 0x8459826734967223LL }, -}; - -static void test_sextract32(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(test_s32_data); i++) { - const S32Test *test = &test_s32_data[i]; - int32_t r = sextract32(test->value, test->start, test->length); - - g_assert_cmpint(r, ==, test->result); - } -} - -static void test_sextract64(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(test_s32_data); i++) { - const S32Test *test = &test_s32_data[i]; - int64_t r = sextract64(test->value, test->start, test->length); - - g_assert_cmpint(r, ==, test->result); - } - - for (i = 0; i < ARRAY_SIZE(test_s64_data); i++) { - const S64Test *test = &test_s64_data[i]; - int64_t r = sextract64(test->value, test->start, test->length); - - g_assert_cmpint(r, ==, test->result); - } -} - -typedef struct { - uint32_t unshuffled; - uint32_t shuffled; -} Shuffle32Test; - -typedef struct { - uint64_t unshuffled; - uint64_t shuffled; -} Shuffle64Test; - -static const Shuffle32Test test_shuffle32_data[] = { - { 0x0000FFFF, 0x55555555 }, - { 0x000081C5, 0x40015011 }, -}; - -static const Shuffle64Test test_shuffle64_data[] = { - { 0x00000000FFFFFFFFULL, 0x5555555555555555ULL }, - { 0x00000000493AB02CULL, 0x1041054445000450ULL }, -}; - -static void test_half_shuffle32(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(test_shuffle32_data); i++) { - const Shuffle32Test *test = &test_shuffle32_data[i]; - uint32_t r = half_shuffle32(test->unshuffled); - - g_assert_cmpint(r, ==, test->shuffled); - } -} - -static void test_half_shuffle64(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(test_shuffle64_data); i++) { - const Shuffle64Test *test = &test_shuffle64_data[i]; - uint64_t r = half_shuffle64(test->unshuffled); - - g_assert_cmpint(r, ==, test->shuffled); - } -} - -static void test_half_unshuffle32(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(test_shuffle32_data); i++) { - const Shuffle32Test *test = &test_shuffle32_data[i]; - uint32_t r = half_unshuffle32(test->shuffled); - - g_assert_cmpint(r, ==, test->unshuffled); - } -} - -static void test_half_unshuffle64(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(test_shuffle64_data); i++) { - const Shuffle64Test *test = &test_shuffle64_data[i]; - uint64_t r = half_unshuffle64(test->shuffled); - - g_assert_cmpint(r, ==, test->unshuffled); - } -} - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - g_test_add_func("/bitops/sextract32", test_sextract32); - g_test_add_func("/bitops/sextract64", test_sextract64); - g_test_add_func("/bitops/half_shuffle32", test_half_shuffle32); - g_test_add_func("/bitops/half_shuffle64", test_half_shuffle64); - g_test_add_func("/bitops/half_unshuffle32", test_half_unshuffle32); - g_test_add_func("/bitops/half_unshuffle64", test_half_unshuffle64); - return g_test_run(); -} diff --git a/tests/test-block-backend.c b/tests/test-block-backend.c deleted file mode 100644 index 2fb1a444bd..0000000000 --- a/tests/test-block-backend.c +++ /dev/null @@ -1,85 +0,0 @@ -/* - * BlockBackend tests - * - * Copyright (c) 2017 Kevin Wolf - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "block/block.h" -#include "sysemu/block-backend.h" -#include "qapi/error.h" -#include "qemu/main-loop.h" - -static void test_drain_aio_error_flush_cb(void *opaque, int ret) -{ - bool *completed = opaque; - - g_assert(ret == -ENOMEDIUM); - *completed = true; -} - -static void test_drain_aio_error(void) -{ - BlockBackend *blk = blk_new(qemu_get_aio_context(), - BLK_PERM_ALL, BLK_PERM_ALL); - BlockAIOCB *acb; - bool completed = false; - - acb = blk_aio_flush(blk, test_drain_aio_error_flush_cb, &completed); - g_assert(acb != NULL); - g_assert(completed == false); - - blk_drain(blk); - g_assert(completed == true); - - blk_unref(blk); -} - -static void test_drain_all_aio_error(void) -{ - BlockBackend *blk = blk_new(qemu_get_aio_context(), - BLK_PERM_ALL, BLK_PERM_ALL); - BlockAIOCB *acb; - bool completed = false; - - acb = blk_aio_flush(blk, test_drain_aio_error_flush_cb, &completed); - g_assert(acb != NULL); - g_assert(completed == false); - - blk_drain_all(); - g_assert(completed == true); - - blk_unref(blk); -} - -int main(int argc, char **argv) -{ - bdrv_init(); - qemu_init_main_loop(&error_abort); - - g_test_init(&argc, &argv, NULL); - - g_test_add_func("/block-backend/drain_aio_error", test_drain_aio_error); - g_test_add_func("/block-backend/drain_all_aio_error", - test_drain_all_aio_error); - - return g_test_run(); -} diff --git a/tests/test-block-iothread.c b/tests/test-block-iothread.c deleted file mode 100644 index 3f866a35c6..0000000000 --- a/tests/test-block-iothread.c +++ /dev/null @@ -1,774 +0,0 @@ -/* - * Block tests for iothreads - * - * Copyright (c) 2018 Kevin Wolf - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "block/block.h" -#include "block/blockjob_int.h" -#include "sysemu/block-backend.h" -#include "qapi/error.h" -#include "qapi/qmp/qdict.h" -#include "qemu/main-loop.h" -#include "iothread.h" - -static int coroutine_fn bdrv_test_co_prwv(BlockDriverState *bs, - uint64_t offset, uint64_t bytes, - QEMUIOVector *qiov, int flags) -{ - return 0; -} - -static int coroutine_fn bdrv_test_co_pdiscard(BlockDriverState *bs, - int64_t offset, int bytes) -{ - return 0; -} - -static int coroutine_fn -bdrv_test_co_truncate(BlockDriverState *bs, int64_t offset, bool exact, - PreallocMode prealloc, BdrvRequestFlags flags, - Error **errp) -{ - return 0; -} - -static int coroutine_fn bdrv_test_co_block_status(BlockDriverState *bs, - bool want_zero, - int64_t offset, int64_t count, - int64_t *pnum, int64_t *map, - BlockDriverState **file) -{ - *pnum = count; - return 0; -} - -static BlockDriver bdrv_test = { - .format_name = "test", - .instance_size = 1, - - .bdrv_co_preadv = bdrv_test_co_prwv, - .bdrv_co_pwritev = bdrv_test_co_prwv, - .bdrv_co_pdiscard = bdrv_test_co_pdiscard, - .bdrv_co_truncate = bdrv_test_co_truncate, - .bdrv_co_block_status = bdrv_test_co_block_status, -}; - -static void test_sync_op_pread(BdrvChild *c) -{ - uint8_t buf[512]; - int ret; - - /* Success */ - ret = bdrv_pread(c, 0, buf, sizeof(buf)); - g_assert_cmpint(ret, ==, 512); - - /* Early error: Negative offset */ - ret = bdrv_pread(c, -2, buf, sizeof(buf)); - g_assert_cmpint(ret, ==, -EIO); -} - -static void test_sync_op_pwrite(BdrvChild *c) -{ - uint8_t buf[512]; - int ret; - - /* Success */ - ret = bdrv_pwrite(c, 0, buf, sizeof(buf)); - g_assert_cmpint(ret, ==, 512); - - /* Early error: Negative offset */ - ret = bdrv_pwrite(c, -2, buf, sizeof(buf)); - g_assert_cmpint(ret, ==, -EIO); -} - -static void test_sync_op_blk_pread(BlockBackend *blk) -{ - uint8_t buf[512]; - int ret; - - /* Success */ - ret = blk_pread(blk, 0, buf, sizeof(buf)); - g_assert_cmpint(ret, ==, 512); - - /* Early error: Negative offset */ - ret = blk_pread(blk, -2, buf, sizeof(buf)); - g_assert_cmpint(ret, ==, -EIO); -} - -static void test_sync_op_blk_pwrite(BlockBackend *blk) -{ - uint8_t buf[512]; - int ret; - - /* Success */ - ret = blk_pwrite(blk, 0, buf, sizeof(buf), 0); - g_assert_cmpint(ret, ==, 512); - - /* Early error: Negative offset */ - ret = blk_pwrite(blk, -2, buf, sizeof(buf), 0); - g_assert_cmpint(ret, ==, -EIO); -} - -static void test_sync_op_load_vmstate(BdrvChild *c) -{ - uint8_t buf[512]; - int ret; - - /* Error: Driver does not support snapshots */ - ret = bdrv_load_vmstate(c->bs, buf, 0, sizeof(buf)); - g_assert_cmpint(ret, ==, -ENOTSUP); -} - -static void test_sync_op_save_vmstate(BdrvChild *c) -{ - uint8_t buf[512]; - int ret; - - /* Error: Driver does not support snapshots */ - ret = bdrv_save_vmstate(c->bs, buf, 0, sizeof(buf)); - g_assert_cmpint(ret, ==, -ENOTSUP); -} - -static void test_sync_op_pdiscard(BdrvChild *c) -{ - int ret; - - /* Normal success path */ - c->bs->open_flags |= BDRV_O_UNMAP; - ret = bdrv_pdiscard(c, 0, 512); - g_assert_cmpint(ret, ==, 0); - - /* Early success: UNMAP not supported */ - c->bs->open_flags &= ~BDRV_O_UNMAP; - ret = bdrv_pdiscard(c, 0, 512); - g_assert_cmpint(ret, ==, 0); - - /* Early error: Negative offset */ - ret = bdrv_pdiscard(c, -2, 512); - g_assert_cmpint(ret, ==, -EIO); -} - -static void test_sync_op_blk_pdiscard(BlockBackend *blk) -{ - int ret; - - /* Early success: UNMAP not supported */ - ret = blk_pdiscard(blk, 0, 512); - g_assert_cmpint(ret, ==, 0); - - /* Early error: Negative offset */ - ret = blk_pdiscard(blk, -2, 512); - g_assert_cmpint(ret, ==, -EIO); -} - -static void test_sync_op_truncate(BdrvChild *c) -{ - int ret; - - /* Normal success path */ - ret = bdrv_truncate(c, 65536, false, PREALLOC_MODE_OFF, 0, NULL); - g_assert_cmpint(ret, ==, 0); - - /* Early error: Negative offset */ - ret = bdrv_truncate(c, -2, false, PREALLOC_MODE_OFF, 0, NULL); - g_assert_cmpint(ret, ==, -EINVAL); - - /* Error: Read-only image */ - c->bs->read_only = true; - c->bs->open_flags &= ~BDRV_O_RDWR; - - ret = bdrv_truncate(c, 65536, false, PREALLOC_MODE_OFF, 0, NULL); - g_assert_cmpint(ret, ==, -EACCES); - - c->bs->read_only = false; - c->bs->open_flags |= BDRV_O_RDWR; -} - -static void test_sync_op_block_status(BdrvChild *c) -{ - int ret; - int64_t n; - - /* Normal success path */ - ret = bdrv_is_allocated(c->bs, 0, 65536, &n); - g_assert_cmpint(ret, ==, 0); - - /* Early success: No driver support */ - bdrv_test.bdrv_co_block_status = NULL; - ret = bdrv_is_allocated(c->bs, 0, 65536, &n); - g_assert_cmpint(ret, ==, 1); - - /* Early success: bytes = 0 */ - ret = bdrv_is_allocated(c->bs, 0, 0, &n); - g_assert_cmpint(ret, ==, 0); - - /* Early success: Offset > image size*/ - ret = bdrv_is_allocated(c->bs, 0x1000000, 0x1000000, &n); - g_assert_cmpint(ret, ==, 0); -} - -static void test_sync_op_flush(BdrvChild *c) -{ - int ret; - - /* Normal success path */ - ret = bdrv_flush(c->bs); - g_assert_cmpint(ret, ==, 0); - - /* Early success: Read-only image */ - c->bs->read_only = true; - c->bs->open_flags &= ~BDRV_O_RDWR; - - ret = bdrv_flush(c->bs); - g_assert_cmpint(ret, ==, 0); - - c->bs->read_only = false; - c->bs->open_flags |= BDRV_O_RDWR; -} - -static void test_sync_op_blk_flush(BlockBackend *blk) -{ - BlockDriverState *bs = blk_bs(blk); - int ret; - - /* Normal success path */ - ret = blk_flush(blk); - g_assert_cmpint(ret, ==, 0); - - /* Early success: Read-only image */ - bs->read_only = true; - bs->open_flags &= ~BDRV_O_RDWR; - - ret = blk_flush(blk); - g_assert_cmpint(ret, ==, 0); - - bs->read_only = false; - bs->open_flags |= BDRV_O_RDWR; -} - -static void test_sync_op_check(BdrvChild *c) -{ - BdrvCheckResult result; - int ret; - - /* Error: Driver does not implement check */ - ret = bdrv_check(c->bs, &result, 0); - g_assert_cmpint(ret, ==, -ENOTSUP); -} - -static void test_sync_op_invalidate_cache(BdrvChild *c) -{ - /* Early success: Image is not inactive */ - bdrv_invalidate_cache(c->bs, NULL); -} - - -typedef struct SyncOpTest { - const char *name; - void (*fn)(BdrvChild *c); - void (*blkfn)(BlockBackend *blk); -} SyncOpTest; - -const SyncOpTest sync_op_tests[] = { - { - .name = "/sync-op/pread", - .fn = test_sync_op_pread, - .blkfn = test_sync_op_blk_pread, - }, { - .name = "/sync-op/pwrite", - .fn = test_sync_op_pwrite, - .blkfn = test_sync_op_blk_pwrite, - }, { - .name = "/sync-op/load_vmstate", - .fn = test_sync_op_load_vmstate, - }, { - .name = "/sync-op/save_vmstate", - .fn = test_sync_op_save_vmstate, - }, { - .name = "/sync-op/pdiscard", - .fn = test_sync_op_pdiscard, - .blkfn = test_sync_op_blk_pdiscard, - }, { - .name = "/sync-op/truncate", - .fn = test_sync_op_truncate, - }, { - .name = "/sync-op/block_status", - .fn = test_sync_op_block_status, - }, { - .name = "/sync-op/flush", - .fn = test_sync_op_flush, - .blkfn = test_sync_op_blk_flush, - }, { - .name = "/sync-op/check", - .fn = test_sync_op_check, - }, { - .name = "/sync-op/invalidate_cache", - .fn = test_sync_op_invalidate_cache, - }, -}; - -/* Test synchronous operations that run in a different iothread, so we have to - * poll for the coroutine there to return. */ -static void test_sync_op(const void *opaque) -{ - const SyncOpTest *t = opaque; - IOThread *iothread = iothread_new(); - AioContext *ctx = iothread_get_aio_context(iothread); - BlockBackend *blk; - BlockDriverState *bs; - BdrvChild *c; - - blk = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL); - bs = bdrv_new_open_driver(&bdrv_test, "base", BDRV_O_RDWR, &error_abort); - bs->total_sectors = 65536 / BDRV_SECTOR_SIZE; - blk_insert_bs(blk, bs, &error_abort); - c = QLIST_FIRST(&bs->parents); - - blk_set_aio_context(blk, ctx, &error_abort); - aio_context_acquire(ctx); - t->fn(c); - if (t->blkfn) { - t->blkfn(blk); - } - blk_set_aio_context(blk, qemu_get_aio_context(), &error_abort); - aio_context_release(ctx); - - bdrv_unref(bs); - blk_unref(blk); -} - -typedef struct TestBlockJob { - BlockJob common; - bool should_complete; - int n; -} TestBlockJob; - -static int test_job_prepare(Job *job) -{ - g_assert(qemu_get_current_aio_context() == qemu_get_aio_context()); - return 0; -} - -static int coroutine_fn test_job_run(Job *job, Error **errp) -{ - TestBlockJob *s = container_of(job, TestBlockJob, common.job); - - job_transition_to_ready(&s->common.job); - while (!s->should_complete) { - s->n++; - g_assert(qemu_get_current_aio_context() == job->aio_context); - - /* Avoid job_sleep_ns() because it marks the job as !busy. We want to - * emulate some actual activity (probably some I/O) here so that the - * drain involved in AioContext switches has to wait for this activity - * to stop. */ - qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 1000000); - - job_pause_point(&s->common.job); - } - - g_assert(qemu_get_current_aio_context() == job->aio_context); - return 0; -} - -static void test_job_complete(Job *job, Error **errp) -{ - TestBlockJob *s = container_of(job, TestBlockJob, common.job); - s->should_complete = true; -} - -BlockJobDriver test_job_driver = { - .job_driver = { - .instance_size = sizeof(TestBlockJob), - .free = block_job_free, - .user_resume = block_job_user_resume, - .run = test_job_run, - .complete = test_job_complete, - .prepare = test_job_prepare, - }, -}; - -static void test_attach_blockjob(void) -{ - IOThread *iothread = iothread_new(); - AioContext *ctx = iothread_get_aio_context(iothread); - BlockBackend *blk; - BlockDriverState *bs; - TestBlockJob *tjob; - - blk = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL); - bs = bdrv_new_open_driver(&bdrv_test, "base", BDRV_O_RDWR, &error_abort); - blk_insert_bs(blk, bs, &error_abort); - - tjob = block_job_create("job0", &test_job_driver, NULL, bs, - 0, BLK_PERM_ALL, - 0, 0, NULL, NULL, &error_abort); - job_start(&tjob->common.job); - - while (tjob->n == 0) { - aio_poll(qemu_get_aio_context(), false); - } - - blk_set_aio_context(blk, ctx, &error_abort); - - tjob->n = 0; - while (tjob->n == 0) { - aio_poll(qemu_get_aio_context(), false); - } - - aio_context_acquire(ctx); - blk_set_aio_context(blk, qemu_get_aio_context(), &error_abort); - aio_context_release(ctx); - - tjob->n = 0; - while (tjob->n == 0) { - aio_poll(qemu_get_aio_context(), false); - } - - blk_set_aio_context(blk, ctx, &error_abort); - - tjob->n = 0; - while (tjob->n == 0) { - aio_poll(qemu_get_aio_context(), false); - } - - aio_context_acquire(ctx); - job_complete_sync(&tjob->common.job, &error_abort); - blk_set_aio_context(blk, qemu_get_aio_context(), &error_abort); - aio_context_release(ctx); - - bdrv_unref(bs); - blk_unref(blk); -} - -/* - * Test that changing the AioContext for one node in a tree (here through blk) - * changes all other nodes as well: - * - * blk - * | - * | bs_verify [blkverify] - * | / \ - * | / \ - * bs_a [bdrv_test] bs_b [bdrv_test] - * - */ -static void test_propagate_basic(void) -{ - IOThread *iothread = iothread_new(); - AioContext *ctx = iothread_get_aio_context(iothread); - AioContext *main_ctx; - BlockBackend *blk; - BlockDriverState *bs_a, *bs_b, *bs_verify; - QDict *options; - - /* - * Create bs_a and its BlockBackend. We cannot take the RESIZE - * permission because blkverify will not share it on the test - * image. - */ - blk = blk_new(qemu_get_aio_context(), BLK_PERM_ALL & ~BLK_PERM_RESIZE, - BLK_PERM_ALL); - bs_a = bdrv_new_open_driver(&bdrv_test, "bs_a", BDRV_O_RDWR, &error_abort); - blk_insert_bs(blk, bs_a, &error_abort); - - /* Create bs_b */ - bs_b = bdrv_new_open_driver(&bdrv_test, "bs_b", BDRV_O_RDWR, &error_abort); - - /* Create blkverify filter that references both bs_a and bs_b */ - options = qdict_new(); - qdict_put_str(options, "driver", "blkverify"); - qdict_put_str(options, "test", "bs_a"); - qdict_put_str(options, "raw", "bs_b"); - - bs_verify = bdrv_open(NULL, NULL, options, BDRV_O_RDWR, &error_abort); - - /* Switch the AioContext */ - blk_set_aio_context(blk, ctx, &error_abort); - g_assert(blk_get_aio_context(blk) == ctx); - g_assert(bdrv_get_aio_context(bs_a) == ctx); - g_assert(bdrv_get_aio_context(bs_verify) == ctx); - g_assert(bdrv_get_aio_context(bs_b) == ctx); - - /* Switch the AioContext back */ - main_ctx = qemu_get_aio_context(); - aio_context_acquire(ctx); - blk_set_aio_context(blk, main_ctx, &error_abort); - aio_context_release(ctx); - g_assert(blk_get_aio_context(blk) == main_ctx); - g_assert(bdrv_get_aio_context(bs_a) == main_ctx); - g_assert(bdrv_get_aio_context(bs_verify) == main_ctx); - g_assert(bdrv_get_aio_context(bs_b) == main_ctx); - - bdrv_unref(bs_verify); - bdrv_unref(bs_b); - bdrv_unref(bs_a); - blk_unref(blk); -} - -/* - * Test that diamonds in the graph don't lead to endless recursion: - * - * blk - * | - * bs_verify [blkverify] - * / \ - * / \ - * bs_b [raw] bs_c[raw] - * \ / - * \ / - * bs_a [bdrv_test] - */ -static void test_propagate_diamond(void) -{ - IOThread *iothread = iothread_new(); - AioContext *ctx = iothread_get_aio_context(iothread); - AioContext *main_ctx; - BlockBackend *blk; - BlockDriverState *bs_a, *bs_b, *bs_c, *bs_verify; - QDict *options; - - /* Create bs_a */ - bs_a = bdrv_new_open_driver(&bdrv_test, "bs_a", BDRV_O_RDWR, &error_abort); - - /* Create bs_b and bc_c */ - options = qdict_new(); - qdict_put_str(options, "driver", "raw"); - qdict_put_str(options, "file", "bs_a"); - qdict_put_str(options, "node-name", "bs_b"); - bs_b = bdrv_open(NULL, NULL, options, BDRV_O_RDWR, &error_abort); - - options = qdict_new(); - qdict_put_str(options, "driver", "raw"); - qdict_put_str(options, "file", "bs_a"); - qdict_put_str(options, "node-name", "bs_c"); - bs_c = bdrv_open(NULL, NULL, options, BDRV_O_RDWR, &error_abort); - - /* Create blkverify filter that references both bs_b and bs_c */ - options = qdict_new(); - qdict_put_str(options, "driver", "blkverify"); - qdict_put_str(options, "test", "bs_b"); - qdict_put_str(options, "raw", "bs_c"); - - bs_verify = bdrv_open(NULL, NULL, options, BDRV_O_RDWR, &error_abort); - /* - * Do not take the RESIZE permission: This would require the same - * from bs_c and thus from bs_a; however, blkverify will not share - * it on bs_b, and thus it will not be available for bs_a. - */ - blk = blk_new(qemu_get_aio_context(), BLK_PERM_ALL & ~BLK_PERM_RESIZE, - BLK_PERM_ALL); - blk_insert_bs(blk, bs_verify, &error_abort); - - /* Switch the AioContext */ - blk_set_aio_context(blk, ctx, &error_abort); - g_assert(blk_get_aio_context(blk) == ctx); - g_assert(bdrv_get_aio_context(bs_verify) == ctx); - g_assert(bdrv_get_aio_context(bs_a) == ctx); - g_assert(bdrv_get_aio_context(bs_b) == ctx); - g_assert(bdrv_get_aio_context(bs_c) == ctx); - - /* Switch the AioContext back */ - main_ctx = qemu_get_aio_context(); - aio_context_acquire(ctx); - blk_set_aio_context(blk, main_ctx, &error_abort); - aio_context_release(ctx); - g_assert(blk_get_aio_context(blk) == main_ctx); - g_assert(bdrv_get_aio_context(bs_verify) == main_ctx); - g_assert(bdrv_get_aio_context(bs_a) == main_ctx); - g_assert(bdrv_get_aio_context(bs_b) == main_ctx); - g_assert(bdrv_get_aio_context(bs_c) == main_ctx); - - blk_unref(blk); - bdrv_unref(bs_verify); - bdrv_unref(bs_c); - bdrv_unref(bs_b); - bdrv_unref(bs_a); -} - -static void test_propagate_mirror(void) -{ - IOThread *iothread = iothread_new(); - AioContext *ctx = iothread_get_aio_context(iothread); - AioContext *main_ctx = qemu_get_aio_context(); - BlockDriverState *src, *target, *filter; - BlockBackend *blk; - Job *job; - Error *local_err = NULL; - - /* Create src and target*/ - src = bdrv_new_open_driver(&bdrv_test, "src", BDRV_O_RDWR, &error_abort); - target = bdrv_new_open_driver(&bdrv_test, "target", BDRV_O_RDWR, - &error_abort); - - /* Start a mirror job */ - mirror_start("job0", src, target, NULL, JOB_DEFAULT, 0, 0, 0, - MIRROR_SYNC_MODE_NONE, MIRROR_OPEN_BACKING_CHAIN, false, - BLOCKDEV_ON_ERROR_REPORT, BLOCKDEV_ON_ERROR_REPORT, - false, "filter_node", MIRROR_COPY_MODE_BACKGROUND, - &error_abort); - job = job_get("job0"); - filter = bdrv_find_node("filter_node"); - - /* Change the AioContext of src */ - bdrv_try_set_aio_context(src, ctx, &error_abort); - g_assert(bdrv_get_aio_context(src) == ctx); - g_assert(bdrv_get_aio_context(target) == ctx); - g_assert(bdrv_get_aio_context(filter) == ctx); - g_assert(job->aio_context == ctx); - - /* Change the AioContext of target */ - aio_context_acquire(ctx); - bdrv_try_set_aio_context(target, main_ctx, &error_abort); - aio_context_release(ctx); - g_assert(bdrv_get_aio_context(src) == main_ctx); - g_assert(bdrv_get_aio_context(target) == main_ctx); - g_assert(bdrv_get_aio_context(filter) == main_ctx); - - /* With a BlockBackend on src, changing target must fail */ - blk = blk_new(qemu_get_aio_context(), 0, BLK_PERM_ALL); - blk_insert_bs(blk, src, &error_abort); - - bdrv_try_set_aio_context(target, ctx, &local_err); - error_free_or_abort(&local_err); - - g_assert(blk_get_aio_context(blk) == main_ctx); - g_assert(bdrv_get_aio_context(src) == main_ctx); - g_assert(bdrv_get_aio_context(target) == main_ctx); - g_assert(bdrv_get_aio_context(filter) == main_ctx); - - /* ...unless we explicitly allow it */ - aio_context_acquire(ctx); - blk_set_allow_aio_context_change(blk, true); - bdrv_try_set_aio_context(target, ctx, &error_abort); - aio_context_release(ctx); - - g_assert(blk_get_aio_context(blk) == ctx); - g_assert(bdrv_get_aio_context(src) == ctx); - g_assert(bdrv_get_aio_context(target) == ctx); - g_assert(bdrv_get_aio_context(filter) == ctx); - - job_cancel_sync_all(); - - aio_context_acquire(ctx); - blk_set_aio_context(blk, main_ctx, &error_abort); - bdrv_try_set_aio_context(target, main_ctx, &error_abort); - aio_context_release(ctx); - - blk_unref(blk); - bdrv_unref(src); - bdrv_unref(target); -} - -static void test_attach_second_node(void) -{ - IOThread *iothread = iothread_new(); - AioContext *ctx = iothread_get_aio_context(iothread); - AioContext *main_ctx = qemu_get_aio_context(); - BlockBackend *blk; - BlockDriverState *bs, *filter; - QDict *options; - - blk = blk_new(ctx, BLK_PERM_ALL, BLK_PERM_ALL); - bs = bdrv_new_open_driver(&bdrv_test, "base", BDRV_O_RDWR, &error_abort); - blk_insert_bs(blk, bs, &error_abort); - - options = qdict_new(); - qdict_put_str(options, "driver", "raw"); - qdict_put_str(options, "file", "base"); - - filter = bdrv_open(NULL, NULL, options, BDRV_O_RDWR, &error_abort); - g_assert(blk_get_aio_context(blk) == ctx); - g_assert(bdrv_get_aio_context(bs) == ctx); - g_assert(bdrv_get_aio_context(filter) == ctx); - - aio_context_acquire(ctx); - blk_set_aio_context(blk, main_ctx, &error_abort); - aio_context_release(ctx); - g_assert(blk_get_aio_context(blk) == main_ctx); - g_assert(bdrv_get_aio_context(bs) == main_ctx); - g_assert(bdrv_get_aio_context(filter) == main_ctx); - - bdrv_unref(filter); - bdrv_unref(bs); - blk_unref(blk); -} - -static void test_attach_preserve_blk_ctx(void) -{ - IOThread *iothread = iothread_new(); - AioContext *ctx = iothread_get_aio_context(iothread); - BlockBackend *blk; - BlockDriverState *bs; - - blk = blk_new(ctx, BLK_PERM_ALL, BLK_PERM_ALL); - bs = bdrv_new_open_driver(&bdrv_test, "base", BDRV_O_RDWR, &error_abort); - bs->total_sectors = 65536 / BDRV_SECTOR_SIZE; - - /* Add node to BlockBackend that has an iothread context assigned */ - blk_insert_bs(blk, bs, &error_abort); - g_assert(blk_get_aio_context(blk) == ctx); - g_assert(bdrv_get_aio_context(bs) == ctx); - - /* Remove the node again */ - aio_context_acquire(ctx); - blk_remove_bs(blk); - aio_context_release(ctx); - g_assert(blk_get_aio_context(blk) == ctx); - g_assert(bdrv_get_aio_context(bs) == qemu_get_aio_context()); - - /* Re-attach the node */ - blk_insert_bs(blk, bs, &error_abort); - g_assert(blk_get_aio_context(blk) == ctx); - g_assert(bdrv_get_aio_context(bs) == ctx); - - aio_context_acquire(ctx); - blk_set_aio_context(blk, qemu_get_aio_context(), &error_abort); - aio_context_release(ctx); - bdrv_unref(bs); - blk_unref(blk); -} - -int main(int argc, char **argv) -{ - int i; - - bdrv_init(); - qemu_init_main_loop(&error_abort); - - g_test_init(&argc, &argv, NULL); - - for (i = 0; i < ARRAY_SIZE(sync_op_tests); i++) { - const SyncOpTest *t = &sync_op_tests[i]; - g_test_add_data_func(t->name, t, test_sync_op); - } - - g_test_add_func("/attach/blockjob", test_attach_blockjob); - g_test_add_func("/attach/second_node", test_attach_second_node); - g_test_add_func("/attach/preserve_blk_ctx", test_attach_preserve_blk_ctx); - g_test_add_func("/propagate/basic", test_propagate_basic); - g_test_add_func("/propagate/diamond", test_propagate_diamond); - g_test_add_func("/propagate/mirror", test_propagate_mirror); - - return g_test_run(); -} diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c deleted file mode 100644 index 8bd13b9949..0000000000 --- a/tests/test-blockjob-txn.c +++ /dev/null @@ -1,262 +0,0 @@ -/* - * Blockjob transactions tests - * - * Copyright Red Hat, Inc. 2015 - * - * Authors: - * Stefan Hajnoczi - * - * This work is licensed under the terms of the GNU LGPL, version 2 or later. - * See the COPYING.LIB file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "qemu/main-loop.h" -#include "block/blockjob_int.h" -#include "sysemu/block-backend.h" -#include "qapi/qmp/qdict.h" - -typedef struct { - BlockJob common; - unsigned int iterations; - bool use_timer; - int rc; - int *result; -} TestBlockJob; - -static void test_block_job_clean(Job *job) -{ - BlockJob *bjob = container_of(job, BlockJob, job); - BlockDriverState *bs = blk_bs(bjob->blk); - - bdrv_unref(bs); -} - -static int coroutine_fn test_block_job_run(Job *job, Error **errp) -{ - TestBlockJob *s = container_of(job, TestBlockJob, common.job); - - while (s->iterations--) { - if (s->use_timer) { - job_sleep_ns(job, 0); - } else { - job_yield(job); - } - - if (job_is_cancelled(job)) { - break; - } - } - - return s->rc; -} - -typedef struct { - TestBlockJob *job; - int *result; -} TestBlockJobCBData; - -static void test_block_job_cb(void *opaque, int ret) -{ - TestBlockJobCBData *data = opaque; - if (!ret && job_is_cancelled(&data->job->common.job)) { - ret = -ECANCELED; - } - *data->result = ret; - g_free(data); -} - -static const BlockJobDriver test_block_job_driver = { - .job_driver = { - .instance_size = sizeof(TestBlockJob), - .free = block_job_free, - .user_resume = block_job_user_resume, - .run = test_block_job_run, - .clean = test_block_job_clean, - }, -}; - -/* Create a block job that completes with a given return code after a given - * number of event loop iterations. The return code is stored in the given - * result pointer. - * - * The event loop iterations can either be handled automatically with a 0 delay - * timer, or they can be stepped manually by entering the coroutine. - */ -static BlockJob *test_block_job_start(unsigned int iterations, - bool use_timer, - int rc, int *result, JobTxn *txn) -{ - BlockDriverState *bs; - TestBlockJob *s; - TestBlockJobCBData *data; - static unsigned counter; - char job_id[24]; - - data = g_new0(TestBlockJobCBData, 1); - - QDict *opt = qdict_new(); - qdict_put_str(opt, "file.read-zeroes", "on"); - bs = bdrv_open("null-co://", NULL, opt, 0, &error_abort); - g_assert_nonnull(bs); - - snprintf(job_id, sizeof(job_id), "job%u", counter++); - s = block_job_create(job_id, &test_block_job_driver, txn, bs, - 0, BLK_PERM_ALL, 0, JOB_DEFAULT, - test_block_job_cb, data, &error_abort); - s->iterations = iterations; - s->use_timer = use_timer; - s->rc = rc; - s->result = result; - data->job = s; - data->result = result; - return &s->common; -} - -static void test_single_job(int expected) -{ - BlockJob *job; - JobTxn *txn; - int result = -EINPROGRESS; - - txn = job_txn_new(); - job = test_block_job_start(1, true, expected, &result, txn); - job_start(&job->job); - - if (expected == -ECANCELED) { - job_cancel(&job->job, false); - } - - while (result == -EINPROGRESS) { - aio_poll(qemu_get_aio_context(), true); - } - g_assert_cmpint(result, ==, expected); - - job_txn_unref(txn); -} - -static void test_single_job_success(void) -{ - test_single_job(0); -} - -static void test_single_job_failure(void) -{ - test_single_job(-EIO); -} - -static void test_single_job_cancel(void) -{ - test_single_job(-ECANCELED); -} - -static void test_pair_jobs(int expected1, int expected2) -{ - BlockJob *job1; - BlockJob *job2; - JobTxn *txn; - int result1 = -EINPROGRESS; - int result2 = -EINPROGRESS; - - txn = job_txn_new(); - job1 = test_block_job_start(1, true, expected1, &result1, txn); - job2 = test_block_job_start(2, true, expected2, &result2, txn); - job_start(&job1->job); - job_start(&job2->job); - - /* Release our reference now to trigger as many nice - * use-after-free bugs as possible. - */ - job_txn_unref(txn); - - if (expected1 == -ECANCELED) { - job_cancel(&job1->job, false); - } - if (expected2 == -ECANCELED) { - job_cancel(&job2->job, false); - } - - while (result1 == -EINPROGRESS || result2 == -EINPROGRESS) { - aio_poll(qemu_get_aio_context(), true); - } - - /* Failure or cancellation of one job cancels the other job */ - if (expected1 != 0) { - expected2 = -ECANCELED; - } else if (expected2 != 0) { - expected1 = -ECANCELED; - } - - g_assert_cmpint(result1, ==, expected1); - g_assert_cmpint(result2, ==, expected2); -} - -static void test_pair_jobs_success(void) -{ - test_pair_jobs(0, 0); -} - -static void test_pair_jobs_failure(void) -{ - /* Test both orderings. The two jobs run for a different number of - * iterations so the code path is different depending on which job fails - * first. - */ - test_pair_jobs(-EIO, 0); - test_pair_jobs(0, -EIO); -} - -static void test_pair_jobs_cancel(void) -{ - test_pair_jobs(-ECANCELED, 0); - test_pair_jobs(0, -ECANCELED); -} - -static void test_pair_jobs_fail_cancel_race(void) -{ - BlockJob *job1; - BlockJob *job2; - JobTxn *txn; - int result1 = -EINPROGRESS; - int result2 = -EINPROGRESS; - - txn = job_txn_new(); - job1 = test_block_job_start(1, true, -ECANCELED, &result1, txn); - job2 = test_block_job_start(2, false, 0, &result2, txn); - job_start(&job1->job); - job_start(&job2->job); - - job_cancel(&job1->job, false); - - /* Now make job2 finish before the main loop kicks jobs. This simulates - * the race between a pending kick and another job completing. - */ - job_enter(&job2->job); - job_enter(&job2->job); - - while (result1 == -EINPROGRESS || result2 == -EINPROGRESS) { - aio_poll(qemu_get_aio_context(), true); - } - - g_assert_cmpint(result1, ==, -ECANCELED); - g_assert_cmpint(result2, ==, -ECANCELED); - - job_txn_unref(txn); -} - -int main(int argc, char **argv) -{ - qemu_init_main_loop(&error_abort); - bdrv_init(); - - g_test_init(&argc, &argv, NULL); - g_test_add_func("/single/success", test_single_job_success); - g_test_add_func("/single/failure", test_single_job_failure); - g_test_add_func("/single/cancel", test_single_job_cancel); - g_test_add_func("/pair/success", test_pair_jobs_success); - g_test_add_func("/pair/failure", test_pair_jobs_failure); - g_test_add_func("/pair/cancel", test_pair_jobs_cancel); - g_test_add_func("/pair/fail-cancel-race", test_pair_jobs_fail_cancel_race); - return g_test_run(); -} diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c deleted file mode 100644 index 7519847912..0000000000 --- a/tests/test-blockjob.c +++ /dev/null @@ -1,393 +0,0 @@ -/* - * Blockjob tests - * - * Copyright Igalia, S.L. 2016 - * - * Authors: - * Alberto Garcia - * - * This work is licensed under the terms of the GNU LGPL, version 2 or later. - * See the COPYING.LIB file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "qemu/main-loop.h" -#include "block/blockjob_int.h" -#include "sysemu/block-backend.h" -#include "qapi/qmp/qdict.h" - -static const BlockJobDriver test_block_job_driver = { - .job_driver = { - .instance_size = sizeof(BlockJob), - .free = block_job_free, - .user_resume = block_job_user_resume, - }, -}; - -static void block_job_cb(void *opaque, int ret) -{ -} - -static BlockJob *mk_job(BlockBackend *blk, const char *id, - const BlockJobDriver *drv, bool should_succeed, - int flags) -{ - BlockJob *job; - Error *err = NULL; - - job = block_job_create(id, drv, NULL, blk_bs(blk), - 0, BLK_PERM_ALL, 0, flags, block_job_cb, - NULL, &err); - if (should_succeed) { - g_assert_null(err); - g_assert_nonnull(job); - if (id) { - g_assert_cmpstr(job->job.id, ==, id); - } else { - g_assert_cmpstr(job->job.id, ==, blk_name(blk)); - } - } else { - error_free_or_abort(&err); - g_assert_null(job); - } - - return job; -} - -static BlockJob *do_test_id(BlockBackend *blk, const char *id, - bool should_succeed) -{ - return mk_job(blk, id, &test_block_job_driver, - should_succeed, JOB_DEFAULT); -} - -/* This creates a BlockBackend (optionally with a name) with a - * BlockDriverState inserted. */ -static BlockBackend *create_blk(const char *name) -{ - /* No I/O is performed on this device */ - BlockBackend *blk = blk_new(qemu_get_aio_context(), 0, BLK_PERM_ALL); - BlockDriverState *bs; - - QDict *opt = qdict_new(); - qdict_put_str(opt, "file.read-zeroes", "on"); - bs = bdrv_open("null-co://", NULL, opt, 0, &error_abort); - g_assert_nonnull(bs); - - blk_insert_bs(blk, bs, &error_abort); - bdrv_unref(bs); - - if (name) { - Error *err = NULL; - monitor_add_blk(blk, name, &err); - g_assert_null(err); - } - - return blk; -} - -/* This destroys the backend */ -static void destroy_blk(BlockBackend *blk) -{ - if (blk_name(blk)[0] != '\0') { - monitor_remove_blk(blk); - } - - blk_remove_bs(blk); - blk_unref(blk); -} - -static void test_job_ids(void) -{ - BlockBackend *blk[3]; - BlockJob *job[3]; - - blk[0] = create_blk(NULL); - blk[1] = create_blk("drive1"); - blk[2] = create_blk("drive2"); - - /* No job ID provided and the block backend has no name */ - job[0] = do_test_id(blk[0], NULL, false); - - /* These are all invalid job IDs */ - job[0] = do_test_id(blk[0], "0id", false); - job[0] = do_test_id(blk[0], "", false); - job[0] = do_test_id(blk[0], " ", false); - job[0] = do_test_id(blk[0], "123", false); - job[0] = do_test_id(blk[0], "_id", false); - job[0] = do_test_id(blk[0], "-id", false); - job[0] = do_test_id(blk[0], ".id", false); - job[0] = do_test_id(blk[0], "#id", false); - - /* This one is valid */ - job[0] = do_test_id(blk[0], "id0", true); - - /* We can have two jobs in the same BDS */ - job[1] = do_test_id(blk[0], "id1", true); - job_early_fail(&job[1]->job); - - /* Duplicate job IDs are not allowed */ - job[1] = do_test_id(blk[1], "id0", false); - - /* But once job[0] finishes we can reuse its ID */ - job_early_fail(&job[0]->job); - job[1] = do_test_id(blk[1], "id0", true); - - /* No job ID specified, defaults to the backend name ('drive1') */ - job_early_fail(&job[1]->job); - job[1] = do_test_id(blk[1], NULL, true); - - /* Duplicate job ID */ - job[2] = do_test_id(blk[2], "drive1", false); - - /* The ID of job[2] would default to 'drive2' but it is already in use */ - job[0] = do_test_id(blk[0], "drive2", true); - job[2] = do_test_id(blk[2], NULL, false); - - /* This one is valid */ - job[2] = do_test_id(blk[2], "id_2", true); - - job_early_fail(&job[0]->job); - job_early_fail(&job[1]->job); - job_early_fail(&job[2]->job); - - destroy_blk(blk[0]); - destroy_blk(blk[1]); - destroy_blk(blk[2]); -} - -typedef struct CancelJob { - BlockJob common; - BlockBackend *blk; - bool should_converge; - bool should_complete; -} CancelJob; - -static void cancel_job_complete(Job *job, Error **errp) -{ - CancelJob *s = container_of(job, CancelJob, common.job); - s->should_complete = true; -} - -static int coroutine_fn cancel_job_run(Job *job, Error **errp) -{ - CancelJob *s = container_of(job, CancelJob, common.job); - - while (!s->should_complete) { - if (job_is_cancelled(&s->common.job)) { - return 0; - } - - if (!job_is_ready(&s->common.job) && s->should_converge) { - job_transition_to_ready(&s->common.job); - } - - job_sleep_ns(&s->common.job, 100000); - } - - return 0; -} - -static const BlockJobDriver test_cancel_driver = { - .job_driver = { - .instance_size = sizeof(CancelJob), - .free = block_job_free, - .user_resume = block_job_user_resume, - .run = cancel_job_run, - .complete = cancel_job_complete, - }, -}; - -static CancelJob *create_common(Job **pjob) -{ - BlockBackend *blk; - Job *job; - BlockJob *bjob; - CancelJob *s; - - blk = create_blk(NULL); - bjob = mk_job(blk, "Steve", &test_cancel_driver, true, - JOB_MANUAL_FINALIZE | JOB_MANUAL_DISMISS); - job = &bjob->job; - job_ref(job); - assert(job->status == JOB_STATUS_CREATED); - s = container_of(bjob, CancelJob, common); - s->blk = blk; - - *pjob = job; - return s; -} - -static void cancel_common(CancelJob *s) -{ - BlockJob *job = &s->common; - BlockBackend *blk = s->blk; - JobStatus sts = job->job.status; - AioContext *ctx; - - ctx = job->job.aio_context; - aio_context_acquire(ctx); - - job_cancel_sync(&job->job); - if (sts != JOB_STATUS_CREATED && sts != JOB_STATUS_CONCLUDED) { - Job *dummy = &job->job; - job_dismiss(&dummy, &error_abort); - } - assert(job->job.status == JOB_STATUS_NULL); - job_unref(&job->job); - destroy_blk(blk); - - aio_context_release(ctx); -} - -static void test_cancel_created(void) -{ - Job *job; - CancelJob *s; - - s = create_common(&job); - cancel_common(s); -} - -static void test_cancel_running(void) -{ - Job *job; - CancelJob *s; - - s = create_common(&job); - - job_start(job); - assert(job->status == JOB_STATUS_RUNNING); - - cancel_common(s); -} - -static void test_cancel_paused(void) -{ - Job *job; - CancelJob *s; - - s = create_common(&job); - - job_start(job); - assert(job->status == JOB_STATUS_RUNNING); - - job_user_pause(job, &error_abort); - job_enter(job); - assert(job->status == JOB_STATUS_PAUSED); - - cancel_common(s); -} - -static void test_cancel_ready(void) -{ - Job *job; - CancelJob *s; - - s = create_common(&job); - - job_start(job); - assert(job->status == JOB_STATUS_RUNNING); - - s->should_converge = true; - job_enter(job); - assert(job->status == JOB_STATUS_READY); - - cancel_common(s); -} - -static void test_cancel_standby(void) -{ - Job *job; - CancelJob *s; - - s = create_common(&job); - - job_start(job); - assert(job->status == JOB_STATUS_RUNNING); - - s->should_converge = true; - job_enter(job); - assert(job->status == JOB_STATUS_READY); - - job_user_pause(job, &error_abort); - job_enter(job); - assert(job->status == JOB_STATUS_STANDBY); - - cancel_common(s); -} - -static void test_cancel_pending(void) -{ - Job *job; - CancelJob *s; - - s = create_common(&job); - - job_start(job); - assert(job->status == JOB_STATUS_RUNNING); - - s->should_converge = true; - job_enter(job); - assert(job->status == JOB_STATUS_READY); - - job_complete(job, &error_abort); - job_enter(job); - while (!job->deferred_to_main_loop) { - aio_poll(qemu_get_aio_context(), true); - } - assert(job->status == JOB_STATUS_READY); - aio_poll(qemu_get_aio_context(), true); - assert(job->status == JOB_STATUS_PENDING); - - cancel_common(s); -} - -static void test_cancel_concluded(void) -{ - Job *job; - CancelJob *s; - - s = create_common(&job); - - job_start(job); - assert(job->status == JOB_STATUS_RUNNING); - - s->should_converge = true; - job_enter(job); - assert(job->status == JOB_STATUS_READY); - - job_complete(job, &error_abort); - job_enter(job); - while (!job->deferred_to_main_loop) { - aio_poll(qemu_get_aio_context(), true); - } - assert(job->status == JOB_STATUS_READY); - aio_poll(qemu_get_aio_context(), true); - assert(job->status == JOB_STATUS_PENDING); - - aio_context_acquire(job->aio_context); - job_finalize(job, &error_abort); - aio_context_release(job->aio_context); - assert(job->status == JOB_STATUS_CONCLUDED); - - cancel_common(s); -} - -int main(int argc, char **argv) -{ - qemu_init_main_loop(&error_abort); - bdrv_init(); - - g_test_init(&argc, &argv, NULL); - g_test_add_func("/blockjob/ids", test_job_ids); - g_test_add_func("/blockjob/cancel/created", test_cancel_created); - g_test_add_func("/blockjob/cancel/running", test_cancel_running); - g_test_add_func("/blockjob/cancel/paused", test_cancel_paused); - g_test_add_func("/blockjob/cancel/ready", test_cancel_ready); - g_test_add_func("/blockjob/cancel/standby", test_cancel_standby); - g_test_add_func("/blockjob/cancel/pending", test_cancel_pending); - g_test_add_func("/blockjob/cancel/concluded", test_cancel_concluded); - return g_test_run(); -} diff --git a/tests/test-bufferiszero.c b/tests/test-bufferiszero.c deleted file mode 100644 index e45fd31804..0000000000 --- a/tests/test-bufferiszero.c +++ /dev/null @@ -1,78 +0,0 @@ -/* - * QEMU buffer_is_zero test - * - * Copyright (c) 2016 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - * - */ - -#include "qemu/osdep.h" -#include "qemu/cutils.h" - -static char buffer[8 * 1024 * 1024]; - -static void test_1(void) -{ - size_t s, a, o; - - /* Basic positive test. */ - g_assert(buffer_is_zero(buffer, sizeof(buffer))); - - /* Basic negative test. */ - buffer[sizeof(buffer) - 1] = 1; - g_assert(!buffer_is_zero(buffer, sizeof(buffer))); - buffer[sizeof(buffer) - 1] = 0; - - /* Positive tests for size and alignment. */ - for (a = 1; a <= 64; a++) { - for (s = 1; s < 1024; s++) { - buffer[a - 1] = 1; - buffer[a + s] = 1; - g_assert(buffer_is_zero(buffer + a, s)); - buffer[a - 1] = 0; - buffer[a + s] = 0; - } - } - - /* Negative tests for size, alignment, and the offset of the marker. */ - for (a = 1; a <= 64; a++) { - for (s = 1; s < 1024; s++) { - for (o = 0; o < s; ++o) { - buffer[a + o] = 1; - g_assert(!buffer_is_zero(buffer + a, s)); - buffer[a + o] = 0; - } - } - } -} - -static void test_2(void) -{ - if (g_test_perf()) { - test_1(); - } else { - do { - test_1(); - } while (test_buffer_is_zero_next_accel()); - } -} - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - g_test_add_func("/cutils/bufferiszero", test_2); - - return g_test_run(); -} diff --git a/tests/test-char.c b/tests/test-char.c deleted file mode 100644 index 755d54c15e..0000000000 --- a/tests/test-char.c +++ /dev/null @@ -1,1560 +0,0 @@ -#include "qemu/osdep.h" -#include - -#include "qemu/config-file.h" -#include "qemu/module.h" -#include "qemu/option.h" -#include "qemu/sockets.h" -#include "chardev/char-fe.h" -#include "sysemu/sysemu.h" -#include "qapi/error.h" -#include "qapi/qapi-commands-char.h" -#include "qapi/qmp/qdict.h" -#include "qom/qom-qobject.h" -#include "io/channel-socket.h" -#include "qapi/qobject-input-visitor.h" -#include "qapi/qapi-visit-sockets.h" -#include "socket-helpers.h" - -static bool quit; - -typedef struct FeHandler { - int read_count; - bool is_open; - int openclose_count; - bool openclose_mismatch; - int last_event; - char read_buf[128]; -} FeHandler; - -static void main_loop(void) -{ - quit = false; - do { - main_loop_wait(false); - } while (!quit); -} - -static int fe_can_read(void *opaque) -{ - FeHandler *h = opaque; - - return sizeof(h->read_buf) - h->read_count; -} - -static void fe_read(void *opaque, const uint8_t *buf, int size) -{ - FeHandler *h = opaque; - - g_assert_cmpint(size, <=, fe_can_read(opaque)); - - memcpy(h->read_buf + h->read_count, buf, size); - h->read_count += size; - quit = true; -} - -static void fe_event(void *opaque, QEMUChrEvent event) -{ - FeHandler *h = opaque; - bool new_open_state; - - h->last_event = event; - switch (event) { - case CHR_EVENT_BREAK: - break; - case CHR_EVENT_OPENED: - case CHR_EVENT_CLOSED: - h->openclose_count++; - new_open_state = (event == CHR_EVENT_OPENED); - if (h->is_open == new_open_state) { - h->openclose_mismatch = true; - } - h->is_open = new_open_state; - /* fallthrough */ - default: - quit = true; - break; - } -} - -#ifdef _WIN32 -static void char_console_test_subprocess(void) -{ - QemuOpts *opts; - Chardev *chr; - - opts = qemu_opts_create(qemu_find_opts("chardev"), "console-label", - 1, &error_abort); - qemu_opt_set(opts, "backend", "console", &error_abort); - - chr = qemu_chr_new_from_opts(opts, NULL, NULL); - g_assert_nonnull(chr); - - qemu_chr_write_all(chr, (const uint8_t *)"CONSOLE", 7); - - qemu_opts_del(opts); - object_unparent(OBJECT(chr)); -} - -static void char_console_test(void) -{ - g_test_trap_subprocess("/char/console/subprocess", 0, 0); - g_test_trap_assert_passed(); - g_test_trap_assert_stdout("CONSOLE"); -} -#endif -static void char_stdio_test_subprocess(void) -{ - Chardev *chr; - CharBackend be; - int ret; - - chr = qemu_chr_new("label", "stdio", NULL); - g_assert_nonnull(chr); - - qemu_chr_fe_init(&be, chr, &error_abort); - qemu_chr_fe_set_open(&be, true); - ret = qemu_chr_fe_write(&be, (void *)"buf", 4); - g_assert_cmpint(ret, ==, 4); - - qemu_chr_fe_deinit(&be, true); -} - -static void char_stdio_test(void) -{ - g_test_trap_subprocess("/char/stdio/subprocess", 0, 0); - g_test_trap_assert_passed(); - g_test_trap_assert_stdout("buf"); -} - -static void char_ringbuf_test(void) -{ - QemuOpts *opts; - Chardev *chr; - CharBackend be; - char *data; - int ret; - - opts = qemu_opts_create(qemu_find_opts("chardev"), "ringbuf-label", - 1, &error_abort); - qemu_opt_set(opts, "backend", "ringbuf", &error_abort); - - qemu_opt_set(opts, "size", "5", &error_abort); - chr = qemu_chr_new_from_opts(opts, NULL, NULL); - g_assert_null(chr); - qemu_opts_del(opts); - - opts = qemu_opts_create(qemu_find_opts("chardev"), "ringbuf-label", - 1, &error_abort); - qemu_opt_set(opts, "backend", "ringbuf", &error_abort); - qemu_opt_set(opts, "size", "2", &error_abort); - chr = qemu_chr_new_from_opts(opts, NULL, &error_abort); - g_assert_nonnull(chr); - qemu_opts_del(opts); - - qemu_chr_fe_init(&be, chr, &error_abort); - ret = qemu_chr_fe_write(&be, (void *)"buff", 4); - g_assert_cmpint(ret, ==, 4); - - data = qmp_ringbuf_read("ringbuf-label", 4, false, 0, &error_abort); - g_assert_cmpstr(data, ==, "ff"); - g_free(data); - - data = qmp_ringbuf_read("ringbuf-label", 4, false, 0, &error_abort); - g_assert_cmpstr(data, ==, ""); - g_free(data); - - qemu_chr_fe_deinit(&be, true); - - /* check alias */ - opts = qemu_opts_create(qemu_find_opts("chardev"), "memory-label", - 1, &error_abort); - qemu_opt_set(opts, "backend", "memory", &error_abort); - qemu_opt_set(opts, "size", "2", &error_abort); - chr = qemu_chr_new_from_opts(opts, NULL, NULL); - g_assert_nonnull(chr); - object_unparent(OBJECT(chr)); - qemu_opts_del(opts); -} - -static void char_mux_test(void) -{ - QemuOpts *opts; - Chardev *chr, *base; - char *data; - FeHandler h1 = { 0, false, 0, false, }, h2 = { 0, false, 0, false, }; - CharBackend chr_be1, chr_be2; - - opts = qemu_opts_create(qemu_find_opts("chardev"), "mux-label", - 1, &error_abort); - qemu_opt_set(opts, "backend", "ringbuf", &error_abort); - qemu_opt_set(opts, "size", "128", &error_abort); - qemu_opt_set(opts, "mux", "on", &error_abort); - chr = qemu_chr_new_from_opts(opts, NULL, &error_abort); - g_assert_nonnull(chr); - qemu_opts_del(opts); - - qemu_chr_fe_init(&chr_be1, chr, &error_abort); - qemu_chr_fe_set_handlers(&chr_be1, - fe_can_read, - fe_read, - fe_event, - NULL, - &h1, - NULL, true); - - qemu_chr_fe_init(&chr_be2, chr, &error_abort); - qemu_chr_fe_set_handlers(&chr_be2, - fe_can_read, - fe_read, - fe_event, - NULL, - &h2, - NULL, true); - qemu_chr_fe_take_focus(&chr_be2); - - base = qemu_chr_find("mux-label-base"); - g_assert_cmpint(qemu_chr_be_can_write(base), !=, 0); - - qemu_chr_be_write(base, (void *)"hello", 6); - g_assert_cmpint(h1.read_count, ==, 0); - g_assert_cmpint(h2.read_count, ==, 6); - g_assert_cmpstr(h2.read_buf, ==, "hello"); - h2.read_count = 0; - - g_assert_cmpint(h1.last_event, !=, 42); /* should be MUX_OUT or OPENED */ - g_assert_cmpint(h2.last_event, !=, 42); /* should be MUX_IN or OPENED */ - /* sending event on the base broadcast to all fe, historical reasons? */ - qemu_chr_be_event(base, 42); - g_assert_cmpint(h1.last_event, ==, 42); - g_assert_cmpint(h2.last_event, ==, 42); - qemu_chr_be_event(chr, -1); - g_assert_cmpint(h1.last_event, ==, 42); - g_assert_cmpint(h2.last_event, ==, -1); - - /* switch focus */ - qemu_chr_be_write(base, (void *)"\1b", 2); - g_assert_cmpint(h1.last_event, ==, 42); - g_assert_cmpint(h2.last_event, ==, CHR_EVENT_BREAK); - - qemu_chr_be_write(base, (void *)"\1c", 2); - g_assert_cmpint(h1.last_event, ==, CHR_EVENT_MUX_IN); - g_assert_cmpint(h2.last_event, ==, CHR_EVENT_MUX_OUT); - qemu_chr_be_event(chr, -1); - g_assert_cmpint(h1.last_event, ==, -1); - g_assert_cmpint(h2.last_event, ==, CHR_EVENT_MUX_OUT); - - qemu_chr_be_write(base, (void *)"hello", 6); - g_assert_cmpint(h2.read_count, ==, 0); - g_assert_cmpint(h1.read_count, ==, 6); - g_assert_cmpstr(h1.read_buf, ==, "hello"); - h1.read_count = 0; - - qemu_chr_be_write(base, (void *)"\1b", 2); - g_assert_cmpint(h1.last_event, ==, CHR_EVENT_BREAK); - g_assert_cmpint(h2.last_event, ==, CHR_EVENT_MUX_OUT); - - /* open/close state and corresponding events */ - g_assert_true(qemu_chr_fe_backend_open(&chr_be1)); - g_assert_true(qemu_chr_fe_backend_open(&chr_be2)); - g_assert_true(h1.is_open); - g_assert_false(h1.openclose_mismatch); - g_assert_true(h2.is_open); - g_assert_false(h2.openclose_mismatch); - - h1.openclose_count = h2.openclose_count = 0; - - qemu_chr_fe_set_handlers(&chr_be1, NULL, NULL, NULL, NULL, - NULL, NULL, false); - qemu_chr_fe_set_handlers(&chr_be2, NULL, NULL, NULL, NULL, - NULL, NULL, false); - g_assert_cmpint(h1.openclose_count, ==, 0); - g_assert_cmpint(h2.openclose_count, ==, 0); - - h1.is_open = h2.is_open = false; - qemu_chr_fe_set_handlers(&chr_be1, - NULL, - NULL, - fe_event, - NULL, - &h1, - NULL, false); - qemu_chr_fe_set_handlers(&chr_be2, - NULL, - NULL, - fe_event, - NULL, - &h2, - NULL, false); - g_assert_cmpint(h1.openclose_count, ==, 1); - g_assert_false(h1.openclose_mismatch); - g_assert_cmpint(h2.openclose_count, ==, 1); - g_assert_false(h2.openclose_mismatch); - - qemu_chr_be_event(base, CHR_EVENT_CLOSED); - qemu_chr_be_event(base, CHR_EVENT_OPENED); - g_assert_cmpint(h1.openclose_count, ==, 3); - g_assert_false(h1.openclose_mismatch); - g_assert_cmpint(h2.openclose_count, ==, 3); - g_assert_false(h2.openclose_mismatch); - - qemu_chr_fe_set_handlers(&chr_be2, - fe_can_read, - fe_read, - fe_event, - NULL, - &h2, - NULL, false); - qemu_chr_fe_set_handlers(&chr_be1, - fe_can_read, - fe_read, - fe_event, - NULL, - &h1, - NULL, false); - - /* remove first handler */ - qemu_chr_fe_set_handlers(&chr_be1, NULL, NULL, NULL, NULL, - NULL, NULL, true); - qemu_chr_be_write(base, (void *)"hello", 6); - g_assert_cmpint(h1.read_count, ==, 0); - g_assert_cmpint(h2.read_count, ==, 0); - - qemu_chr_be_write(base, (void *)"\1c", 2); - qemu_chr_be_write(base, (void *)"hello", 6); - g_assert_cmpint(h1.read_count, ==, 0); - g_assert_cmpint(h2.read_count, ==, 6); - g_assert_cmpstr(h2.read_buf, ==, "hello"); - h2.read_count = 0; - - /* print help */ - qemu_chr_be_write(base, (void *)"\1?", 2); - data = qmp_ringbuf_read("mux-label-base", 128, false, 0, &error_abort); - g_assert_cmpint(strlen(data), !=, 0); - g_free(data); - - qemu_chr_fe_deinit(&chr_be1, false); - qemu_chr_fe_deinit(&chr_be2, true); -} - - -static void websock_server_read(void *opaque, const uint8_t *buf, int size) -{ - g_assert_cmpint(size, ==, 5); - g_assert(memcmp(buf, "world", size) == 0); - quit = true; -} - - -static int websock_server_can_read(void *opaque) -{ - return 10; -} - - -static bool websock_check_http_headers(char *buf, int size) -{ - int i; - const char *ans[] = { "HTTP/1.1 101 Switching Protocols\r\n", - "Server: QEMU VNC\r\n", - "Upgrade: websocket\r\n", - "Connection: Upgrade\r\n", - "Sec-WebSocket-Accept:", - "Sec-WebSocket-Protocol: binary\r\n" }; - - for (i = 0; i < 6; i++) { - if (g_strstr_len(buf, size, ans[i]) == NULL) { - return false; - } - } - - return true; -} - - -static void websock_client_read(void *opaque, const uint8_t *buf, int size) -{ - const uint8_t ping[] = { 0x89, 0x85, /* Ping header */ - 0x07, 0x77, 0x9e, 0xf9, /* Masking key */ - 0x6f, 0x12, 0xf2, 0x95, 0x68 /* "hello" */ }; - - const uint8_t binary[] = { 0x82, 0x85, /* Binary header */ - 0x74, 0x90, 0xb9, 0xdf, /* Masking key */ - 0x03, 0xff, 0xcb, 0xb3, 0x10 /* "world" */ }; - Chardev *chr_client = opaque; - - if (websock_check_http_headers((char *) buf, size)) { - qemu_chr_fe_write(chr_client->be, ping, sizeof(ping)); - } else if (buf[0] == 0x8a && buf[1] == 0x05) { - g_assert(strncmp((char *) buf + 2, "hello", 5) == 0); - qemu_chr_fe_write(chr_client->be, binary, sizeof(binary)); - } else { - g_assert(buf[0] == 0x88 && buf[1] == 0x16); - g_assert(strncmp((char *) buf + 4, "peer requested close", 10) == 0); - quit = true; - } -} - - -static int websock_client_can_read(void *opaque) -{ - return 4096; -} - - -static void char_websock_test(void) -{ - QObject *addr; - QDict *qdict; - const char *port; - char *tmp; - char *handshake_port; - CharBackend be; - CharBackend client_be; - Chardev *chr_client; - Chardev *chr = qemu_chr_new("server", - "websocket:127.0.0.1:0,server=on,wait=off", NULL); - const char handshake[] = "GET / HTTP/1.1\r\n" - "Upgrade: websocket\r\n" - "Connection: Upgrade\r\n" - "Host: localhost:%s\r\n" - "Origin: http://localhost:%s\r\n" - "Sec-WebSocket-Key: o9JHNiS3/0/0zYE1wa3yIw==\r\n" - "Sec-WebSocket-Version: 13\r\n" - "Sec-WebSocket-Protocol: binary\r\n\r\n"; - const uint8_t close[] = { 0x88, 0x82, /* Close header */ - 0xef, 0xaa, 0xc5, 0x97, /* Masking key */ - 0xec, 0x42 /* Status code */ }; - - addr = object_property_get_qobject(OBJECT(chr), "addr", &error_abort); - qdict = qobject_to(QDict, addr); - port = qdict_get_str(qdict, "port"); - tmp = g_strdup_printf("tcp:127.0.0.1:%s", port); - handshake_port = g_strdup_printf(handshake, port, port); - qobject_unref(qdict); - - qemu_chr_fe_init(&be, chr, &error_abort); - qemu_chr_fe_set_handlers(&be, websock_server_can_read, websock_server_read, - NULL, NULL, chr, NULL, true); - - chr_client = qemu_chr_new("client", tmp, NULL); - qemu_chr_fe_init(&client_be, chr_client, &error_abort); - qemu_chr_fe_set_handlers(&client_be, websock_client_can_read, - websock_client_read, - NULL, NULL, chr_client, NULL, true); - g_free(tmp); - - qemu_chr_write_all(chr_client, - (uint8_t *) handshake_port, - strlen(handshake_port)); - g_free(handshake_port); - main_loop(); - - g_assert(object_property_get_bool(OBJECT(chr), "connected", &error_abort)); - g_assert(object_property_get_bool(OBJECT(chr_client), - "connected", &error_abort)); - - qemu_chr_write_all(chr_client, close, sizeof(close)); - main_loop(); - - object_unparent(OBJECT(chr_client)); - object_unparent(OBJECT(chr)); -} - - -#ifndef _WIN32 -static void char_pipe_test(void) -{ - gchar *tmp_path = g_dir_make_tmp("qemu-test-char.XXXXXX", NULL); - gchar *tmp, *in, *out, *pipe = g_build_filename(tmp_path, "pipe", NULL); - Chardev *chr; - CharBackend be; - int ret, fd; - char buf[10]; - FeHandler fe = { 0, }; - - in = g_strdup_printf("%s.in", pipe); - if (mkfifo(in, 0600) < 0) { - abort(); - } - out = g_strdup_printf("%s.out", pipe); - if (mkfifo(out, 0600) < 0) { - abort(); - } - - tmp = g_strdup_printf("pipe:%s", pipe); - chr = qemu_chr_new("pipe", tmp, NULL); - g_assert_nonnull(chr); - g_free(tmp); - - qemu_chr_fe_init(&be, chr, &error_abort); - - ret = qemu_chr_fe_write(&be, (void *)"pipe-out", 9); - g_assert_cmpint(ret, ==, 9); - - fd = open(out, O_RDWR); - ret = read(fd, buf, sizeof(buf)); - g_assert_cmpint(ret, ==, 9); - g_assert_cmpstr(buf, ==, "pipe-out"); - close(fd); - - fd = open(in, O_WRONLY); - ret = write(fd, "pipe-in", 8); - g_assert_cmpint(ret, ==, 8); - close(fd); - - qemu_chr_fe_set_handlers(&be, - fe_can_read, - fe_read, - fe_event, - NULL, - &fe, - NULL, true); - - main_loop(); - - g_assert_cmpint(fe.read_count, ==, 8); - g_assert_cmpstr(fe.read_buf, ==, "pipe-in"); - - qemu_chr_fe_deinit(&be, true); - - g_assert(g_unlink(in) == 0); - g_assert(g_unlink(out) == 0); - g_assert(g_rmdir(tmp_path) == 0); - g_free(in); - g_free(out); - g_free(tmp_path); - g_free(pipe); -} -#endif - -typedef struct SocketIdleData { - GMainLoop *loop; - Chardev *chr; - bool conn_expected; - CharBackend *be; - CharBackend *client_be; -} SocketIdleData; - - -static void socket_read_hello(void *opaque, const uint8_t *buf, int size) -{ - g_assert_cmpint(size, ==, 5); - g_assert(strncmp((char *)buf, "hello", 5) == 0); - - quit = true; -} - -static int socket_can_read_hello(void *opaque) -{ - return 10; -} - -static int make_udp_socket(int *port) -{ - struct sockaddr_in addr = { 0, }; - socklen_t alen = sizeof(addr); - int ret, sock = qemu_socket(PF_INET, SOCK_DGRAM, 0); - - g_assert_cmpint(sock, >, 0); - addr.sin_family = AF_INET ; - addr.sin_addr.s_addr = htonl(INADDR_ANY); - addr.sin_port = 0; - ret = bind(sock, (struct sockaddr *)&addr, sizeof(addr)); - g_assert_cmpint(ret, ==, 0); - ret = getsockname(sock, (struct sockaddr *)&addr, &alen); - g_assert_cmpint(ret, ==, 0); - - *port = ntohs(addr.sin_port); - return sock; -} - -static void char_udp_test_internal(Chardev *reuse_chr, int sock) -{ - struct sockaddr_in other; - SocketIdleData d = { 0, }; - Chardev *chr; - CharBackend *be; - socklen_t alen = sizeof(other); - int ret; - char buf[10]; - char *tmp = NULL; - - if (reuse_chr) { - chr = reuse_chr; - be = chr->be; - } else { - int port; - sock = make_udp_socket(&port); - tmp = g_strdup_printf("udp:127.0.0.1:%d", port); - chr = qemu_chr_new("client", tmp, NULL); - g_assert_nonnull(chr); - - be = g_alloca(sizeof(CharBackend)); - qemu_chr_fe_init(be, chr, &error_abort); - } - - d.chr = chr; - qemu_chr_fe_set_handlers(be, socket_can_read_hello, socket_read_hello, - NULL, NULL, &d, NULL, true); - ret = qemu_chr_write_all(chr, (uint8_t *)"hello", 5); - g_assert_cmpint(ret, ==, 5); - - ret = recvfrom(sock, buf, sizeof(buf), 0, - (struct sockaddr *)&other, &alen); - g_assert_cmpint(ret, ==, 5); - ret = sendto(sock, buf, 5, 0, (struct sockaddr *)&other, alen); - g_assert_cmpint(ret, ==, 5); - - main_loop(); - - if (!reuse_chr) { - close(sock); - qemu_chr_fe_deinit(be, true); - } - g_free(tmp); -} - -static void char_udp_test(void) -{ - char_udp_test_internal(NULL, 0); -} - - -typedef struct { - int event; - bool got_pong; - CharBackend *be; -} CharSocketTestData; - - -#define SOCKET_PING "Hello" -#define SOCKET_PONG "World" - -typedef void (*char_socket_cb)(void *opaque, QEMUChrEvent event); - -static void -char_socket_event(void *opaque, QEMUChrEvent event) -{ - CharSocketTestData *data = opaque; - data->event = event; -} - -static void -char_socket_event_with_error(void *opaque, QEMUChrEvent event) -{ - static bool first_error; - CharSocketTestData *data = opaque; - CharBackend *be = data->be; - data->event = event; - switch (event) { - case CHR_EVENT_OPENED: - if (!first_error) { - first_error = true; - qemu_chr_fe_disconnect(be); - } - return; - case CHR_EVENT_CLOSED: - return; - default: - return; - } -} - - -static void -char_socket_read(void *opaque, const uint8_t *buf, int size) -{ - CharSocketTestData *data = opaque; - g_assert_cmpint(size, ==, sizeof(SOCKET_PONG)); - g_assert(memcmp(buf, SOCKET_PONG, size) == 0); - data->got_pong = true; -} - - -static int -char_socket_can_read(void *opaque) -{ - return sizeof(SOCKET_PONG); -} - - -static char * -char_socket_addr_to_opt_str(SocketAddress *addr, bool fd_pass, - const char *reconnect, bool is_listen) -{ - if (fd_pass) { - QIOChannelSocket *ioc = qio_channel_socket_new(); - int fd; - char *optstr; - g_assert(!reconnect); - if (is_listen) { - qio_channel_socket_listen_sync(ioc, addr, 1, &error_abort); - } else { - qio_channel_socket_connect_sync(ioc, addr, &error_abort); - } - fd = ioc->fd; - ioc->fd = -1; - optstr = g_strdup_printf("socket,id=cdev0,fd=%d%s", - fd, is_listen ? ",server=on,wait=off" : ""); - object_unref(OBJECT(ioc)); - return optstr; - } else { - switch (addr->type) { - case SOCKET_ADDRESS_TYPE_INET: - return g_strdup_printf("socket,id=cdev0,host=%s,port=%s%s%s", - addr->u.inet.host, - addr->u.inet.port, - reconnect ? reconnect : "", - is_listen ? ",server=on,wait=off" : ""); - - case SOCKET_ADDRESS_TYPE_UNIX: - return g_strdup_printf("socket,id=cdev0,path=%s%s%s", - addr->u.q_unix.path, - reconnect ? reconnect : "", - is_listen ? ",server=on,wait=off" : ""); - - default: - g_assert_not_reached(); - } - } -} - - -static int -char_socket_ping_pong(QIOChannel *ioc, Error **errp) -{ - char greeting[sizeof(SOCKET_PING)]; - const char *response = SOCKET_PONG; - - int ret; - ret = qio_channel_read_all(ioc, greeting, sizeof(greeting), errp); - if (ret != 0) { - object_unref(OBJECT(ioc)); - return -1; - } - - g_assert(memcmp(greeting, SOCKET_PING, sizeof(greeting)) == 0); - - qio_channel_write_all(ioc, response, sizeof(SOCKET_PONG), errp); - object_unref(OBJECT(ioc)); - return 0; -} - - -static gpointer -char_socket_server_client_thread(gpointer data) -{ - SocketAddress *addr = data; - QIOChannelSocket *ioc = qio_channel_socket_new(); - - qio_channel_socket_connect_sync(ioc, addr, &error_abort); - - char_socket_ping_pong(QIO_CHANNEL(ioc), &error_abort); - - return NULL; -} - - -typedef struct { - SocketAddress *addr; - bool wait_connected; - bool fd_pass; -} CharSocketServerTestConfig; - - -static void char_socket_server_test(gconstpointer opaque) -{ - const CharSocketServerTestConfig *config = opaque; - Chardev *chr; - CharBackend be = {0}; - CharSocketTestData data = {0}; - QObject *qaddr; - SocketAddress *addr; - Visitor *v; - QemuThread thread; - int ret; - bool reconnected = false; - char *optstr; - QemuOpts *opts; - - g_setenv("QTEST_SILENT_ERRORS", "1", 1); - /* - * We rely on config->addr containing "wait=off", otherwise - * qemu_chr_new() will block until a client connects. We - * can't spawn our client thread though, because until - * qemu_chr_new() returns we don't know what TCP port was - * allocated by the OS - */ - optstr = char_socket_addr_to_opt_str(config->addr, - config->fd_pass, - NULL, - true); - opts = qemu_opts_parse_noisily(qemu_find_opts("chardev"), - optstr, true); - g_assert_nonnull(opts); - chr = qemu_chr_new_from_opts(opts, NULL, &error_abort); - qemu_opts_del(opts); - g_assert_nonnull(chr); - g_assert(!object_property_get_bool(OBJECT(chr), "connected", &error_abort)); - - qaddr = object_property_get_qobject(OBJECT(chr), "addr", &error_abort); - g_assert_nonnull(qaddr); - - v = qobject_input_visitor_new(qaddr); - visit_type_SocketAddress(v, "addr", &addr, &error_abort); - visit_free(v); - qobject_unref(qaddr); - - qemu_chr_fe_init(&be, chr, &error_abort); - - reconnect: - data.event = -1; - data.be = &be; - qemu_chr_fe_set_handlers(&be, NULL, NULL, - char_socket_event, NULL, - &data, NULL, true); - g_assert(data.event == -1); - - /* - * Kick off a thread to act as the "remote" client - * which just plays ping-pong with us - */ - qemu_thread_create(&thread, "client", - char_socket_server_client_thread, - addr, QEMU_THREAD_JOINABLE); - g_assert(data.event == -1); - - if (config->wait_connected) { - /* Synchronously accept a connection */ - qemu_chr_wait_connected(chr, &error_abort); - } else { - /* - * Asynchronously accept a connection when the evnt - * loop reports the listener socket as readable - */ - while (data.event == -1) { - main_loop_wait(false); - } - } - g_assert(object_property_get_bool(OBJECT(chr), "connected", &error_abort)); - g_assert(data.event == CHR_EVENT_OPENED); - data.event = -1; - - /* Send a greeting to the client */ - ret = qemu_chr_fe_write_all(&be, (const uint8_t *)SOCKET_PING, - sizeof(SOCKET_PING)); - g_assert_cmpint(ret, ==, sizeof(SOCKET_PING)); - g_assert(data.event == -1); - - /* Setup a callback to receive the reply to our greeting */ - qemu_chr_fe_set_handlers(&be, char_socket_can_read, - char_socket_read, - char_socket_event, NULL, - &data, NULL, true); - g_assert(data.event == CHR_EVENT_OPENED); - data.event = -1; - - /* Wait for the client to go away */ - while (data.event == -1) { - main_loop_wait(false); - } - g_assert(!object_property_get_bool(OBJECT(chr), "connected", &error_abort)); - g_assert(data.event == CHR_EVENT_CLOSED); - g_assert(data.got_pong); - - qemu_thread_join(&thread); - - if (!reconnected) { - reconnected = true; - goto reconnect; - } - - qapi_free_SocketAddress(addr); - object_unparent(OBJECT(chr)); - g_free(optstr); - g_unsetenv("QTEST_SILENT_ERRORS"); -} - - -static gpointer -char_socket_client_server_thread(gpointer data) -{ - QIOChannelSocket *ioc = data; - QIOChannelSocket *cioc; - -retry: - cioc = qio_channel_socket_accept(ioc, &error_abort); - g_assert_nonnull(cioc); - - if (char_socket_ping_pong(QIO_CHANNEL(cioc), NULL) != 0) { - goto retry; - } - - return NULL; -} - - -typedef struct { - SocketAddress *addr; - const char *reconnect; - bool wait_connected; - bool fd_pass; - char_socket_cb event_cb; -} CharSocketClientTestConfig; - -static void char_socket_client_dupid_test(gconstpointer opaque) -{ - const CharSocketClientTestConfig *config = opaque; - QIOChannelSocket *ioc; - char *optstr; - Chardev *chr1, *chr2; - SocketAddress *addr; - QemuOpts *opts; - Error *local_err = NULL; - - /* - * Setup a listener socket and determine get its address - * so we know the TCP port for the client later - */ - ioc = qio_channel_socket_new(); - g_assert_nonnull(ioc); - qio_channel_socket_listen_sync(ioc, config->addr, 1, &error_abort); - addr = qio_channel_socket_get_local_address(ioc, &error_abort); - g_assert_nonnull(addr); - - /* - * Populate the chardev address based on what the server - * is actually listening on - */ - optstr = char_socket_addr_to_opt_str(addr, - config->fd_pass, - config->reconnect, - false); - - opts = qemu_opts_parse_noisily(qemu_find_opts("chardev"), - optstr, true); - g_assert_nonnull(opts); - chr1 = qemu_chr_new_from_opts(opts, NULL, &error_abort); - g_assert_nonnull(chr1); - qemu_chr_wait_connected(chr1, &error_abort); - - chr2 = qemu_chr_new_from_opts(opts, NULL, &local_err); - g_assert_null(chr2); - error_free_or_abort(&local_err); - - object_unref(OBJECT(ioc)); - qemu_opts_del(opts); - object_unparent(OBJECT(chr1)); - qapi_free_SocketAddress(addr); - g_free(optstr); -} - -static void char_socket_client_test(gconstpointer opaque) -{ - const CharSocketClientTestConfig *config = opaque; - const char_socket_cb event_cb = config->event_cb; - QIOChannelSocket *ioc; - char *optstr; - Chardev *chr; - CharBackend be = {0}; - CharSocketTestData data = {0}; - SocketAddress *addr; - QemuThread thread; - int ret; - bool reconnected = false; - QemuOpts *opts; - - /* - * Setup a listener socket and determine get its address - * so we know the TCP port for the client later - */ - ioc = qio_channel_socket_new(); - g_assert_nonnull(ioc); - qio_channel_socket_listen_sync(ioc, config->addr, 1, &error_abort); - addr = qio_channel_socket_get_local_address(ioc, &error_abort); - g_assert_nonnull(addr); - - /* - * Kick off a thread to act as the "remote" client - * which just plays ping-pong with us - */ - qemu_thread_create(&thread, "client", - char_socket_client_server_thread, - ioc, QEMU_THREAD_JOINABLE); - - /* - * Populate the chardev address based on what the server - * is actually listening on - */ - optstr = char_socket_addr_to_opt_str(addr, - config->fd_pass, - config->reconnect, - false); - - opts = qemu_opts_parse_noisily(qemu_find_opts("chardev"), - optstr, true); - g_assert_nonnull(opts); - chr = qemu_chr_new_from_opts(opts, NULL, &error_abort); - qemu_opts_del(opts); - g_assert_nonnull(chr); - - if (config->reconnect) { - /* - * If reconnect is set, the connection will be - * established in a background thread and we won't - * see the "connected" status updated until we - * run the main event loop, or call qemu_chr_wait_connected - */ - g_assert(!object_property_get_bool(OBJECT(chr), "connected", - &error_abort)); - } else { - g_assert(object_property_get_bool(OBJECT(chr), "connected", - &error_abort)); - } - - qemu_chr_fe_init(&be, chr, &error_abort); - - reconnect: - data.event = -1; - data.be = &be; - qemu_chr_fe_set_handlers(&be, NULL, NULL, - event_cb, NULL, - &data, NULL, true); - if (config->reconnect) { - g_assert(data.event == -1); - } else { - g_assert(data.event == CHR_EVENT_OPENED); - } - - if (config->wait_connected) { - /* - * Synchronously wait for the connection to complete - * This should be a no-op if reconnect is not set. - */ - qemu_chr_wait_connected(chr, &error_abort); - } else { - /* - * Asynchronously wait for the connection to be reported - * as complete when the background thread reports its - * status. - * The loop will short-circuit if reconnect was set - */ - while (data.event == -1) { - main_loop_wait(false); - } - } - g_assert(data.event == CHR_EVENT_OPENED); - data.event = -1; - g_assert(object_property_get_bool(OBJECT(chr), "connected", &error_abort)); - - /* Send a greeting to the server */ - ret = qemu_chr_fe_write_all(&be, (const uint8_t *)SOCKET_PING, - sizeof(SOCKET_PING)); - g_assert_cmpint(ret, ==, sizeof(SOCKET_PING)); - g_assert(data.event == -1); - - /* Setup a callback to receive the reply to our greeting */ - qemu_chr_fe_set_handlers(&be, char_socket_can_read, - char_socket_read, - event_cb, NULL, - &data, NULL, true); - g_assert(data.event == CHR_EVENT_OPENED); - data.event = -1; - - /* Wait for the server to go away */ - while (data.event == -1) { - main_loop_wait(false); - } - g_assert(data.event == CHR_EVENT_CLOSED); - g_assert(!object_property_get_bool(OBJECT(chr), "connected", &error_abort)); - g_assert(data.got_pong); - qemu_thread_join(&thread); - - if (config->reconnect && !reconnected) { - reconnected = true; - qemu_thread_create(&thread, "client", - char_socket_client_server_thread, - ioc, QEMU_THREAD_JOINABLE); - goto reconnect; - } - - object_unref(OBJECT(ioc)); - object_unparent(OBJECT(chr)); - qapi_free_SocketAddress(addr); - g_free(optstr); -} - -static void -count_closed_event(void *opaque, QEMUChrEvent event) -{ - int *count = opaque; - if (event == CHR_EVENT_CLOSED) { - (*count)++; - } -} - -static void -char_socket_discard_read(void *opaque, const uint8_t *buf, int size) -{ -} - -static void char_socket_server_two_clients_test(gconstpointer opaque) -{ - SocketAddress *incoming_addr = (gpointer) opaque; - Chardev *chr; - CharBackend be = {0}; - QObject *qaddr; - SocketAddress *addr; - Visitor *v; - char *optstr; - QemuOpts *opts; - QIOChannelSocket *ioc1, *ioc2; - int closed = 0; - - g_setenv("QTEST_SILENT_ERRORS", "1", 1); - /* - * We rely on addr containing "wait=off", otherwise - * qemu_chr_new() will block until a client connects. We - * can't spawn our client thread though, because until - * qemu_chr_new() returns we don't know what TCP port was - * allocated by the OS - */ - optstr = char_socket_addr_to_opt_str(incoming_addr, - false, - NULL, - true); - opts = qemu_opts_parse_noisily(qemu_find_opts("chardev"), - optstr, true); - g_assert_nonnull(opts); - chr = qemu_chr_new_from_opts(opts, NULL, &error_abort); - qemu_opts_del(opts); - g_assert_nonnull(chr); - g_assert(!object_property_get_bool(OBJECT(chr), "connected", &error_abort)); - - qaddr = object_property_get_qobject(OBJECT(chr), "addr", &error_abort); - g_assert_nonnull(qaddr); - - v = qobject_input_visitor_new(qaddr); - visit_type_SocketAddress(v, "addr", &addr, &error_abort); - visit_free(v); - qobject_unref(qaddr); - - qemu_chr_fe_init(&be, chr, &error_abort); - - qemu_chr_fe_set_handlers(&be, char_socket_can_read, char_socket_discard_read, - count_closed_event, NULL, - &closed, NULL, true); - - ioc1 = qio_channel_socket_new(); - qio_channel_socket_connect_sync(ioc1, addr, &error_abort); - qemu_chr_wait_connected(chr, &error_abort); - - /* switch the chardev to another context */ - GMainContext *ctx = g_main_context_new(); - qemu_chr_fe_set_handlers(&be, char_socket_can_read, char_socket_discard_read, - count_closed_event, NULL, - &closed, ctx, true); - - /* Start a second connection while the first is still connected. - * It will be placed in the listen() backlog, and connect() will - * succeed immediately. - */ - ioc2 = qio_channel_socket_new(); - qio_channel_socket_connect_sync(ioc2, addr, &error_abort); - - object_unref(OBJECT(ioc1)); - /* The two connections should now be processed serially. */ - while (g_main_context_iteration(ctx, TRUE)) { - if (closed == 1 && ioc2) { - object_unref(OBJECT(ioc2)); - ioc2 = NULL; - } - if (closed == 2) { - break; - } - } - - qapi_free_SocketAddress(addr); - object_unparent(OBJECT(chr)); - g_main_context_unref(ctx); - g_free(optstr); - g_unsetenv("QTEST_SILENT_ERRORS"); -} - - -#if defined(HAVE_CHARDEV_SERIAL) && !defined(WIN32) -static void char_serial_test(void) -{ - QemuOpts *opts; - Chardev *chr; - - opts = qemu_opts_create(qemu_find_opts("chardev"), "serial-id", - 1, &error_abort); - qemu_opt_set(opts, "backend", "serial", &error_abort); - qemu_opt_set(opts, "path", "/dev/null", &error_abort); - - chr = qemu_chr_new_from_opts(opts, NULL, NULL); - g_assert_nonnull(chr); - /* TODO: add more tests with a pty */ - object_unparent(OBJECT(chr)); - - /* test tty alias */ - qemu_opt_set(opts, "backend", "tty", &error_abort); - chr = qemu_chr_new_from_opts(opts, NULL, &error_abort); - g_assert_nonnull(chr); - object_unparent(OBJECT(chr)); - - qemu_opts_del(opts); -} -#endif - -#ifndef _WIN32 -static void char_file_fifo_test(void) -{ - Chardev *chr; - CharBackend be; - char *tmp_path = g_dir_make_tmp("qemu-test-char.XXXXXX", NULL); - char *fifo = g_build_filename(tmp_path, "fifo", NULL); - char *out = g_build_filename(tmp_path, "out", NULL); - ChardevFile file = { .in = fifo, - .has_in = true, - .out = out }; - ChardevBackend backend = { .type = CHARDEV_BACKEND_KIND_FILE, - .u.file.data = &file }; - FeHandler fe = { 0, }; - int fd, ret; - - if (mkfifo(fifo, 0600) < 0) { - abort(); - } - - fd = open(fifo, O_RDWR); - ret = write(fd, "fifo-in", 8); - g_assert_cmpint(ret, ==, 8); - - chr = qemu_chardev_new("label-file", TYPE_CHARDEV_FILE, &backend, - NULL, &error_abort); - - qemu_chr_fe_init(&be, chr, &error_abort); - qemu_chr_fe_set_handlers(&be, - fe_can_read, - fe_read, - fe_event, - NULL, - &fe, NULL, true); - - g_assert_cmpint(fe.last_event, !=, CHR_EVENT_BREAK); - qmp_chardev_send_break("label-foo", NULL); - g_assert_cmpint(fe.last_event, !=, CHR_EVENT_BREAK); - qmp_chardev_send_break("label-file", NULL); - g_assert_cmpint(fe.last_event, ==, CHR_EVENT_BREAK); - - main_loop(); - - close(fd); - - g_assert_cmpint(fe.read_count, ==, 8); - g_assert_cmpstr(fe.read_buf, ==, "fifo-in"); - - qemu_chr_fe_deinit(&be, true); - - g_unlink(fifo); - g_free(fifo); - g_unlink(out); - g_free(out); - g_rmdir(tmp_path); - g_free(tmp_path); -} -#endif - -static void char_file_test_internal(Chardev *ext_chr, const char *filepath) -{ - char *tmp_path = g_dir_make_tmp("qemu-test-char.XXXXXX", NULL); - char *out; - Chardev *chr; - char *contents = NULL; - ChardevFile file = {}; - ChardevBackend backend = { .type = CHARDEV_BACKEND_KIND_FILE, - .u.file.data = &file }; - gsize length; - int ret; - - if (ext_chr) { - chr = ext_chr; - out = g_strdup(filepath); - file.out = out; - } else { - out = g_build_filename(tmp_path, "out", NULL); - file.out = out; - chr = qemu_chardev_new(NULL, TYPE_CHARDEV_FILE, &backend, - NULL, &error_abort); - } - ret = qemu_chr_write_all(chr, (uint8_t *)"hello!", 6); - g_assert_cmpint(ret, ==, 6); - - ret = g_file_get_contents(out, &contents, &length, NULL); - g_assert(ret == TRUE); - g_assert_cmpint(length, ==, 6); - g_assert(strncmp(contents, "hello!", 6) == 0); - - if (!ext_chr) { - object_unparent(OBJECT(chr)); - g_unlink(out); - } - g_free(contents); - g_rmdir(tmp_path); - g_free(tmp_path); - g_free(out); -} - -static void char_file_test(void) -{ - char_file_test_internal(NULL, NULL); -} - -static void char_null_test(void) -{ - Error *err = NULL; - Chardev *chr; - CharBackend be; - int ret; - - chr = qemu_chr_find("label-null"); - g_assert_null(chr); - - chr = qemu_chr_new("label-null", "null", NULL); - chr = qemu_chr_find("label-null"); - g_assert_nonnull(chr); - - g_assert(qemu_chr_has_feature(chr, - QEMU_CHAR_FEATURE_FD_PASS) == false); - g_assert(qemu_chr_has_feature(chr, - QEMU_CHAR_FEATURE_RECONNECTABLE) == false); - - /* check max avail */ - qemu_chr_fe_init(&be, chr, &error_abort); - qemu_chr_fe_init(&be, chr, &err); - error_free_or_abort(&err); - - /* deinit & reinit */ - qemu_chr_fe_deinit(&be, false); - qemu_chr_fe_init(&be, chr, &error_abort); - - qemu_chr_fe_set_open(&be, true); - - qemu_chr_fe_set_handlers(&be, - fe_can_read, - fe_read, - fe_event, - NULL, - NULL, NULL, true); - - ret = qemu_chr_fe_write(&be, (void *)"buf", 4); - g_assert_cmpint(ret, ==, 4); - - qemu_chr_fe_deinit(&be, true); -} - -static void char_invalid_test(void) -{ - Chardev *chr; - g_setenv("QTEST_SILENT_ERRORS", "1", 1); - chr = qemu_chr_new("label-invalid", "invalid", NULL); - g_assert_null(chr); - g_unsetenv("QTEST_SILENT_ERRORS"); -} - -static int chardev_change(void *opaque) -{ - return 0; -} - -static int chardev_change_denied(void *opaque) -{ - return -1; -} - -static void char_hotswap_test(void) -{ - char *chr_args; - Chardev *chr; - CharBackend be; - - gchar *tmp_path = g_dir_make_tmp("qemu-test-char.XXXXXX", NULL); - char *filename = g_build_filename(tmp_path, "file", NULL); - ChardevFile file = { .out = filename }; - ChardevBackend backend = { .type = CHARDEV_BACKEND_KIND_FILE, - .u.file.data = &file }; - ChardevReturn *ret; - - int port; - int sock = make_udp_socket(&port); - g_assert_cmpint(sock, >, 0); - - chr_args = g_strdup_printf("udp:127.0.0.1:%d", port); - - chr = qemu_chr_new("chardev", chr_args, NULL); - qemu_chr_fe_init(&be, chr, &error_abort); - - /* check that chardev operates correctly */ - char_udp_test_internal(chr, sock); - - /* set the handler that denies the hotswap */ - qemu_chr_fe_set_handlers(&be, NULL, NULL, - NULL, chardev_change_denied, NULL, NULL, true); - - /* now, change is denied and has to keep the old backend operating */ - ret = qmp_chardev_change("chardev", &backend, NULL); - g_assert(!ret); - g_assert(be.chr == chr); - - char_udp_test_internal(chr, sock); - - /* now allow the change */ - qemu_chr_fe_set_handlers(&be, NULL, NULL, - NULL, chardev_change, NULL, NULL, true); - - /* has to succeed now */ - ret = qmp_chardev_change("chardev", &backend, &error_abort); - g_assert(be.chr != chr); - - close(sock); - chr = be.chr; - - /* run the file chardev test */ - char_file_test_internal(chr, filename); - - object_unparent(OBJECT(chr)); - - qapi_free_ChardevReturn(ret); - g_unlink(filename); - g_free(filename); - g_rmdir(tmp_path); - g_free(tmp_path); - g_free(chr_args); -} - -static SocketAddress tcpaddr = { - .type = SOCKET_ADDRESS_TYPE_INET, - .u.inet.host = (char *)"127.0.0.1", - .u.inet.port = (char *)"0", -}; -#ifndef WIN32 -static SocketAddress unixaddr = { - .type = SOCKET_ADDRESS_TYPE_UNIX, - .u.q_unix.path = (char *)"test-char.sock", -}; -#endif - -int main(int argc, char **argv) -{ - bool has_ipv4, has_ipv6; - - qemu_init_main_loop(&error_abort); - socket_init(); - - g_test_init(&argc, &argv, NULL); - - if (socket_check_protocol_support(&has_ipv4, &has_ipv6) < 0) { - g_printerr("socket_check_protocol_support() failed\n"); - goto end; - } - - module_call_init(MODULE_INIT_QOM); - qemu_add_opts(&qemu_chardev_opts); - - g_test_add_func("/char/null", char_null_test); - g_test_add_func("/char/invalid", char_invalid_test); - g_test_add_func("/char/ringbuf", char_ringbuf_test); - g_test_add_func("/char/mux", char_mux_test); -#ifdef _WIN32 - g_test_add_func("/char/console/subprocess", char_console_test_subprocess); - g_test_add_func("/char/console", char_console_test); -#endif - g_test_add_func("/char/stdio/subprocess", char_stdio_test_subprocess); - g_test_add_func("/char/stdio", char_stdio_test); -#ifndef _WIN32 - g_test_add_func("/char/pipe", char_pipe_test); -#endif - g_test_add_func("/char/file", char_file_test); -#ifndef _WIN32 - g_test_add_func("/char/file-fifo", char_file_fifo_test); -#endif - -#define SOCKET_SERVER_TEST(name, addr) \ - static CharSocketServerTestConfig server1 ## name = \ - { addr, false, false }; \ - static CharSocketServerTestConfig server2 ## name = \ - { addr, true, false }; \ - static CharSocketServerTestConfig server3 ## name = \ - { addr, false, true }; \ - static CharSocketServerTestConfig server4 ## name = \ - { addr, true, true }; \ - g_test_add_data_func("/char/socket/server/mainloop/" # name, \ - &server1 ##name, char_socket_server_test); \ - g_test_add_data_func("/char/socket/server/wait-conn/" # name, \ - &server2 ##name, char_socket_server_test); \ - g_test_add_data_func("/char/socket/server/mainloop-fdpass/" # name, \ - &server3 ##name, char_socket_server_test); \ - g_test_add_data_func("/char/socket/server/wait-conn-fdpass/" # name, \ - &server4 ##name, char_socket_server_test) - -#define SOCKET_CLIENT_TEST(name, addr) \ - static CharSocketClientTestConfig client1 ## name = \ - { addr, NULL, false, false, char_socket_event }; \ - static CharSocketClientTestConfig client2 ## name = \ - { addr, NULL, true, false, char_socket_event }; \ - static CharSocketClientTestConfig client3 ## name = \ - { addr, ",reconnect=1", false, false, char_socket_event }; \ - static CharSocketClientTestConfig client4 ## name = \ - { addr, ",reconnect=1", true, false, char_socket_event }; \ - static CharSocketClientTestConfig client5 ## name = \ - { addr, NULL, false, true, char_socket_event }; \ - static CharSocketClientTestConfig client6 ## name = \ - { addr, NULL, true, true, char_socket_event }; \ - static CharSocketClientTestConfig client7 ## name = \ - { addr, ",reconnect=1", true, false, \ - char_socket_event_with_error }; \ - static CharSocketClientTestConfig client8 ## name = \ - { addr, ",reconnect=1", false, false, char_socket_event }; \ - g_test_add_data_func("/char/socket/client/mainloop/" # name, \ - &client1 ##name, char_socket_client_test); \ - g_test_add_data_func("/char/socket/client/wait-conn/" # name, \ - &client2 ##name, char_socket_client_test); \ - g_test_add_data_func("/char/socket/client/mainloop-reconnect/" # name, \ - &client3 ##name, char_socket_client_test); \ - g_test_add_data_func("/char/socket/client/wait-conn-reconnect/" # name, \ - &client4 ##name, char_socket_client_test); \ - g_test_add_data_func("/char/socket/client/mainloop-fdpass/" # name, \ - &client5 ##name, char_socket_client_test); \ - g_test_add_data_func("/char/socket/client/wait-conn-fdpass/" # name, \ - &client6 ##name, char_socket_client_test); \ - g_test_add_data_func("/char/socket/client/reconnect-error/" # name, \ - &client7 ##name, char_socket_client_test); \ - g_test_add_data_func("/char/socket/client/dupid-reconnect/" # name, \ - &client8 ##name, char_socket_client_dupid_test) - - if (has_ipv4) { - SOCKET_SERVER_TEST(tcp, &tcpaddr); - SOCKET_CLIENT_TEST(tcp, &tcpaddr); - g_test_add_data_func("/char/socket/server/two-clients/tcp", &tcpaddr, - char_socket_server_two_clients_test); - } -#ifndef WIN32 - SOCKET_SERVER_TEST(unix, &unixaddr); - SOCKET_CLIENT_TEST(unix, &unixaddr); - g_test_add_data_func("/char/socket/server/two-clients/unix", &unixaddr, - char_socket_server_two_clients_test); -#endif - - g_test_add_func("/char/udp", char_udp_test); -#if defined(HAVE_CHARDEV_SERIAL) && !defined(WIN32) - g_test_add_func("/char/serial", char_serial_test); -#endif - g_test_add_func("/char/hotswap", char_hotswap_test); - g_test_add_func("/char/websocket", char_websock_test); - -end: - return g_test_run(); -} diff --git a/tests/test-clone-visitor.c b/tests/test-clone-visitor.c deleted file mode 100644 index 4944b3d857..0000000000 --- a/tests/test-clone-visitor.c +++ /dev/null @@ -1,199 +0,0 @@ -/* - * QAPI Clone Visitor unit-tests. - * - * Copyright (C) 2016 Red Hat Inc. - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" - -#include "qemu-common.h" -#include "qapi/clone-visitor.h" -#include "test-qapi-visit.h" - -static void test_clone_struct(void) -{ - UserDefOne *src, *dst; - - src = g_new0(UserDefOne, 1); - src->integer = 42; - src->string = g_strdup("Hello"); - src->has_enum1 = false; - src->enum1 = ENUM_ONE_VALUE2; - - dst = QAPI_CLONE(UserDefOne, src); - g_assert(dst); - g_assert_cmpint(dst->integer, ==, 42); - g_assert(dst->string != src->string); - g_assert_cmpstr(dst->string, ==, "Hello"); - g_assert_cmpint(dst->has_enum1, ==, false); - /* Our implementation does this, but it is not required: - g_assert_cmpint(dst->enum1, ==, ENUM_ONE_VALUE2); - */ - - qapi_free_UserDefOne(src); - qapi_free_UserDefOne(dst); -} - -static void test_clone_alternate(void) -{ - AltEnumBool *b_src, *s_src, *b_dst, *s_dst; - - b_src = g_new0(AltEnumBool, 1); - b_src->type = QTYPE_QBOOL; - b_src->u.b = true; - s_src = g_new0(AltEnumBool, 1); - s_src->type = QTYPE_QSTRING; - s_src->u.e = ENUM_ONE_VALUE1; - - b_dst = QAPI_CLONE(AltEnumBool, b_src); - g_assert(b_dst); - g_assert_cmpint(b_dst->type, ==, b_src->type); - g_assert_cmpint(b_dst->u.b, ==, b_src->u.b); - s_dst = QAPI_CLONE(AltEnumBool, s_src); - g_assert(s_dst); - g_assert_cmpint(s_dst->type, ==, s_src->type); - g_assert_cmpint(s_dst->u.e, ==, s_src->u.e); - - qapi_free_AltEnumBool(b_src); - qapi_free_AltEnumBool(s_src); - qapi_free_AltEnumBool(b_dst); - qapi_free_AltEnumBool(s_dst); -} - -static void test_clone_list_union(void) -{ - uint8List *src = NULL, *dst; - uint8List *tmp = NULL; - int i; - - /* Build list in reverse */ - for (i = 10; i; i--) { - QAPI_LIST_PREPEND(src, i); - } - - dst = QAPI_CLONE(uint8List, src); - for (tmp = dst, i = 1; i <= 10; i++) { - g_assert(tmp); - g_assert_cmpint(tmp->value, ==, i); - tmp = tmp->next; - } - g_assert(!tmp); - - qapi_free_uint8List(src); - qapi_free_uint8List(dst); -} - -static void test_clone_empty(void) -{ - Empty2 *src, *dst; - - src = g_new0(Empty2, 1); - dst = QAPI_CLONE(Empty2, src); - g_assert(dst); - qapi_free_Empty2(src); - qapi_free_Empty2(dst); -} - -static void test_clone_complex1(void) -{ - UserDefListUnion *src, *dst; - - src = g_new0(UserDefListUnion, 1); - src->type = USER_DEF_LIST_UNION_KIND_STRING; - - dst = QAPI_CLONE(UserDefListUnion, src); - g_assert(dst); - g_assert_cmpint(dst->type, ==, src->type); - g_assert(!dst->u.string.data); - - qapi_free_UserDefListUnion(src); - qapi_free_UserDefListUnion(dst); -} - -static void test_clone_complex2(void) -{ - WrapAlternate *src, *dst; - - src = g_new0(WrapAlternate, 1); - src->alt = g_new(UserDefAlternate, 1); - src->alt->type = QTYPE_QDICT; - src->alt->u.udfu.integer = 42; - /* Clone intentionally converts NULL into "" for strings */ - src->alt->u.udfu.string = NULL; - src->alt->u.udfu.enum1 = ENUM_ONE_VALUE3; - src->alt->u.udfu.u.value3.intb = 99; - src->alt->u.udfu.u.value3.has_a_b = true; - src->alt->u.udfu.u.value3.a_b = true; - - dst = QAPI_CLONE(WrapAlternate, src); - g_assert(dst); - g_assert(dst->alt); - g_assert_cmpint(dst->alt->type, ==, QTYPE_QDICT); - g_assert_cmpint(dst->alt->u.udfu.integer, ==, 42); - g_assert_cmpstr(dst->alt->u.udfu.string, ==, ""); - g_assert_cmpint(dst->alt->u.udfu.enum1, ==, ENUM_ONE_VALUE3); - g_assert_cmpint(dst->alt->u.udfu.u.value3.intb, ==, 99); - g_assert_cmpint(dst->alt->u.udfu.u.value3.has_a_b, ==, true); - g_assert_cmpint(dst->alt->u.udfu.u.value3.a_b, ==, true); - - qapi_free_WrapAlternate(src); - qapi_free_WrapAlternate(dst); -} - -static void test_clone_complex3(void) -{ - __org_qemu_x_Struct2 *src, *dst; - __org_qemu_x_Union1List *tmp; - - src = g_new0(__org_qemu_x_Struct2, 1); - tmp = src->array = g_new0(__org_qemu_x_Union1List, 1); - tmp->value = g_new0(__org_qemu_x_Union1, 1); - tmp->value->type = ORG_QEMU_X_UNION1_KIND___ORG_QEMU_X_BRANCH; - tmp->value->u.__org_qemu_x_branch.data = g_strdup("one"); - tmp = tmp->next = g_new0(__org_qemu_x_Union1List, 1); - tmp->value = g_new0(__org_qemu_x_Union1, 1); - tmp->value->type = ORG_QEMU_X_UNION1_KIND___ORG_QEMU_X_BRANCH; - tmp->value->u.__org_qemu_x_branch.data = g_strdup("two"); - tmp = tmp->next = g_new0(__org_qemu_x_Union1List, 1); - tmp->value = g_new0(__org_qemu_x_Union1, 1); - tmp->value->type = ORG_QEMU_X_UNION1_KIND___ORG_QEMU_X_BRANCH; - tmp->value->u.__org_qemu_x_branch.data = g_strdup("three"); - - dst = QAPI_CLONE(__org_qemu_x_Struct2, src); - g_assert(dst); - tmp = dst->array; - g_assert(tmp); - g_assert(tmp->value); - g_assert_cmpstr(tmp->value->u.__org_qemu_x_branch.data, ==, "one"); - tmp = tmp->next; - g_assert(tmp); - g_assert(tmp->value); - g_assert_cmpstr(tmp->value->u.__org_qemu_x_branch.data, ==, "two"); - tmp = tmp->next; - g_assert(tmp); - g_assert(tmp->value); - g_assert_cmpstr(tmp->value->u.__org_qemu_x_branch.data, ==, "three"); - tmp = tmp->next; - g_assert(!tmp); - - qapi_free___org_qemu_x_Struct2(src); - qapi_free___org_qemu_x_Struct2(dst); -} - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - - g_test_add_func("/visitor/clone/struct", test_clone_struct); - g_test_add_func("/visitor/clone/alternate", test_clone_alternate); - g_test_add_func("/visitor/clone/list_union", test_clone_list_union); - g_test_add_func("/visitor/clone/empty", test_clone_empty); - g_test_add_func("/visitor/clone/complex1", test_clone_complex1); - g_test_add_func("/visitor/clone/complex2", test_clone_complex2); - g_test_add_func("/visitor/clone/complex3", test_clone_complex3); - - return g_test_run(); -} diff --git a/tests/test-coroutine.c b/tests/test-coroutine.c deleted file mode 100644 index e946d93a65..0000000000 --- a/tests/test-coroutine.c +++ /dev/null @@ -1,512 +0,0 @@ -/* - * Coroutine tests - * - * Copyright IBM, Corp. 2011 - * - * Authors: - * Stefan Hajnoczi - * - * This work is licensed under the terms of the GNU LGPL, version 2 or later. - * See the COPYING.LIB file in the top-level directory. - * - */ - -#include "qemu/osdep.h" -#include "qemu/coroutine.h" -#include "qemu/coroutine_int.h" -#include "qemu/lockable.h" - -/* - * Check that qemu_in_coroutine() works - */ - -static void coroutine_fn verify_in_coroutine(void *opaque) -{ - g_assert(qemu_in_coroutine()); -} - -static void test_in_coroutine(void) -{ - Coroutine *coroutine; - - g_assert(!qemu_in_coroutine()); - - coroutine = qemu_coroutine_create(verify_in_coroutine, NULL); - qemu_coroutine_enter(coroutine); -} - -/* - * Check that qemu_coroutine_self() works - */ - -static void coroutine_fn verify_self(void *opaque) -{ - Coroutine **p_co = opaque; - g_assert(qemu_coroutine_self() == *p_co); -} - -static void test_self(void) -{ - Coroutine *coroutine; - - coroutine = qemu_coroutine_create(verify_self, &coroutine); - qemu_coroutine_enter(coroutine); -} - -/* - * Check that qemu_coroutine_entered() works - */ - -static void coroutine_fn verify_entered_step_2(void *opaque) -{ - Coroutine *caller = (Coroutine *)opaque; - - g_assert(qemu_coroutine_entered(caller)); - g_assert(qemu_coroutine_entered(qemu_coroutine_self())); - qemu_coroutine_yield(); - - /* Once more to check it still works after yielding */ - g_assert(qemu_coroutine_entered(caller)); - g_assert(qemu_coroutine_entered(qemu_coroutine_self())); -} - -static void coroutine_fn verify_entered_step_1(void *opaque) -{ - Coroutine *self = qemu_coroutine_self(); - Coroutine *coroutine; - - g_assert(qemu_coroutine_entered(self)); - - coroutine = qemu_coroutine_create(verify_entered_step_2, self); - g_assert(!qemu_coroutine_entered(coroutine)); - qemu_coroutine_enter(coroutine); - g_assert(!qemu_coroutine_entered(coroutine)); - qemu_coroutine_enter(coroutine); -} - -static void test_entered(void) -{ - Coroutine *coroutine; - - coroutine = qemu_coroutine_create(verify_entered_step_1, NULL); - g_assert(!qemu_coroutine_entered(coroutine)); - qemu_coroutine_enter(coroutine); -} - -/* - * Check that coroutines may nest multiple levels - */ - -typedef struct { - unsigned int n_enter; /* num coroutines entered */ - unsigned int n_return; /* num coroutines returned */ - unsigned int max; /* maximum level of nesting */ -} NestData; - -static void coroutine_fn nest(void *opaque) -{ - NestData *nd = opaque; - - nd->n_enter++; - - if (nd->n_enter < nd->max) { - Coroutine *child; - - child = qemu_coroutine_create(nest, nd); - qemu_coroutine_enter(child); - } - - nd->n_return++; -} - -static void test_nesting(void) -{ - Coroutine *root; - NestData nd = { - .n_enter = 0, - .n_return = 0, - .max = 128, - }; - - root = qemu_coroutine_create(nest, &nd); - qemu_coroutine_enter(root); - - /* Must enter and return from max nesting level */ - g_assert_cmpint(nd.n_enter, ==, nd.max); - g_assert_cmpint(nd.n_return, ==, nd.max); -} - -/* - * Check that yield/enter transfer control correctly - */ - -static void coroutine_fn yield_5_times(void *opaque) -{ - bool *done = opaque; - int i; - - for (i = 0; i < 5; i++) { - qemu_coroutine_yield(); - } - *done = true; -} - -static void test_yield(void) -{ - Coroutine *coroutine; - bool done = false; - int i = -1; /* one extra time to return from coroutine */ - - coroutine = qemu_coroutine_create(yield_5_times, &done); - while (!done) { - qemu_coroutine_enter(coroutine); - i++; - } - g_assert_cmpint(i, ==, 5); /* coroutine must yield 5 times */ -} - -static void coroutine_fn c2_fn(void *opaque) -{ - qemu_coroutine_yield(); -} - -static void coroutine_fn c1_fn(void *opaque) -{ - Coroutine *c2 = opaque; - qemu_coroutine_enter(c2); -} - -static void test_no_dangling_access(void) -{ - Coroutine *c1; - Coroutine *c2; - Coroutine tmp; - - c2 = qemu_coroutine_create(c2_fn, NULL); - c1 = qemu_coroutine_create(c1_fn, c2); - - qemu_coroutine_enter(c1); - - /* c1 shouldn't be used any more now; make sure we segfault if it is */ - tmp = *c1; - memset(c1, 0xff, sizeof(Coroutine)); - qemu_coroutine_enter(c2); - - /* Must restore the coroutine now to avoid corrupted pool */ - *c1 = tmp; -} - -static bool locked; -static int done; - -static void coroutine_fn mutex_fn(void *opaque) -{ - CoMutex *m = opaque; - qemu_co_mutex_lock(m); - assert(!locked); - locked = true; - qemu_coroutine_yield(); - locked = false; - qemu_co_mutex_unlock(m); - done++; -} - -static void coroutine_fn lockable_fn(void *opaque) -{ - QemuLockable *x = opaque; - qemu_lockable_lock(x); - assert(!locked); - locked = true; - qemu_coroutine_yield(); - locked = false; - qemu_lockable_unlock(x); - done++; -} - -static void do_test_co_mutex(CoroutineEntry *entry, void *opaque) -{ - Coroutine *c1 = qemu_coroutine_create(entry, opaque); - Coroutine *c2 = qemu_coroutine_create(entry, opaque); - - done = 0; - qemu_coroutine_enter(c1); - g_assert(locked); - qemu_coroutine_enter(c2); - - /* Unlock queues c2. It is then started automatically when c1 yields or - * terminates. - */ - qemu_coroutine_enter(c1); - g_assert_cmpint(done, ==, 1); - g_assert(locked); - - qemu_coroutine_enter(c2); - g_assert_cmpint(done, ==, 2); - g_assert(!locked); -} - -static void test_co_mutex(void) -{ - CoMutex m; - - qemu_co_mutex_init(&m); - do_test_co_mutex(mutex_fn, &m); -} - -static void test_co_mutex_lockable(void) -{ - CoMutex m; - CoMutex *null_pointer = NULL; - - qemu_co_mutex_init(&m); - do_test_co_mutex(lockable_fn, QEMU_MAKE_LOCKABLE(&m)); - - g_assert(QEMU_MAKE_LOCKABLE(null_pointer) == NULL); -} - -/* - * Check that creation, enter, and return work - */ - -static void coroutine_fn set_and_exit(void *opaque) -{ - bool *done = opaque; - - *done = true; -} - -static void test_lifecycle(void) -{ - Coroutine *coroutine; - bool done = false; - - /* Create, enter, and return from coroutine */ - coroutine = qemu_coroutine_create(set_and_exit, &done); - qemu_coroutine_enter(coroutine); - g_assert(done); /* expect done to be true (first time) */ - - /* Repeat to check that no state affects this test */ - done = false; - coroutine = qemu_coroutine_create(set_and_exit, &done); - qemu_coroutine_enter(coroutine); - g_assert(done); /* expect done to be true (second time) */ -} - - -#define RECORD_SIZE 10 /* Leave some room for expansion */ -struct coroutine_position { - int func; - int state; -}; -static struct coroutine_position records[RECORD_SIZE]; -static unsigned record_pos; - -static void record_push(int func, int state) -{ - struct coroutine_position *cp = &records[record_pos++]; - g_assert_cmpint(record_pos, <, RECORD_SIZE); - cp->func = func; - cp->state = state; -} - -static void coroutine_fn co_order_test(void *opaque) -{ - record_push(2, 1); - g_assert(qemu_in_coroutine()); - qemu_coroutine_yield(); - record_push(2, 2); - g_assert(qemu_in_coroutine()); -} - -static void do_order_test(void) -{ - Coroutine *co; - - co = qemu_coroutine_create(co_order_test, NULL); - record_push(1, 1); - qemu_coroutine_enter(co); - record_push(1, 2); - g_assert(!qemu_in_coroutine()); - qemu_coroutine_enter(co); - record_push(1, 3); - g_assert(!qemu_in_coroutine()); -} - -static void test_order(void) -{ - int i; - const struct coroutine_position expected_pos[] = { - {1, 1,}, {2, 1}, {1, 2}, {2, 2}, {1, 3} - }; - do_order_test(); - g_assert_cmpint(record_pos, ==, 5); - for (i = 0; i < record_pos; i++) { - g_assert_cmpint(records[i].func , ==, expected_pos[i].func ); - g_assert_cmpint(records[i].state, ==, expected_pos[i].state); - } -} -/* - * Lifecycle benchmark - */ - -static void coroutine_fn empty_coroutine(void *opaque) -{ - /* Do nothing */ -} - -static void perf_lifecycle(void) -{ - Coroutine *coroutine; - unsigned int i, max; - double duration; - - max = 1000000; - - g_test_timer_start(); - for (i = 0; i < max; i++) { - coroutine = qemu_coroutine_create(empty_coroutine, NULL); - qemu_coroutine_enter(coroutine); - } - duration = g_test_timer_elapsed(); - - g_test_message("Lifecycle %u iterations: %f s", max, duration); -} - -static void perf_nesting(void) -{ - unsigned int i, maxcycles, maxnesting; - double duration; - - maxcycles = 10000; - maxnesting = 1000; - Coroutine *root; - - g_test_timer_start(); - for (i = 0; i < maxcycles; i++) { - NestData nd = { - .n_enter = 0, - .n_return = 0, - .max = maxnesting, - }; - root = qemu_coroutine_create(nest, &nd); - qemu_coroutine_enter(root); - } - duration = g_test_timer_elapsed(); - - g_test_message("Nesting %u iterations of %u depth each: %f s", - maxcycles, maxnesting, duration); -} - -/* - * Yield benchmark - */ - -static void coroutine_fn yield_loop(void *opaque) -{ - unsigned int *counter = opaque; - - while ((*counter) > 0) { - (*counter)--; - qemu_coroutine_yield(); - } -} - -static void perf_yield(void) -{ - unsigned int i, maxcycles; - double duration; - - maxcycles = 100000000; - i = maxcycles; - Coroutine *coroutine = qemu_coroutine_create(yield_loop, &i); - - g_test_timer_start(); - while (i > 0) { - qemu_coroutine_enter(coroutine); - } - duration = g_test_timer_elapsed(); - - g_test_message("Yield %u iterations: %f s", maxcycles, duration); -} - -static __attribute__((noinline)) void dummy(unsigned *i) -{ - (*i)--; -} - -static void perf_baseline(void) -{ - unsigned int i, maxcycles; - double duration; - - maxcycles = 100000000; - i = maxcycles; - - g_test_timer_start(); - while (i > 0) { - dummy(&i); - } - duration = g_test_timer_elapsed(); - - g_test_message("Function call %u iterations: %f s", maxcycles, duration); -} - -static __attribute__((noinline)) void perf_cost_func(void *opaque) -{ - qemu_coroutine_yield(); -} - -static void perf_cost(void) -{ - const unsigned long maxcycles = 40000000; - unsigned long i = 0; - double duration; - unsigned long ops; - Coroutine *co; - - g_test_timer_start(); - while (i++ < maxcycles) { - co = qemu_coroutine_create(perf_cost_func, &i); - qemu_coroutine_enter(co); - qemu_coroutine_enter(co); - } - duration = g_test_timer_elapsed(); - ops = (long)(maxcycles / (duration * 1000)); - - g_test_message("Run operation %lu iterations %f s, %luK operations/s, " - "%luns per coroutine", - maxcycles, - duration, ops, - (unsigned long)(1000000000.0 * duration / maxcycles)); -} - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - - /* This test assumes there is a freelist and marks freed coroutine memory - * with a sentinel value. If there is no freelist this would legitimately - * crash, so skip it. - */ - if (CONFIG_COROUTINE_POOL) { - g_test_add_func("/basic/no-dangling-access", test_no_dangling_access); - } - - g_test_add_func("/basic/lifecycle", test_lifecycle); - g_test_add_func("/basic/yield", test_yield); - g_test_add_func("/basic/nesting", test_nesting); - g_test_add_func("/basic/self", test_self); - g_test_add_func("/basic/entered", test_entered); - g_test_add_func("/basic/in_coroutine", test_in_coroutine); - g_test_add_func("/basic/order", test_order); - g_test_add_func("/locking/co-mutex", test_co_mutex); - g_test_add_func("/locking/co-mutex/lockable", test_co_mutex_lockable); - if (g_test_perf()) { - g_test_add_func("/perf/lifecycle", perf_lifecycle); - g_test_add_func("/perf/nesting", perf_nesting); - g_test_add_func("/perf/yield", perf_yield); - g_test_add_func("/perf/function-call", perf_baseline); - g_test_add_func("/perf/cost", perf_cost); - } - return g_test_run(); -} diff --git a/tests/test-crypto-afsplit.c b/tests/test-crypto-afsplit.c deleted file mode 100644 index 00a7c180fd..0000000000 --- a/tests/test-crypto-afsplit.c +++ /dev/null @@ -1,194 +0,0 @@ -/* - * QEMU Crypto anti-forensic splitter - * - * Copyright (c) 2015-2016 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - * - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "crypto/init.h" -#include "crypto/afsplit.h" - -typedef struct QCryptoAFSplitTestData QCryptoAFSplitTestData; -struct QCryptoAFSplitTestData { - const char *path; - QCryptoHashAlgorithm hash; - uint32_t stripes; - size_t blocklen; - const uint8_t *key; - const uint8_t *splitkey; -}; - -static QCryptoAFSplitTestData test_data[] = { - { - .path = "/crypto/afsplit/sha256/5", - .hash = QCRYPTO_HASH_ALG_SHA256, - .stripes = 5, - .blocklen = 32, - .key = (const uint8_t *) - "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" - "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7" - "\xa8\xa9\xaa\xab\xac\xad\xae\xaf", - .splitkey = (const uint8_t *) - "\xfd\xd2\x73\xb1\x7d\x99\x93\x34" - "\x70\xde\xfa\x07\xc5\xac\x58\xd2" - "\x30\x67\x2f\x1a\x35\x43\x60\x7d" - "\x77\x02\xdb\x62\x3c\xcb\x2c\x33" - "\x48\x08\xb6\xf1\x7c\xa3\x20\xa0" - "\xad\x2d\x4c\xf3\xcd\x18\x6f\x53" - "\xf9\xe8\xe7\x59\x27\x3c\xa9\x54" - "\x61\x87\xb3\xaf\xf6\xf7\x7e\x64" - "\x86\xaa\x89\x7f\x1f\x9f\xdb\x86" - "\xf4\xa2\x16\xff\xa3\x4f\x8c\xa1" - "\x59\xc4\x23\x34\x28\xc4\x77\x71" - "\x83\xd4\xcd\x8e\x89\x1b\xc7\xc5" - "\xae\x4d\xa9\xcd\xc9\x72\x85\x70" - "\x13\x68\x52\x83\xfc\xb8\x11\x72" - "\xba\x3d\xc6\x4a\x28\xfa\xe2\x86" - "\x7b\x27\xab\x58\xe1\xa4\xca\xf6" - "\x9e\xbc\xfe\x0c\x92\x79\xb3\xec" - "\x1c\x5f\x79\x3b\x0d\x1e\xaa\x1a" - "\x77\x0f\x70\x19\x4b\xc8\x80\xee" - "\x27\x7c\x6e\x4a\x91\x96\x5c\xf4" - }, - { - .path = "/crypto/afsplit/sha256/5000", - .hash = QCRYPTO_HASH_ALG_SHA256, - .stripes = 5000, - .blocklen = 16, - .key = (const uint8_t *) - "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", - }, - { - .path = "/crypto/afsplit/sha1/1000", - .hash = QCRYPTO_HASH_ALG_SHA1, - .stripes = 1000, - .blocklen = 32, - .key = (const uint8_t *) - "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" - "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7" - "\xa8\xa9\xaa\xab\xac\xad\xae\xaf", - }, - { - .path = "/crypto/afsplit/sha256/big", - .hash = QCRYPTO_HASH_ALG_SHA256, - .stripes = 1000, - .blocklen = 64, - .key = (const uint8_t *) - "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" - "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" - "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" - "\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", - }, -}; - - -static inline char hex(int i) -{ - if (i < 10) { - return '0' + i; - } - return 'a' + (i - 10); -} - -static char *hex_string(const uint8_t *bytes, - size_t len) -{ - char *hexstr = g_new0(char, len * 2 + 1); - size_t i; - - for (i = 0; i < len; i++) { - hexstr[i * 2] = hex((bytes[i] >> 4) & 0xf); - hexstr[i * 2 + 1] = hex(bytes[i] & 0xf); - } - hexstr[len * 2] = '\0'; - - return hexstr; -} - -static void test_afsplit(const void *opaque) -{ - const QCryptoAFSplitTestData *data = opaque; - size_t splitlen = data->blocklen * data->stripes; - uint8_t *splitkey = g_new0(uint8_t, splitlen); - uint8_t *key = g_new0(uint8_t, data->blocklen); - gchar *expect, *actual; - - /* First time we round-trip the key */ - qcrypto_afsplit_encode(data->hash, - data->blocklen, data->stripes, - data->key, splitkey, - &error_abort); - - qcrypto_afsplit_decode(data->hash, - data->blocklen, data->stripes, - splitkey, key, - &error_abort); - - expect = hex_string(data->key, data->blocklen); - actual = hex_string(key, data->blocklen); - - g_assert_cmpstr(actual, ==, expect); - - g_free(actual); - g_free(expect); - - /* Second time we merely try decoding a previous split */ - if (data->splitkey) { - memset(key, 0, data->blocklen); - - qcrypto_afsplit_decode(data->hash, - data->blocklen, data->stripes, - data->splitkey, key, - &error_abort); - - expect = hex_string(data->key, data->blocklen); - actual = hex_string(key, data->blocklen); - - g_assert_cmpstr(actual, ==, expect); - - g_free(actual); - g_free(expect); - } - - g_free(key); - g_free(splitkey); -} - -int main(int argc, char **argv) -{ - size_t i; - - g_test_init(&argc, &argv, NULL); - - g_assert(qcrypto_init(NULL) == 0); - - for (i = 0; i < G_N_ELEMENTS(test_data); i++) { - if (!qcrypto_hash_supports(test_data[i].hash)) { - continue; - } - g_test_add_data_func(test_data[i].path, &test_data[i], test_afsplit); - } - return g_test_run(); -} diff --git a/tests/test-crypto-block.c b/tests/test-crypto-block.c deleted file mode 100644 index 3b1f0d509f..0000000000 --- a/tests/test-crypto-block.c +++ /dev/null @@ -1,368 +0,0 @@ -/* - * QEMU Crypto block encryption - * - * Copyright (c) 2016 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - * - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "crypto/init.h" -#include "crypto/block.h" -#include "qemu/buffer.h" -#include "qemu/module.h" -#include "crypto/secret.h" -#ifndef _WIN32 -#include -#endif - -#if (defined(_WIN32) || defined RUSAGE_THREAD) && \ - (defined(CONFIG_NETTLE) || defined(CONFIG_GCRYPT)) -#define TEST_LUKS -#else -#undef TEST_LUKS -#endif - -static QCryptoBlockCreateOptions qcow_create_opts = { - .format = Q_CRYPTO_BLOCK_FORMAT_QCOW, - .u.qcow = { - .has_key_secret = true, - .key_secret = (char *)"sec0", - }, -}; - -static QCryptoBlockOpenOptions qcow_open_opts = { - .format = Q_CRYPTO_BLOCK_FORMAT_QCOW, - .u.qcow = { - .has_key_secret = true, - .key_secret = (char *)"sec0", - }, -}; - - -#ifdef TEST_LUKS -static QCryptoBlockOpenOptions luks_open_opts = { - .format = Q_CRYPTO_BLOCK_FORMAT_LUKS, - .u.luks = { - .has_key_secret = true, - .key_secret = (char *)"sec0", - }, -}; - - -/* Creation with all default values */ -static QCryptoBlockCreateOptions luks_create_opts_default = { - .format = Q_CRYPTO_BLOCK_FORMAT_LUKS, - .u.luks = { - .has_key_secret = true, - .key_secret = (char *)"sec0", - }, -}; - - -/* ...and with explicit values */ -static QCryptoBlockCreateOptions luks_create_opts_aes256_cbc_plain64 = { - .format = Q_CRYPTO_BLOCK_FORMAT_LUKS, - .u.luks = { - .has_key_secret = true, - .key_secret = (char *)"sec0", - .has_cipher_alg = true, - .cipher_alg = QCRYPTO_CIPHER_ALG_AES_256, - .has_cipher_mode = true, - .cipher_mode = QCRYPTO_CIPHER_MODE_CBC, - .has_ivgen_alg = true, - .ivgen_alg = QCRYPTO_IVGEN_ALG_PLAIN64, - }, -}; - - -static QCryptoBlockCreateOptions luks_create_opts_aes256_cbc_essiv = { - .format = Q_CRYPTO_BLOCK_FORMAT_LUKS, - .u.luks = { - .has_key_secret = true, - .key_secret = (char *)"sec0", - .has_cipher_alg = true, - .cipher_alg = QCRYPTO_CIPHER_ALG_AES_256, - .has_cipher_mode = true, - .cipher_mode = QCRYPTO_CIPHER_MODE_CBC, - .has_ivgen_alg = true, - .ivgen_alg = QCRYPTO_IVGEN_ALG_ESSIV, - .has_ivgen_hash_alg = true, - .ivgen_hash_alg = QCRYPTO_HASH_ALG_SHA256, - .has_hash_alg = true, - .hash_alg = QCRYPTO_HASH_ALG_SHA1, - }, -}; -#endif /* TEST_LUKS */ - - -static struct QCryptoBlockTestData { - const char *path; - QCryptoBlockCreateOptions *create_opts; - QCryptoBlockOpenOptions *open_opts; - - bool expect_header; - - QCryptoCipherAlgorithm cipher_alg; - QCryptoCipherMode cipher_mode; - QCryptoHashAlgorithm hash_alg; - - QCryptoIVGenAlgorithm ivgen_alg; - QCryptoHashAlgorithm ivgen_hash; - - bool slow; -} test_data[] = { - { - .path = "/crypto/block/qcow", - .create_opts = &qcow_create_opts, - .open_opts = &qcow_open_opts, - - .expect_header = false, - - .cipher_alg = QCRYPTO_CIPHER_ALG_AES_128, - .cipher_mode = QCRYPTO_CIPHER_MODE_CBC, - - .ivgen_alg = QCRYPTO_IVGEN_ALG_PLAIN64, - }, -#ifdef TEST_LUKS - { - .path = "/crypto/block/luks/default", - .create_opts = &luks_create_opts_default, - .open_opts = &luks_open_opts, - - .expect_header = true, - - .cipher_alg = QCRYPTO_CIPHER_ALG_AES_256, - .cipher_mode = QCRYPTO_CIPHER_MODE_XTS, - .hash_alg = QCRYPTO_HASH_ALG_SHA256, - - .ivgen_alg = QCRYPTO_IVGEN_ALG_PLAIN64, - - .slow = true, - }, - { - .path = "/crypto/block/luks/aes-256-cbc-plain64", - .create_opts = &luks_create_opts_aes256_cbc_plain64, - .open_opts = &luks_open_opts, - - .expect_header = true, - - .cipher_alg = QCRYPTO_CIPHER_ALG_AES_256, - .cipher_mode = QCRYPTO_CIPHER_MODE_CBC, - .hash_alg = QCRYPTO_HASH_ALG_SHA256, - - .ivgen_alg = QCRYPTO_IVGEN_ALG_PLAIN64, - - .slow = true, - }, - { - .path = "/crypto/block/luks/aes-256-cbc-essiv", - .create_opts = &luks_create_opts_aes256_cbc_essiv, - .open_opts = &luks_open_opts, - - .expect_header = true, - - .cipher_alg = QCRYPTO_CIPHER_ALG_AES_256, - .cipher_mode = QCRYPTO_CIPHER_MODE_CBC, - .hash_alg = QCRYPTO_HASH_ALG_SHA1, - - .ivgen_alg = QCRYPTO_IVGEN_ALG_ESSIV, - .ivgen_hash = QCRYPTO_HASH_ALG_SHA256, - - .slow = true, - }, -#endif -}; - - -static ssize_t test_block_read_func(QCryptoBlock *block, - size_t offset, - uint8_t *buf, - size_t buflen, - void *opaque, - Error **errp) -{ - Buffer *header = opaque; - - g_assert_cmpint(offset + buflen, <=, header->capacity); - - memcpy(buf, header->buffer + offset, buflen); - - return buflen; -} - - -static ssize_t test_block_init_func(QCryptoBlock *block, - size_t headerlen, - void *opaque, - Error **errp) -{ - Buffer *header = opaque; - - g_assert_cmpint(header->capacity, ==, 0); - - buffer_reserve(header, headerlen); - - return headerlen; -} - - -static ssize_t test_block_write_func(QCryptoBlock *block, - size_t offset, - const uint8_t *buf, - size_t buflen, - void *opaque, - Error **errp) -{ - Buffer *header = opaque; - - g_assert_cmpint(buflen + offset, <=, header->capacity); - - memcpy(header->buffer + offset, buf, buflen); - header->offset = offset + buflen; - - return buflen; -} - - -static Object *test_block_secret(void) -{ - return object_new_with_props( - TYPE_QCRYPTO_SECRET, - object_get_objects_root(), - "sec0", - &error_abort, - "data", "123456", - NULL); -} - -static void test_block_assert_setup(const struct QCryptoBlockTestData *data, - QCryptoBlock *blk) -{ - QCryptoIVGen *ivgen; - QCryptoCipher *cipher; - - ivgen = qcrypto_block_get_ivgen(blk); - cipher = qcrypto_block_get_cipher(blk); - - g_assert(ivgen); - g_assert(cipher); - - g_assert_cmpint(data->cipher_alg, ==, cipher->alg); - g_assert_cmpint(data->cipher_mode, ==, cipher->mode); - g_assert_cmpint(data->hash_alg, ==, - qcrypto_block_get_kdf_hash(blk)); - - g_assert_cmpint(data->ivgen_alg, ==, - qcrypto_ivgen_get_algorithm(ivgen)); - g_assert_cmpint(data->ivgen_hash, ==, - qcrypto_ivgen_get_hash(ivgen)); -} - - -static void test_block(gconstpointer opaque) -{ - const struct QCryptoBlockTestData *data = opaque; - QCryptoBlock *blk; - Buffer header; - Object *sec = test_block_secret(); - - memset(&header, 0, sizeof(header)); - buffer_init(&header, "header"); - - blk = qcrypto_block_create(data->create_opts, NULL, - test_block_init_func, - test_block_write_func, - &header, - &error_abort); - g_assert(blk); - - if (data->expect_header) { - g_assert_cmpint(header.capacity, >, 0); - } else { - g_assert_cmpint(header.capacity, ==, 0); - } - - test_block_assert_setup(data, blk); - - qcrypto_block_free(blk); - object_unparent(sec); - - /* Ensure we can't open without the secret */ - blk = qcrypto_block_open(data->open_opts, NULL, - test_block_read_func, - &header, - 0, - 1, - NULL); - g_assert(blk == NULL); - - /* Ensure we can't open without the secret, unless NO_IO */ - blk = qcrypto_block_open(data->open_opts, NULL, - test_block_read_func, - &header, - QCRYPTO_BLOCK_OPEN_NO_IO, - 1, - &error_abort); - - g_assert(qcrypto_block_get_cipher(blk) == NULL); - g_assert(qcrypto_block_get_ivgen(blk) == NULL); - - qcrypto_block_free(blk); - - - /* Now open for real with secret */ - sec = test_block_secret(); - blk = qcrypto_block_open(data->open_opts, NULL, - test_block_read_func, - &header, - 0, - 1, - &error_abort); - g_assert(blk); - - test_block_assert_setup(data, blk); - - qcrypto_block_free(blk); - - object_unparent(sec); - - buffer_free(&header); -} - - -int main(int argc, char **argv) -{ - gsize i; - - module_call_init(MODULE_INIT_QOM); - g_test_init(&argc, &argv, NULL); - - g_assert(qcrypto_init(NULL) == 0); - - for (i = 0; i < G_N_ELEMENTS(test_data); i++) { - if (test_data[i].open_opts->format == Q_CRYPTO_BLOCK_FORMAT_LUKS && - !qcrypto_hash_supports(test_data[i].hash_alg)) { - continue; - } - if (!test_data[i].slow || - g_test_slow()) { - g_test_add_data_func(test_data[i].path, &test_data[i], test_block); - } - } - - return g_test_run(); -} diff --git a/tests/test-crypto-cipher.c b/tests/test-crypto-cipher.c deleted file mode 100644 index 280319a223..0000000000 --- a/tests/test-crypto-cipher.c +++ /dev/null @@ -1,801 +0,0 @@ -/* - * QEMU Crypto cipher algorithms - * - * Copyright (c) 2015 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - * - */ - -#include "qemu/osdep.h" - -#include "crypto/init.h" -#include "crypto/cipher.h" -#include "qapi/error.h" - -typedef struct QCryptoCipherTestData QCryptoCipherTestData; -struct QCryptoCipherTestData { - const char *path; - QCryptoCipherAlgorithm alg; - QCryptoCipherMode mode; - const char *key; - const char *plaintext; - const char *ciphertext; - const char *iv; -}; - -/* AES test data comes from appendix F of: - * - * http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf - */ -static QCryptoCipherTestData test_data[] = { - { - /* NIST F.1.1 ECB-AES128.Encrypt */ - .path = "/crypto/cipher/aes-ecb-128", - .alg = QCRYPTO_CIPHER_ALG_AES_128, - .mode = QCRYPTO_CIPHER_MODE_ECB, - .key = "2b7e151628aed2a6abf7158809cf4f3c", - .plaintext = - "6bc1bee22e409f96e93d7e117393172a" - "ae2d8a571e03ac9c9eb76fac45af8e51" - "30c81c46a35ce411e5fbc1191a0a52ef" - "f69f2445df4f9b17ad2b417be66c3710", - .ciphertext = - "3ad77bb40d7a3660a89ecaf32466ef97" - "f5d3d58503b9699de785895a96fdbaaf" - "43b1cd7f598ece23881b00e3ed030688" - "7b0c785e27e8ad3f8223207104725dd4" - }, - { - /* NIST F.1.3 ECB-AES192.Encrypt */ - .path = "/crypto/cipher/aes-ecb-192", - .alg = QCRYPTO_CIPHER_ALG_AES_192, - .mode = QCRYPTO_CIPHER_MODE_ECB, - .key = "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b", - .plaintext = - "6bc1bee22e409f96e93d7e117393172a" - "ae2d8a571e03ac9c9eb76fac45af8e51" - "30c81c46a35ce411e5fbc1191a0a52ef" - "f69f2445df4f9b17ad2b417be66c3710", - .ciphertext = - "bd334f1d6e45f25ff712a214571fa5cc" - "974104846d0ad3ad7734ecb3ecee4eef" - "ef7afd2270e2e60adce0ba2face6444e" - "9a4b41ba738d6c72fb16691603c18e0e" - }, - { - /* NIST F.1.5 ECB-AES256.Encrypt */ - .path = "/crypto/cipher/aes-ecb-256", - .alg = QCRYPTO_CIPHER_ALG_AES_256, - .mode = QCRYPTO_CIPHER_MODE_ECB, - .key = - "603deb1015ca71be2b73aef0857d7781" - "1f352c073b6108d72d9810a30914dff4", - .plaintext = - "6bc1bee22e409f96e93d7e117393172a" - "ae2d8a571e03ac9c9eb76fac45af8e51" - "30c81c46a35ce411e5fbc1191a0a52ef" - "f69f2445df4f9b17ad2b417be66c3710", - .ciphertext = - "f3eed1bdb5d2a03c064b5a7e3db181f8" - "591ccb10d410ed26dc5ba74a31362870" - "b6ed21b99ca6f4f9f153e7b1beafed1d" - "23304b7a39f9f3ff067d8d8f9e24ecc7", - }, - { - /* NIST F.2.1 CBC-AES128.Encrypt */ - .path = "/crypto/cipher/aes-cbc-128", - .alg = QCRYPTO_CIPHER_ALG_AES_128, - .mode = QCRYPTO_CIPHER_MODE_CBC, - .key = "2b7e151628aed2a6abf7158809cf4f3c", - .iv = "000102030405060708090a0b0c0d0e0f", - .plaintext = - "6bc1bee22e409f96e93d7e117393172a" - "ae2d8a571e03ac9c9eb76fac45af8e51" - "30c81c46a35ce411e5fbc1191a0a52ef" - "f69f2445df4f9b17ad2b417be66c3710", - .ciphertext = - "7649abac8119b246cee98e9b12e9197d" - "5086cb9b507219ee95db113a917678b2" - "73bed6b8e3c1743b7116e69e22229516" - "3ff1caa1681fac09120eca307586e1a7", - }, - { - /* NIST F.2.3 CBC-AES128.Encrypt */ - .path = "/crypto/cipher/aes-cbc-192", - .alg = QCRYPTO_CIPHER_ALG_AES_192, - .mode = QCRYPTO_CIPHER_MODE_CBC, - .key = "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b", - .iv = "000102030405060708090a0b0c0d0e0f", - .plaintext = - "6bc1bee22e409f96e93d7e117393172a" - "ae2d8a571e03ac9c9eb76fac45af8e51" - "30c81c46a35ce411e5fbc1191a0a52ef" - "f69f2445df4f9b17ad2b417be66c3710", - .ciphertext = - "4f021db243bc633d7178183a9fa071e8" - "b4d9ada9ad7dedf4e5e738763f69145a" - "571b242012fb7ae07fa9baac3df102e0" - "08b0e27988598881d920a9e64f5615cd", - }, - { - /* NIST F.2.5 CBC-AES128.Encrypt */ - .path = "/crypto/cipher/aes-cbc-256", - .alg = QCRYPTO_CIPHER_ALG_AES_256, - .mode = QCRYPTO_CIPHER_MODE_CBC, - .key = - "603deb1015ca71be2b73aef0857d7781" - "1f352c073b6108d72d9810a30914dff4", - .iv = "000102030405060708090a0b0c0d0e0f", - .plaintext = - "6bc1bee22e409f96e93d7e117393172a" - "ae2d8a571e03ac9c9eb76fac45af8e51" - "30c81c46a35ce411e5fbc1191a0a52ef" - "f69f2445df4f9b17ad2b417be66c3710", - .ciphertext = - "f58c4c04d6e5f1ba779eabfb5f7bfbd6" - "9cfc4e967edb808d679f777bc6702c7d" - "39f23369a9d9bacfa530e26304231461" - "b2eb05e2c39be9fcda6c19078c6a9d1b", - }, - { - .path = "/crypto/cipher/des-rfb-ecb-56", - .alg = QCRYPTO_CIPHER_ALG_DES_RFB, - .mode = QCRYPTO_CIPHER_MODE_ECB, - .key = "0123456789abcdef", - .plaintext = - "6bc1bee22e409f96e93d7e117393172a" - "ae2d8a571e03ac9c9eb76fac45af8e51" - "30c81c46a35ce411e5fbc1191a0a52ef" - "f69f2445df4f9b17ad2b417be66c3710", - .ciphertext = - "8f346aaf64eaf24040720d80648c52e7" - "aefc616be53ab1a3d301e69d91e01838" - "ffd29f1bb5596ad94ea2d8e6196b7f09" - "30d8ed0bf2773af36dd82a6280c20926", - }, -#if defined(CONFIG_NETTLE) || defined(CONFIG_GCRYPT) - { - /* Borrowed from linux-kernel crypto/testmgr.h */ - .path = "/crypto/cipher/3des-cbc", - .alg = QCRYPTO_CIPHER_ALG_3DES, - .mode = QCRYPTO_CIPHER_MODE_CBC, - .key = - "e9c0ff2e760b6424444d995a12d640c0" - "eac284e81495dbe8", - .iv = - "7d3388930f93b242", - .plaintext = - "6f54206f614d796e5320636565727374" - "54206f6f4d206e612079655372637465" - "20736f54206f614d796e532063656572" - "737454206f6f4d206e61207965537263" - "746520736f54206f614d796e53206365" - "6572737454206f6f4d206e6120796553" - "7263746520736f54206f614d796e5320" - "63656572737454206f6f4d206e610a79", - .ciphertext = - "0e2db6973c5633f4671721c76e8ad549" - "74b34905c51cd0ed12565c5396b6007d" - "9048fcf58d2939cc8ad5351836234ed7" - "76d1da0c9467bb048bf2036ca8cfb6ea" - "226447aa8f7513bf9fc2c3f0c956c57a" - "71632e897b1e12cae25fafd8a4f8c97a" - "d6f92131624445a6d6bc5ad32d5443cc" - "9ddea570e942458a6bfab19113b0d919", - }, - { - /* Borrowed from linux-kernel crypto/testmgr.h */ - .path = "/crypto/cipher/3des-ecb", - .alg = QCRYPTO_CIPHER_ALG_3DES, - .mode = QCRYPTO_CIPHER_MODE_ECB, - .key = - "0123456789abcdef5555555555555555" - "fedcba9876543210", - .plaintext = - "736f6d6564617461", - .ciphertext = - "18d748e563620572", - }, - { - /* Borrowed from linux-kernel crypto/testmgr.h */ - .path = "/crypto/cipher/3des-ctr", - .alg = QCRYPTO_CIPHER_ALG_3DES, - .mode = QCRYPTO_CIPHER_MODE_CTR, - .key = - "9cd6f39cb95a67005a67002dceeb2dce" - "ebb45172b451721f", - .iv = - "ffffffffffffffff", - .plaintext = - "05ec77fb42d559208b128669f05bcf56" - "39ad349f66ea7dc448d3ba0db118e34a" - "fe41285c278e11856cf75ec2553ca00b" - "9265e970db4fd6b900b41fe649fd442f" - "533a8d149863ca5dc1a833a70e9178ec" - "77de42d5bc078b12e54cf05b22563980" - "6b9f66c950c4af36ba0d947fe34add41" - "28b31a8e11f843f75e21553c876e9265" - "cc57dba235b900eb72e649d0442fb619" - "8d14ff46ca5d24a8339a6d9178c377de" - "a108bc07ee71e54cd75b22b51c806bf2" - "45c9503baf369960947fc64adda40fb3" - "1aed74f8432a5e218813876ef158cc57" - "3ea2359c67eb72c549d0bb02b619e04b" - "ff46295d248f169a6df45fc3aa3da108" - "937aee71d84cd7be01b51ce74ef2452c" - "503b82159960cb52c6a930a40f9679ed" - "74df432abd048813fa4df15823573e81" - "689c67ce51c5ac37bb02957ce04bd246" - "29b01b8f16f940f45f26aa3d846f937a" - "cd54d8a30abe01e873e74ed1452cb71e" - "8215fc47cb5225a9309b629679c074df" - "a609bd04ef76fa4dd458238a1d8168f3" - "5ace5138ac379e61957cc74bd2a50cb0" - "1be275f9402b5f268910846ff659cd54" - "3fa30a9d64e873da4ed1b803b71ee148" - "fc472e52258c179b62f55cc0ab32a609" - "907bef76d94dd4bf068a1de44ff35a2d" - "5138836a9e61c853c7ae31a50c977ee2" - "75dc402bb2058910fb42f65920543f86" - "699d64cf56daad34b803ea7de148d347", - .ciphertext = - "07c20820721f49ef19cd6f3253052215" - "a2852bdb85d2d8b9dd0d1b45cb6911d4" - "eabeb2455d0caebea0c127ac659f537e" - "afc21bb5b86d360c25c0f86d0b2901da" - "1378dc89121243faf612ef8d87627883" - "e2be41204c6d351bd10c30cfe2de2b03" - "bf4573d4e55995d1b39b276297bdde7f" - "a4d23980aa5023f074883da86a18793b" - "c4966c8d2240926ed6ad2a1fde63c0e7" - "07f72df7b5f3f0cc017c2a9bc210caaa" - "fd2b3fc5f3f6fc9b45db53e45bf3c97b" - "8e52ffc802b8ac9da10039da3d2d0e01" - "097d8d5ebe53b9b08ee7e2966ab278ea" - "de238ba5fa5ce3dabf8e316a55d16ab2" - "b5466fa5f0eeba1f9f98b0664fd03fa9" - "df5f58c4f4ff755c403a097e6e1c97d4" - "cce7e771cf0b150871fa0797cde6ca1d" - "14280ccf99137af1ebfafa9207de1da1" - "d33669fe514d9f2e83374f1f4830ed04" - "4da4ef3aca76f41c418f6337782f86a6" - "ef417ed2af88ab675271c38ef8269372" - "aad60ee70b46b13ab408a9a8a0cf200c" - "52bc8b0556b2bc319b74b92929969a50" - "dc45dc1aeb0c64d4d3057e5955c3f490" - "c2abf89b8adacea1c3f4ad77dd44c8ac" - "a3f1c9d2195cb0caa234c1f76cfdac65" - "32dc48c4f2006b77f17d76acc031632a" - "a53a62c891b10365cb43d106dfc367bc" - "dce0cd35ce4965a0527ba70d07a91bb0" - "407772c2ea0e3a7846b991b6e73d5142" - "fd51b0c62c6313785ceefccfc4700034", - }, -#endif - { - /* RFC 2144, Appendix B.1 */ - .path = "/crypto/cipher/cast5-128", - .alg = QCRYPTO_CIPHER_ALG_CAST5_128, - .mode = QCRYPTO_CIPHER_MODE_ECB, - .key = "0123456712345678234567893456789A", - .plaintext = "0123456789abcdef", - .ciphertext = "238b4fe5847e44b2", - }, - { - /* libgcrypt serpent.c */ - .path = "/crypto/cipher/serpent-128", - .alg = QCRYPTO_CIPHER_ALG_SERPENT_128, - .mode = QCRYPTO_CIPHER_MODE_ECB, - .key = "00000000000000000000000000000000", - .plaintext = "d29d576fcea3a3a7ed9099f29273d78e", - .ciphertext = "b2288b968ae8b08648d1ce9606fd992d", - }, - { - /* libgcrypt serpent.c */ - .path = "/crypto/cipher/serpent-192", - .alg = QCRYPTO_CIPHER_ALG_SERPENT_192, - .mode = QCRYPTO_CIPHER_MODE_ECB, - .key = "00000000000000000000000000000000" - "0000000000000000", - .plaintext = "d29d576fceaba3a7ed9899f2927bd78e", - .ciphertext = "130e353e1037c22405e8faefb2c3c3e9", - }, - { - /* libgcrypt serpent.c */ - .path = "/crypto/cipher/serpent-256a", - .alg = QCRYPTO_CIPHER_ALG_SERPENT_256, - .mode = QCRYPTO_CIPHER_MODE_ECB, - .key = "00000000000000000000000000000000" - "00000000000000000000000000000000", - .plaintext = "d095576fcea3e3a7ed98d9f29073d78e", - .ciphertext = "b90ee5862de69168f2bdd5125b45472b", - }, - { - /* libgcrypt serpent.c */ - .path = "/crypto/cipher/serpent-256b", - .alg = QCRYPTO_CIPHER_ALG_SERPENT_256, - .mode = QCRYPTO_CIPHER_MODE_ECB, - .key = "00000000000000000000000000000000" - "00000000000000000000000000000000", - .plaintext = "00000000010000000200000003000000", - .ciphertext = "2061a42782bd52ec691ec383b03ba77c", - }, - { - /* Twofish paper "Known Answer Test" */ - .path = "/crypto/cipher/twofish-128", - .alg = QCRYPTO_CIPHER_ALG_TWOFISH_128, - .mode = QCRYPTO_CIPHER_MODE_ECB, - .key = "d491db16e7b1c39e86cb086b789f5419", - .plaintext = "019f9809de1711858faac3a3ba20fbc3", - .ciphertext = "6363977de839486297e661c6c9d668eb", - }, - { - /* Twofish paper "Known Answer Test", I=3 */ - .path = "/crypto/cipher/twofish-192", - .alg = QCRYPTO_CIPHER_ALG_TWOFISH_192, - .mode = QCRYPTO_CIPHER_MODE_ECB, - .key = "88b2b2706b105e36b446bb6d731a1e88" - "efa71f788965bd44", - .plaintext = "39da69d6ba4997d585b6dc073ca341b2", - .ciphertext = "182b02d81497ea45f9daacdc29193a65", - }, - { - /* Twofish paper "Known Answer Test", I=4 */ - .path = "/crypto/cipher/twofish-256", - .alg = QCRYPTO_CIPHER_ALG_TWOFISH_256, - .mode = QCRYPTO_CIPHER_MODE_ECB, - .key = "d43bb7556ea32e46f2a282b7d45b4e0d" - "57ff739d4dc92c1bd7fc01700cc8216f", - .plaintext = "90afe91bb288544f2c32dc239b2635e6", - .ciphertext = "6cb4561c40bf0a9705931cb6d408e7fa", - }, - { - /* #1 32 byte key, 32 byte PTX */ - .path = "/crypto/cipher/aes-xts-128-1", - .alg = QCRYPTO_CIPHER_ALG_AES_128, - .mode = QCRYPTO_CIPHER_MODE_XTS, - .key = - "00000000000000000000000000000000" - "00000000000000000000000000000000", - .iv = - "00000000000000000000000000000000", - .plaintext = - "00000000000000000000000000000000" - "00000000000000000000000000000000", - .ciphertext = - "917cf69ebd68b2ec9b9fe9a3eadda692" - "cd43d2f59598ed858c02c2652fbf922e", - }, - { - /* #2, 32 byte key, 32 byte PTX */ - .path = "/crypto/cipher/aes-xts-128-2", - .alg = QCRYPTO_CIPHER_ALG_AES_128, - .mode = QCRYPTO_CIPHER_MODE_XTS, - .key = - "11111111111111111111111111111111" - "22222222222222222222222222222222", - .iv = - "33333333330000000000000000000000", - .plaintext = - "44444444444444444444444444444444" - "44444444444444444444444444444444", - .ciphertext = - "c454185e6a16936e39334038acef838b" - "fb186fff7480adc4289382ecd6d394f0", - }, - { - /* #5 from xts.7, 32 byte key, 32 byte PTX */ - .path = "/crypto/cipher/aes-xts-128-3", - .alg = QCRYPTO_CIPHER_ALG_AES_128, - .mode = QCRYPTO_CIPHER_MODE_XTS, - .key = - "fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0" - "bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0", - .iv = - "9a785634120000000000000000000000", - .plaintext = - "44444444444444444444444444444444" - "44444444444444444444444444444444", - .ciphertext = - "b01f86f8edc1863706fa8a4253e34f28" - "af319de38334870f4dd1f94cbe9832f1", - }, - { - /* #4, 32 byte key, 512 byte PTX */ - .path = "/crypto/cipher/aes-xts-128-4", - .alg = QCRYPTO_CIPHER_ALG_AES_128, - .mode = QCRYPTO_CIPHER_MODE_XTS, - .key = - "27182818284590452353602874713526" - "31415926535897932384626433832795", - .iv = - "00000000000000000000000000000000", - .plaintext = - "000102030405060708090a0b0c0d0e0f" - "101112131415161718191a1b1c1d1e1f" - "202122232425262728292a2b2c2d2e2f" - "303132333435363738393a3b3c3d3e3f" - "404142434445464748494a4b4c4d4e4f" - "505152535455565758595a5b5c5d5e5f" - "606162636465666768696a6b6c6d6e6f" - "707172737475767778797a7b7c7d7e7f" - "808182838485868788898a8b8c8d8e8f" - "909192939495969798999a9b9c9d9e9f" - "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf" - "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf" - "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf" - "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf" - "e0e1e2e3e4e5e6e7e8e9eaebecedeeef" - "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff" - "000102030405060708090a0b0c0d0e0f" - "101112131415161718191a1b1c1d1e1f" - "202122232425262728292a2b2c2d2e2f" - "303132333435363738393a3b3c3d3e3f" - "404142434445464748494a4b4c4d4e4f" - "505152535455565758595a5b5c5d5e5f" - "606162636465666768696a6b6c6d6e6f" - "707172737475767778797a7b7c7d7e7f" - "808182838485868788898a8b8c8d8e8f" - "909192939495969798999a9b9c9d9e9f" - "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf" - "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf" - "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf" - "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf" - "e0e1e2e3e4e5e6e7e8e9eaebecedeeef" - "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", - .ciphertext = - "27a7479befa1d476489f308cd4cfa6e2" - "a96e4bbe3208ff25287dd3819616e89c" - "c78cf7f5e543445f8333d8fa7f560000" - "05279fa5d8b5e4ad40e736ddb4d35412" - "328063fd2aab53e5ea1e0a9f332500a5" - "df9487d07a5c92cc512c8866c7e860ce" - "93fdf166a24912b422976146ae20ce84" - "6bb7dc9ba94a767aaef20c0d61ad0265" - "5ea92dc4c4e41a8952c651d33174be51" - "a10c421110e6d81588ede82103a252d8" - "a750e8768defffed9122810aaeb99f91" - "72af82b604dc4b8e51bcb08235a6f434" - "1332e4ca60482a4ba1a03b3e65008fc5" - "da76b70bf1690db4eae29c5f1badd03c" - "5ccf2a55d705ddcd86d449511ceb7ec3" - "0bf12b1fa35b913f9f747a8afd1b130e" - "94bff94effd01a91735ca1726acd0b19" - "7c4e5b03393697e126826fb6bbde8ecc" - "1e08298516e2c9ed03ff3c1b7860f6de" - "76d4cecd94c8119855ef5297ca67e9f3" - "e7ff72b1e99785ca0a7e7720c5b36dc6" - "d72cac9574c8cbbc2f801e23e56fd344" - "b07f22154beba0f08ce8891e643ed995" - "c94d9a69c9f1b5f499027a78572aeebd" - "74d20cc39881c213ee770b1010e4bea7" - "18846977ae119f7a023ab58cca0ad752" - "afe656bb3c17256a9f6e9bf19fdd5a38" - "fc82bbe872c5539edb609ef4f79c203e" - "bb140f2e583cb2ad15b4aa5b655016a8" - "449277dbd477ef2c8d6c017db738b18d" - "eb4a427d1923ce3ff262735779a418f2" - "0a282df920147beabe421ee5319d0568", - }, - { - /* Bad config - cast5-128 has 8 byte block size - * which is incompatible with XTS - */ - .path = "/crypto/cipher/cast5-xts-128", - .alg = QCRYPTO_CIPHER_ALG_CAST5_128, - .mode = QCRYPTO_CIPHER_MODE_XTS, - .key = - "27182818284590452353602874713526" - "31415926535897932384626433832795", - }, - { - /* NIST F.5.1 CTR-AES128.Encrypt */ - .path = "/crypto/cipher/aes-ctr-128", - .alg = QCRYPTO_CIPHER_ALG_AES_128, - .mode = QCRYPTO_CIPHER_MODE_CTR, - .key = "2b7e151628aed2a6abf7158809cf4f3c", - .iv = "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", - .plaintext = - "6bc1bee22e409f96e93d7e117393172a" - "ae2d8a571e03ac9c9eb76fac45af8e51" - "30c81c46a35ce411e5fbc1191a0a52ef" - "f69f2445df4f9b17ad2b417be66c3710", - .ciphertext = - "874d6191b620e3261bef6864990db6ce" - "9806f66b7970fdff8617187bb9fffdff" - "5ae4df3edbd5d35e5b4f09020db03eab" - "1e031dda2fbe03d1792170a0f3009cee", - }, - { - /* NIST F.5.3 CTR-AES192.Encrypt */ - .path = "/crypto/cipher/aes-ctr-192", - .alg = QCRYPTO_CIPHER_ALG_AES_192, - .mode = QCRYPTO_CIPHER_MODE_CTR, - .key = "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b", - .iv = "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", - .plaintext = - "6bc1bee22e409f96e93d7e117393172a" - "ae2d8a571e03ac9c9eb76fac45af8e51" - "30c81c46a35ce411e5fbc1191a0a52ef" - "f69f2445df4f9b17ad2b417be66c3710", - .ciphertext = - "1abc932417521ca24f2b0459fe7e6e0b" - "090339ec0aa6faefd5ccc2c6f4ce8e94" - "1e36b26bd1ebc670d1bd1d665620abf7" - "4f78a7f6d29809585a97daec58c6b050", - }, - { - /* NIST F.5.5 CTR-AES256.Encrypt */ - .path = "/crypto/cipher/aes-ctr-256", - .alg = QCRYPTO_CIPHER_ALG_AES_256, - .mode = QCRYPTO_CIPHER_MODE_CTR, - .key = "603deb1015ca71be2b73aef0857d7781" - "1f352c073b6108d72d9810a30914dff4", - .iv = "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", - .plaintext = - "6bc1bee22e409f96e93d7e117393172a" - "ae2d8a571e03ac9c9eb76fac45af8e51" - "30c81c46a35ce411e5fbc1191a0a52ef" - "f69f2445df4f9b17ad2b417be66c3710", - .ciphertext = - "601ec313775789a5b7a7f504bbf3d228" - "f443e3ca4d62b59aca84e990cacaf5c5" - "2b0930daa23de94ce87017ba2d84988d" - "dfc9c58db67aada613c2dd08457941a6", - } -}; - - -static inline int unhex(char c) -{ - if (c >= 'a' && c <= 'f') { - return 10 + (c - 'a'); - } - if (c >= 'A' && c <= 'F') { - return 10 + (c - 'A'); - } - return c - '0'; -} - -static inline char hex(int i) -{ - if (i < 10) { - return '0' + i; - } - return 'a' + (i - 10); -} - -static size_t unhex_string(const char *hexstr, - uint8_t **data) -{ - size_t len; - size_t i; - - if (!hexstr) { - *data = NULL; - return 0; - } - - len = strlen(hexstr); - *data = g_new0(uint8_t, len / 2); - - for (i = 0; i < len; i += 2) { - (*data)[i/2] = (unhex(hexstr[i]) << 4) | unhex(hexstr[i+1]); - } - return len / 2; -} - -static char *hex_string(const uint8_t *bytes, - size_t len) -{ - char *hexstr = g_new0(char, len * 2 + 1); - size_t i; - - for (i = 0; i < len; i++) { - hexstr[i*2] = hex((bytes[i] >> 4) & 0xf); - hexstr[i*2+1] = hex(bytes[i] & 0xf); - } - hexstr[len*2] = '\0'; - - return hexstr; -} - -static void test_cipher(const void *opaque) -{ - const QCryptoCipherTestData *data = opaque; - - QCryptoCipher *cipher; - uint8_t *key, *iv = NULL, *ciphertext = NULL, - *plaintext = NULL, *outtext = NULL; - size_t nkey, niv = 0, nciphertext = 0, nplaintext = 0; - char *outtexthex = NULL; - size_t ivsize, keysize, blocksize; - Error *err = NULL; - - nkey = unhex_string(data->key, &key); - if (data->iv) { - niv = unhex_string(data->iv, &iv); - } - if (data->ciphertext) { - nciphertext = unhex_string(data->ciphertext, &ciphertext); - } - if (data->plaintext) { - nplaintext = unhex_string(data->plaintext, &plaintext); - } - - g_assert(nciphertext == nplaintext); - - outtext = g_new0(uint8_t, nciphertext); - - cipher = qcrypto_cipher_new( - data->alg, data->mode, - key, nkey, - &err); - if (data->plaintext) { - g_assert(err == NULL); - g_assert(cipher != NULL); - } else { - error_free_or_abort(&err); - g_assert(cipher == NULL); - goto cleanup; - } - - keysize = qcrypto_cipher_get_key_len(data->alg); - blocksize = qcrypto_cipher_get_block_len(data->alg); - ivsize = qcrypto_cipher_get_iv_len(data->alg, data->mode); - - if (data->mode == QCRYPTO_CIPHER_MODE_XTS) { - g_assert_cmpint(keysize * 2, ==, nkey); - } else { - g_assert_cmpint(keysize, ==, nkey); - } - g_assert_cmpint(ivsize, ==, niv); - if (niv) { - g_assert_cmpint(blocksize, ==, niv); - } - - if (iv) { - g_assert(qcrypto_cipher_setiv(cipher, - iv, niv, - &error_abort) == 0); - } - g_assert(qcrypto_cipher_encrypt(cipher, - plaintext, - outtext, - nplaintext, - &error_abort) == 0); - - outtexthex = hex_string(outtext, nciphertext); - - g_assert_cmpstr(outtexthex, ==, data->ciphertext); - - g_free(outtexthex); - - if (iv) { - g_assert(qcrypto_cipher_setiv(cipher, - iv, niv, - &error_abort) == 0); - } - g_assert(qcrypto_cipher_decrypt(cipher, - ciphertext, - outtext, - nplaintext, - &error_abort) == 0); - - outtexthex = hex_string(outtext, nplaintext); - - g_assert_cmpstr(outtexthex, ==, data->plaintext); - - cleanup: - g_free(outtext); - g_free(outtexthex); - g_free(key); - g_free(iv); - g_free(ciphertext); - g_free(plaintext); - qcrypto_cipher_free(cipher); -} - - -static void test_cipher_null_iv(void) -{ - QCryptoCipher *cipher; - uint8_t key[32] = { 0 }; - uint8_t plaintext[32] = { 0 }; - uint8_t ciphertext[32] = { 0 }; - - cipher = qcrypto_cipher_new( - QCRYPTO_CIPHER_ALG_AES_256, - QCRYPTO_CIPHER_MODE_CBC, - key, sizeof(key), - &error_abort); - g_assert(cipher != NULL); - - /* Don't call qcrypto_cipher_setiv */ - - qcrypto_cipher_encrypt(cipher, - plaintext, - ciphertext, - sizeof(plaintext), - &error_abort); - - qcrypto_cipher_free(cipher); -} - -static void test_cipher_short_plaintext(void) -{ - Error *err = NULL; - QCryptoCipher *cipher; - uint8_t key[32] = { 0 }; - uint8_t plaintext1[20] = { 0 }; - uint8_t ciphertext1[20] = { 0 }; - uint8_t plaintext2[40] = { 0 }; - uint8_t ciphertext2[40] = { 0 }; - int ret; - - cipher = qcrypto_cipher_new( - QCRYPTO_CIPHER_ALG_AES_256, - QCRYPTO_CIPHER_MODE_CBC, - key, sizeof(key), - &error_abort); - g_assert(cipher != NULL); - - /* Should report an error as plaintext is shorter - * than block size - */ - ret = qcrypto_cipher_encrypt(cipher, - plaintext1, - ciphertext1, - sizeof(plaintext1), - &err); - g_assert(ret == -1); - error_free_or_abort(&err); - - /* Should report an error as plaintext is larger than - * block size, but not a multiple of block size - */ - ret = qcrypto_cipher_encrypt(cipher, - plaintext2, - ciphertext2, - sizeof(plaintext2), - &err); - g_assert(ret == -1); - error_free_or_abort(&err); - - qcrypto_cipher_free(cipher); -} - -int main(int argc, char **argv) -{ - size_t i; - - g_test_init(&argc, &argv, NULL); - - g_assert(qcrypto_init(NULL) == 0); - - for (i = 0; i < G_N_ELEMENTS(test_data); i++) { - if (qcrypto_cipher_supports(test_data[i].alg, test_data[i].mode)) { - g_test_add_data_func(test_data[i].path, &test_data[i], test_cipher); - } - } - - g_test_add_func("/crypto/cipher/null-iv", - test_cipher_null_iv); - - g_test_add_func("/crypto/cipher/short-plaintext", - test_cipher_short_plaintext); - - return g_test_run(); -} diff --git a/tests/test-crypto-hash.c b/tests/test-crypto-hash.c deleted file mode 100644 index ce7d0ab9b5..0000000000 --- a/tests/test-crypto-hash.c +++ /dev/null @@ -1,255 +0,0 @@ -/* - * QEMU Crypto hash algorithms - * - * Copyright (c) 2015 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - * - */ - -#include "qemu/osdep.h" - -#include "crypto/init.h" -#include "crypto/hash.h" - -#define INPUT_TEXT "Hiss hisss Hissss hiss Hiss hisss Hiss hiss" -#define INPUT_TEXT1 "Hiss hisss " -#define INPUT_TEXT2 "Hissss hiss " -#define INPUT_TEXT3 "Hiss hisss Hiss hiss" - -#define OUTPUT_MD5 "628d206371563035ab8ef62f492bdec9" -#define OUTPUT_SHA1 "b2e74f26758a3a421e509cee045244b78753cc02" -#define OUTPUT_SHA224 "e2f7415aad33ef79f6516b0986d7175f" \ - "9ca3389a85bf6cfed078737b" -#define OUTPUT_SHA256 "bc757abb0436586f392b437e5dd24096" \ - "f7f224de6b74d4d86e2abc6121b160d0" -#define OUTPUT_SHA384 "887ce52efb4f46700376356583b7e279" \ - "4f612bd024e4495087ddb946c448c69d" \ - "56dbf7152a94a5e63a80f3ba9f0eed78" -#define OUTPUT_SHA512 "3a90d79638235ec6c4c11bebd84d83c0" \ - "549bc1e84edc4b6ec7086487641256cb" \ - "63b54e4cb2d2032b393994aa263c0dbb" \ - "e00a9f2fe9ef6037352232a1eec55ee7" -#define OUTPUT_RIPEMD160 "f3d658fad3fdfb2b52c9369cf0d441249ddfa8a0" - -#define OUTPUT_MD5_B64 "Yo0gY3FWMDWrjvYvSSveyQ==" -#define OUTPUT_SHA1_B64 "sudPJnWKOkIeUJzuBFJEt4dTzAI=" -#define OUTPUT_SHA224_B64 "4vdBWq0z73n2UWsJhtcXX5yjOJqFv2z+0Hhzew==" -#define OUTPUT_SHA256_B64 "vHV6uwQ2WG85K0N+XdJAlvfyJN5rdNTYbiq8YSGxYNA=" -#define OUTPUT_SHA384_B64 "iHzlLvtPRnADdjVlg7fieU9hK9Ak5ElQh925RsRI" \ - "xp1W2/cVKpSl5jqA87qfDu14" -#define OUTPUT_SHA512_B64 "OpDXljgjXsbEwRvr2E2DwFSbwehO3Etuxwhkh2QS" \ - "VstjtU5MstIDKzk5lKomPA274AqfL+nvYDc1IjKh" \ - "7sVe5w==" -#define OUTPUT_RIPEMD160_B64 "89ZY+tP9+ytSyTac8NRBJJ3fqKA=" - -static const char *expected_outputs[] = { - [QCRYPTO_HASH_ALG_MD5] = OUTPUT_MD5, - [QCRYPTO_HASH_ALG_SHA1] = OUTPUT_SHA1, - [QCRYPTO_HASH_ALG_SHA224] = OUTPUT_SHA224, - [QCRYPTO_HASH_ALG_SHA256] = OUTPUT_SHA256, - [QCRYPTO_HASH_ALG_SHA384] = OUTPUT_SHA384, - [QCRYPTO_HASH_ALG_SHA512] = OUTPUT_SHA512, - [QCRYPTO_HASH_ALG_RIPEMD160] = OUTPUT_RIPEMD160, -}; -static const char *expected_outputs_b64[] = { - [QCRYPTO_HASH_ALG_MD5] = OUTPUT_MD5_B64, - [QCRYPTO_HASH_ALG_SHA1] = OUTPUT_SHA1_B64, - [QCRYPTO_HASH_ALG_SHA224] = OUTPUT_SHA224_B64, - [QCRYPTO_HASH_ALG_SHA256] = OUTPUT_SHA256_B64, - [QCRYPTO_HASH_ALG_SHA384] = OUTPUT_SHA384_B64, - [QCRYPTO_HASH_ALG_SHA512] = OUTPUT_SHA512_B64, - [QCRYPTO_HASH_ALG_RIPEMD160] = OUTPUT_RIPEMD160_B64, -}; -static const int expected_lens[] = { - [QCRYPTO_HASH_ALG_MD5] = 16, - [QCRYPTO_HASH_ALG_SHA1] = 20, - [QCRYPTO_HASH_ALG_SHA224] = 28, - [QCRYPTO_HASH_ALG_SHA256] = 32, - [QCRYPTO_HASH_ALG_SHA384] = 48, - [QCRYPTO_HASH_ALG_SHA512] = 64, - [QCRYPTO_HASH_ALG_RIPEMD160] = 20, -}; - -static const char hex[] = "0123456789abcdef"; - -/* Test with dynamic allocation */ -static void test_hash_alloc(void) -{ - size_t i; - - for (i = 0; i < G_N_ELEMENTS(expected_outputs) ; i++) { - uint8_t *result = NULL; - size_t resultlen = 0; - int ret; - size_t j; - - if (!qcrypto_hash_supports(i)) { - continue; - } - - ret = qcrypto_hash_bytes(i, - INPUT_TEXT, - strlen(INPUT_TEXT), - &result, - &resultlen, - NULL); - g_assert(ret == 0); - g_assert(resultlen == expected_lens[i]); - - for (j = 0; j < resultlen; j++) { - g_assert(expected_outputs[i][j * 2] == hex[(result[j] >> 4) & 0xf]); - g_assert(expected_outputs[i][j * 2 + 1] == hex[result[j] & 0xf]); - } - g_free(result); - } -} - -/* Test with caller preallocating */ -static void test_hash_prealloc(void) -{ - size_t i; - - for (i = 0; i < G_N_ELEMENTS(expected_outputs) ; i++) { - uint8_t *result; - size_t resultlen; - int ret; - size_t j; - - if (!qcrypto_hash_supports(i)) { - continue; - } - - resultlen = expected_lens[i]; - result = g_new0(uint8_t, resultlen); - - ret = qcrypto_hash_bytes(i, - INPUT_TEXT, - strlen(INPUT_TEXT), - &result, - &resultlen, - NULL); - g_assert(ret == 0); - - g_assert(resultlen == expected_lens[i]); - for (j = 0; j < resultlen; j++) { - g_assert(expected_outputs[i][j * 2] == hex[(result[j] >> 4) & 0xf]); - g_assert(expected_outputs[i][j * 2 + 1] == hex[result[j] & 0xf]); - } - g_free(result); - } -} - - -/* Test with dynamic allocation */ -static void test_hash_iov(void) -{ - size_t i; - - for (i = 0; i < G_N_ELEMENTS(expected_outputs) ; i++) { - struct iovec iov[3] = { - { .iov_base = (char *)INPUT_TEXT1, .iov_len = strlen(INPUT_TEXT1) }, - { .iov_base = (char *)INPUT_TEXT2, .iov_len = strlen(INPUT_TEXT2) }, - { .iov_base = (char *)INPUT_TEXT3, .iov_len = strlen(INPUT_TEXT3) }, - }; - uint8_t *result = NULL; - size_t resultlen = 0; - int ret; - size_t j; - - if (!qcrypto_hash_supports(i)) { - continue; - } - - ret = qcrypto_hash_bytesv(i, - iov, 3, - &result, - &resultlen, - NULL); - g_assert(ret == 0); - g_assert(resultlen == expected_lens[i]); - for (j = 0; j < resultlen; j++) { - g_assert(expected_outputs[i][j * 2] == hex[(result[j] >> 4) & 0xf]); - g_assert(expected_outputs[i][j * 2 + 1] == hex[result[j] & 0xf]); - } - g_free(result); - } -} - - -/* Test with printable hashing */ -static void test_hash_digest(void) -{ - size_t i; - - for (i = 0; i < G_N_ELEMENTS(expected_outputs) ; i++) { - int ret; - char *digest; - size_t digestsize; - - if (!qcrypto_hash_supports(i)) { - continue; - } - - digestsize = qcrypto_hash_digest_len(i); - - g_assert_cmpint(digestsize * 2, ==, strlen(expected_outputs[i])); - - ret = qcrypto_hash_digest(i, - INPUT_TEXT, - strlen(INPUT_TEXT), - &digest, - NULL); - g_assert(ret == 0); - g_assert_cmpstr(digest, ==, expected_outputs[i]); - g_free(digest); - } -} - -/* Test with base64 encoding */ -static void test_hash_base64(void) -{ - size_t i; - - for (i = 0; i < G_N_ELEMENTS(expected_outputs) ; i++) { - int ret; - char *digest; - - if (!qcrypto_hash_supports(i)) { - continue; - } - - ret = qcrypto_hash_base64(i, - INPUT_TEXT, - strlen(INPUT_TEXT), - &digest, - NULL); - g_assert(ret == 0); - g_assert_cmpstr(digest, ==, expected_outputs_b64[i]); - g_free(digest); - } -} - -int main(int argc, char **argv) -{ - g_assert(qcrypto_init(NULL) == 0); - - g_test_init(&argc, &argv, NULL); - g_test_add_func("/crypto/hash/iov", test_hash_iov); - g_test_add_func("/crypto/hash/alloc", test_hash_alloc); - g_test_add_func("/crypto/hash/prealloc", test_hash_prealloc); - g_test_add_func("/crypto/hash/digest", test_hash_digest); - g_test_add_func("/crypto/hash/base64", test_hash_base64); - return g_test_run(); -} diff --git a/tests/test-crypto-hmac.c b/tests/test-crypto-hmac.c deleted file mode 100644 index ee55382a3c..0000000000 --- a/tests/test-crypto-hmac.c +++ /dev/null @@ -1,266 +0,0 @@ -/* - * QEMU Crypto hmac algorithms tests - * - * Copyright (c) 2016 HUAWEI TECHNOLOGIES CO., LTD. - * - * Authors: - * Longpeng(Mike) - * - * This work is licensed under the terms of the GNU GPL, version 2 or - * (at your option) any later version. See the COPYING file in the - * top-level directory. - * - */ - -#include "qemu/osdep.h" -#include "crypto/init.h" -#include "crypto/hmac.h" - -#define INPUT_TEXT1 "ABCDEFGHIJKLMNOPQRSTUVWXY" -#define INPUT_TEXT2 "Zabcdefghijklmnopqrstuvwx" -#define INPUT_TEXT3 "yz0123456789" -#define INPUT_TEXT INPUT_TEXT1 \ - INPUT_TEXT2 \ - INPUT_TEXT3 - -#define KEY "monkey monkey monkey monkey" - -typedef struct QCryptoHmacTestData QCryptoHmacTestData; -struct QCryptoHmacTestData { - QCryptoHashAlgorithm alg; - const char *hex_digest; -}; - -static QCryptoHmacTestData test_data[] = { - { - .alg = QCRYPTO_HASH_ALG_MD5, - .hex_digest = - "ede9cb83679ba82d88fbeae865b3f8fc", - }, - { - .alg = QCRYPTO_HASH_ALG_SHA1, - .hex_digest = - "c7b5a631e3aac975c4ededfcd346e469" - "dbc5f2d1", - }, - { - .alg = QCRYPTO_HASH_ALG_SHA224, - .hex_digest = - "5f768179dbb29ca722875d0f461a2e2f" - "597d0210340a84df1a8e9c63", - }, - { - .alg = QCRYPTO_HASH_ALG_SHA256, - .hex_digest = - "3798f363c57afa6edaffe39016ca7bad" - "efd1e670afb0e3987194307dec3197db", - }, - { - .alg = QCRYPTO_HASH_ALG_SHA384, - .hex_digest = - "d218680a6032d33dccd9882d6a6a7164" - "64f26623be257a9b2919b185294f4a49" - "9e54b190bfd6bc5cedd2cd05c7e65e82", - }, - { - .alg = QCRYPTO_HASH_ALG_SHA512, - .hex_digest = - "835a4f5b3750b4c1fccfa88da2f746a4" - "900160c9f18964309bb736c13b59491b" - "8e32d37b724cc5aebb0f554c6338a3b5" - "94c4ba26862b2dadb59b7ede1d08d53e", - }, - { - .alg = QCRYPTO_HASH_ALG_RIPEMD160, - .hex_digest = - "94964ed4c1155b62b668c241d67279e5" - "8a711676", - }, -}; - -static const char hex[] = "0123456789abcdef"; - -static void test_hmac_alloc(void) -{ - size_t i; - - for (i = 0; i < G_N_ELEMENTS(test_data); i++) { - QCryptoHmacTestData *data = &test_data[i]; - QCryptoHmac *hmac = NULL; - uint8_t *result = NULL; - size_t resultlen = 0; - Error *err = NULL; - const char *exp_output = NULL; - int ret; - size_t j; - - if (!qcrypto_hmac_supports(data->alg)) { - return; - } - - exp_output = data->hex_digest; - - hmac = qcrypto_hmac_new(data->alg, (const uint8_t *)KEY, - strlen(KEY), &err); - g_assert(err == NULL); - g_assert(hmac != NULL); - - ret = qcrypto_hmac_bytes(hmac, (const char *)INPUT_TEXT, - strlen(INPUT_TEXT), &result, - &resultlen, &err); - g_assert(err == NULL); - g_assert(ret == 0); - - for (j = 0; j < resultlen; j++) { - g_assert(exp_output[j * 2] == hex[(result[j] >> 4) & 0xf]); - g_assert(exp_output[j * 2 + 1] == hex[result[j] & 0xf]); - } - - qcrypto_hmac_free(hmac); - - g_free(result); - } -} - -static void test_hmac_prealloc(void) -{ - size_t i; - - for (i = 0; i < G_N_ELEMENTS(test_data); i++) { - QCryptoHmacTestData *data = &test_data[i]; - QCryptoHmac *hmac = NULL; - uint8_t *result = NULL; - size_t resultlen = 0; - Error *err = NULL; - const char *exp_output = NULL; - int ret; - size_t j; - - if (!qcrypto_hmac_supports(data->alg)) { - return; - } - - exp_output = data->hex_digest; - - resultlen = strlen(exp_output) / 2; - result = g_new0(uint8_t, resultlen); - - hmac = qcrypto_hmac_new(data->alg, (const uint8_t *)KEY, - strlen(KEY), &err); - g_assert(err == NULL); - g_assert(hmac != NULL); - - ret = qcrypto_hmac_bytes(hmac, (const char *)INPUT_TEXT, - strlen(INPUT_TEXT), &result, - &resultlen, &err); - g_assert(err == NULL); - g_assert(ret == 0); - - exp_output = data->hex_digest; - for (j = 0; j < resultlen; j++) { - g_assert(exp_output[j * 2] == hex[(result[j] >> 4) & 0xf]); - g_assert(exp_output[j * 2 + 1] == hex[result[j] & 0xf]); - } - - qcrypto_hmac_free(hmac); - - g_free(result); - } -} - -static void test_hmac_iov(void) -{ - size_t i; - - for (i = 0; i < G_N_ELEMENTS(test_data); i++) { - QCryptoHmacTestData *data = &test_data[i]; - QCryptoHmac *hmac = NULL; - uint8_t *result = NULL; - size_t resultlen = 0; - Error *err = NULL; - const char *exp_output = NULL; - int ret; - size_t j; - struct iovec iov[3] = { - { .iov_base = (char *)INPUT_TEXT1, .iov_len = strlen(INPUT_TEXT1) }, - { .iov_base = (char *)INPUT_TEXT2, .iov_len = strlen(INPUT_TEXT2) }, - { .iov_base = (char *)INPUT_TEXT3, .iov_len = strlen(INPUT_TEXT3) }, - }; - - if (!qcrypto_hmac_supports(data->alg)) { - return; - } - - exp_output = data->hex_digest; - - hmac = qcrypto_hmac_new(data->alg, (const uint8_t *)KEY, - strlen(KEY), &err); - g_assert(err == NULL); - g_assert(hmac != NULL); - - ret = qcrypto_hmac_bytesv(hmac, iov, 3, &result, - &resultlen, &err); - g_assert(err == NULL); - g_assert(ret == 0); - - for (j = 0; j < resultlen; j++) { - g_assert(exp_output[j * 2] == hex[(result[j] >> 4) & 0xf]); - g_assert(exp_output[j * 2 + 1] == hex[result[j] & 0xf]); - } - - qcrypto_hmac_free(hmac); - - g_free(result); - } -} - -static void test_hmac_digest(void) -{ - size_t i; - - for (i = 0; i < G_N_ELEMENTS(test_data); i++) { - QCryptoHmacTestData *data = &test_data[i]; - QCryptoHmac *hmac = NULL; - uint8_t *result = NULL; - Error *err = NULL; - const char *exp_output = NULL; - int ret; - - if (!qcrypto_hmac_supports(data->alg)) { - return; - } - - exp_output = data->hex_digest; - - hmac = qcrypto_hmac_new(data->alg, (const uint8_t *)KEY, - strlen(KEY), &err); - g_assert(err == NULL); - g_assert(hmac != NULL); - - ret = qcrypto_hmac_digest(hmac, (const char *)INPUT_TEXT, - strlen(INPUT_TEXT), (char **)&result, - &err); - g_assert(err == NULL); - g_assert(ret == 0); - - g_assert_cmpstr((const char *)result, ==, exp_output); - - qcrypto_hmac_free(hmac); - - g_free(result); - } -} - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - - g_assert(qcrypto_init(NULL) == 0); - - g_test_add_func("/crypto/hmac/iov", test_hmac_iov); - g_test_add_func("/crypto/hmac/alloc", test_hmac_alloc); - g_test_add_func("/crypto/hmac/prealloc", test_hmac_prealloc); - g_test_add_func("/crypto/hmac/digest", test_hmac_digest); - - return g_test_run(); -} diff --git a/tests/test-crypto-ivgen.c b/tests/test-crypto-ivgen.c deleted file mode 100644 index f581e6aba7..0000000000 --- a/tests/test-crypto-ivgen.c +++ /dev/null @@ -1,174 +0,0 @@ -/* - * QEMU Crypto IV generator algorithms - * - * Copyright (c) 2015-2016 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - * - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "crypto/ivgen.h" - - -struct QCryptoIVGenTestData { - const char *path; - uint64_t sector; - QCryptoIVGenAlgorithm ivalg; - QCryptoHashAlgorithm hashalg; - QCryptoCipherAlgorithm cipheralg; - const uint8_t *key; - size_t nkey; - const uint8_t *iv; - size_t niv; -} test_data[] = { - /* Small */ - { - "/crypto/ivgen/plain/1", - .sector = 0x1, - .ivalg = QCRYPTO_IVGEN_ALG_PLAIN, - .iv = (const uint8_t *)"\x01\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00", - .niv = 16, - }, - /* Big ! */ - { - "/crypto/ivgen/plain/1f2e3d4c", - .sector = 0x1f2e3d4cULL, - .ivalg = QCRYPTO_IVGEN_ALG_PLAIN, - .iv = (const uint8_t *)"\x4c\x3d\x2e\x1f\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00", - .niv = 16, - }, - /* Truncation */ - { - "/crypto/ivgen/plain/1f2e3d4c5b6a7988", - .sector = 0x1f2e3d4c5b6a7988ULL, - .ivalg = QCRYPTO_IVGEN_ALG_PLAIN, - .iv = (const uint8_t *)"\x88\x79\x6a\x5b\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00", - .niv = 16, - }, - /* Small */ - { - "/crypto/ivgen/plain64/1", - .sector = 0x1, - .ivalg = QCRYPTO_IVGEN_ALG_PLAIN64, - .iv = (const uint8_t *)"\x01\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00", - .niv = 16, - }, - /* Big ! */ - { - "/crypto/ivgen/plain64/1f2e3d4c", - .sector = 0x1f2e3d4cULL, - .ivalg = QCRYPTO_IVGEN_ALG_PLAIN64, - .iv = (const uint8_t *)"\x4c\x3d\x2e\x1f\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00", - .niv = 16, - }, - /* No Truncation */ - { - "/crypto/ivgen/plain64/1f2e3d4c5b6a7988", - .sector = 0x1f2e3d4c5b6a7988ULL, - .ivalg = QCRYPTO_IVGEN_ALG_PLAIN64, - .iv = (const uint8_t *)"\x88\x79\x6a\x5b\x4c\x3d\x2e\x1f" - "\x00\x00\x00\x00\x00\x00\x00\x00", - .niv = 16, - }, - /* Small */ - { - "/crypto/ivgen/essiv/1", - .sector = 0x1, - .ivalg = QCRYPTO_IVGEN_ALG_ESSIV, - .cipheralg = QCRYPTO_CIPHER_ALG_AES_128, - .hashalg = QCRYPTO_HASH_ALG_SHA256, - .key = (const uint8_t *)"\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", - .nkey = 16, - .iv = (const uint8_t *)"\xd4\x83\x71\xb2\xa1\x94\x53\x88" - "\x1c\x7a\x2d\06\x2d\x0b\x65\x46", - .niv = 16, - }, - /* Big ! */ - { - "/crypto/ivgen/essiv/1f2e3d4c", - .sector = 0x1f2e3d4cULL, - .ivalg = QCRYPTO_IVGEN_ALG_ESSIV, - .cipheralg = QCRYPTO_CIPHER_ALG_AES_128, - .hashalg = QCRYPTO_HASH_ALG_SHA256, - .key = (const uint8_t *)"\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", - .nkey = 16, - .iv = (const uint8_t *)"\x5d\x36\x09\x5d\xc6\x9e\x5e\xe9" - "\xe3\x02\x8d\xd8\x7a\x3d\xe7\x8f", - .niv = 16, - }, - /* No Truncation */ - { - "/crypto/ivgen/essiv/1f2e3d4c5b6a7988", - .sector = 0x1f2e3d4c5b6a7988ULL, - .ivalg = QCRYPTO_IVGEN_ALG_ESSIV, - .cipheralg = QCRYPTO_CIPHER_ALG_AES_128, - .hashalg = QCRYPTO_HASH_ALG_SHA256, - .key = (const uint8_t *)"\x00\x01\x02\x03\x04\x05\x06\x07" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", - .nkey = 16, - .iv = (const uint8_t *)"\x58\xbb\x81\x94\x51\x83\x23\x23" - "\x7a\x08\x93\xa9\xdc\xd2\xd9\xab", - .niv = 16, - }, -}; - - -static void test_ivgen(const void *opaque) -{ - const struct QCryptoIVGenTestData *data = opaque; - uint8_t *iv = g_new0(uint8_t, data->niv); - QCryptoIVGen *ivgen = qcrypto_ivgen_new( - data->ivalg, - data->cipheralg, - data->hashalg, - data->key, - data->nkey, - &error_abort); - - qcrypto_ivgen_calculate(ivgen, - data->sector, - iv, - data->niv, - &error_abort); - - g_assert(memcmp(iv, data->iv, data->niv) == 0); - - qcrypto_ivgen_free(ivgen); - g_free(iv); -} - -int main(int argc, char **argv) -{ - size_t i; - g_test_init(&argc, &argv, NULL); - for (i = 0; i < G_N_ELEMENTS(test_data); i++) { - if (test_data[i].ivalg == QCRYPTO_IVGEN_ALG_ESSIV && - !qcrypto_hash_supports(test_data[i].hashalg)) { - continue; - } - g_test_add_data_func(test_data[i].path, - &(test_data[i]), - test_ivgen); - } - return g_test_run(); -} diff --git a/tests/test-crypto-pbkdf.c b/tests/test-crypto-pbkdf.c deleted file mode 100644 index c50fd639d2..0000000000 --- a/tests/test-crypto-pbkdf.c +++ /dev/null @@ -1,446 +0,0 @@ -/* - * QEMU Crypto cipher algorithms - * - * Copyright (c) 2015-2016 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - * - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "crypto/init.h" -#ifndef _WIN32 -#include -#endif - -#if ((defined(CONFIG_NETTLE) || defined(CONFIG_GCRYPT)) && \ - (defined(_WIN32) || defined(RUSAGE_THREAD))) -#include "crypto/pbkdf.h" - -typedef struct QCryptoPbkdfTestData QCryptoPbkdfTestData; -struct QCryptoPbkdfTestData { - const char *path; - QCryptoHashAlgorithm hash; - unsigned int iterations; - const char *key; - size_t nkey; - const char *salt; - size_t nsalt; - const char *out; - size_t nout; - bool slow; -}; - -/* This test data comes from cryptsetup package - * - * $SRC/lib/crypto_backend/pbkdf2_generic.c - * - * under LGPLv2.1+ license - */ -static QCryptoPbkdfTestData test_data[] = { - /* RFC 3962 test data */ - { - .path = "/crypto/pbkdf/rfc3962/sha1/iter1", - .hash = QCRYPTO_HASH_ALG_SHA1, - .iterations = 1, - .key = "password", - .nkey = 8, - .salt = "ATHENA.MIT.EDUraeburn", - .nsalt = 21, - .out = "\xcd\xed\xb5\x28\x1b\xb2\xf8\x01" - "\x56\x5a\x11\x22\xb2\x56\x35\x15" - "\x0a\xd1\xf7\xa0\x4b\xb9\xf3\xa3" - "\x33\xec\xc0\xe2\xe1\xf7\x08\x37", - .nout = 32 - }, - { - .path = "/crypto/pbkdf/rfc3962/sha1/iter2", - .hash = QCRYPTO_HASH_ALG_SHA1, - .iterations = 2, - .key = "password", - .nkey = 8, - .salt = "ATHENA.MIT.EDUraeburn", - .nsalt = 21, - .out = "\x01\xdb\xee\x7f\x4a\x9e\x24\x3e" - "\x98\x8b\x62\xc7\x3c\xda\x93\x5d" - "\xa0\x53\x78\xb9\x32\x44\xec\x8f" - "\x48\xa9\x9e\x61\xad\x79\x9d\x86", - .nout = 32 - }, - { - .path = "/crypto/pbkdf/rfc3962/sha1/iter1200a", - .hash = QCRYPTO_HASH_ALG_SHA1, - .iterations = 1200, - .key = "password", - .nkey = 8, - .salt = "ATHENA.MIT.EDUraeburn", - .nsalt = 21, - .out = "\x5c\x08\xeb\x61\xfd\xf7\x1e\x4e" - "\x4e\xc3\xcf\x6b\xa1\xf5\x51\x2b" - "\xa7\xe5\x2d\xdb\xc5\xe5\x14\x2f" - "\x70\x8a\x31\xe2\xe6\x2b\x1e\x13", - .nout = 32 - }, - { - .path = "/crypto/pbkdf/rfc3962/sha1/iter5", - .hash = QCRYPTO_HASH_ALG_SHA1, - .iterations = 5, - .key = "password", - .nkey = 8, - .salt = "\0224VxxV4\022", /* "\x1234567878563412 */ - .nsalt = 8, - .out = "\xd1\xda\xa7\x86\x15\xf2\x87\xe6" - "\xa1\xc8\xb1\x20\xd7\x06\x2a\x49" - "\x3f\x98\xd2\x03\xe6\xbe\x49\xa6" - "\xad\xf4\xfa\x57\x4b\x6e\x64\xee", - .nout = 32 - }, - { - .path = "/crypto/pbkdf/rfc3962/sha1/iter1200b", - .hash = QCRYPTO_HASH_ALG_SHA1, - .iterations = 1200, - .key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", - .nkey = 64, - .salt = "pass phrase equals block size", - .nsalt = 29, - .out = "\x13\x9c\x30\xc0\x96\x6b\xc3\x2b" - "\xa5\x5f\xdb\xf2\x12\x53\x0a\xc9" - "\xc5\xec\x59\xf1\xa4\x52\xf5\xcc" - "\x9a\xd9\x40\xfe\xa0\x59\x8e\xd1", - .nout = 32 - }, - { - .path = "/crypto/pbkdf/rfc3962/sha1/iter1200c", - .hash = QCRYPTO_HASH_ALG_SHA1, - .iterations = 1200, - .key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", - .nkey = 65, - .salt = "pass phrase exceeds block size", - .nsalt = 30, - .out = "\x9c\xca\xd6\xd4\x68\x77\x0c\xd5" - "\x1b\x10\xe6\xa6\x87\x21\xbe\x61" - "\x1a\x8b\x4d\x28\x26\x01\xdb\x3b" - "\x36\xbe\x92\x46\x91\x5e\xc8\x2a", - .nout = 32 - }, - { - .path = "/crypto/pbkdf/rfc3962/sha1/iter50", - .hash = QCRYPTO_HASH_ALG_SHA1, - .iterations = 50, - .key = "\360\235\204\236", /* g-clef ("\xf09d849e) */ - .nkey = 4, - .salt = "EXAMPLE.COMpianist", - .nsalt = 18, - .out = "\x6b\x9c\xf2\x6d\x45\x45\x5a\x43" - "\xa5\xb8\xbb\x27\x6a\x40\x3b\x39" - "\xe7\xfe\x37\xa0\xc4\x1e\x02\xc2" - "\x81\xff\x30\x69\xe1\xe9\x4f\x52", - .nout = 32 - }, - - /* RFC-6070 test data */ - { - .path = "/crypto/pbkdf/rfc6070/sha1/iter1", - .hash = QCRYPTO_HASH_ALG_SHA1, - .iterations = 1, - .key = "password", - .nkey = 8, - .salt = "salt", - .nsalt = 4, - .out = "\x0c\x60\xc8\x0f\x96\x1f\x0e\x71\xf3\xa9" - "\xb5\x24\xaf\x60\x12\x06\x2f\xe0\x37\xa6", - .nout = 20 - }, - { - .path = "/crypto/pbkdf/rfc6070/sha1/iter2", - .hash = QCRYPTO_HASH_ALG_SHA1, - .iterations = 2, - .key = "password", - .nkey = 8, - .salt = "salt", - .nsalt = 4, - .out = "\xea\x6c\x01\x4d\xc7\x2d\x6f\x8c\xcd\x1e" - "\xd9\x2a\xce\x1d\x41\xf0\xd8\xde\x89\x57", - .nout = 20 - }, - { - .path = "/crypto/pbkdf/rfc6070/sha1/iter4096", - .hash = QCRYPTO_HASH_ALG_SHA1, - .iterations = 4096, - .key = "password", - .nkey = 8, - .salt = "salt", - .nsalt = 4, - .out = "\x4b\x00\x79\x01\xb7\x65\x48\x9a\xbe\xad" - "\x49\xd9\x26\xf7\x21\xd0\x65\xa4\x29\xc1", - .nout = 20 - }, - { - .path = "/crypto/pbkdf/rfc6070/sha1/iter16777216", - .hash = QCRYPTO_HASH_ALG_SHA1, - .iterations = 16777216, - .key = "password", - .nkey = 8, - .salt = "salt", - .nsalt = 4, - .out = "\xee\xfe\x3d\x61\xcd\x4d\xa4\xe4\xe9\x94" - "\x5b\x3d\x6b\xa2\x15\x8c\x26\x34\xe9\x84", - .nout = 20, - .slow = true, - }, - { - .path = "/crypto/pbkdf/rfc6070/sha1/iter4096a", - .hash = QCRYPTO_HASH_ALG_SHA1, - .iterations = 4096, - .key = "passwordPASSWORDpassword", - .nkey = 24, - .salt = "saltSALTsaltSALTsaltSALTsaltSALTsalt", - .nsalt = 36, - .out = "\x3d\x2e\xec\x4f\xe4\x1c\x84\x9b\x80\xc8" - "\xd8\x36\x62\xc0\xe4\x4a\x8b\x29\x1a\x96" - "\x4c\xf2\xf0\x70\x38", - .nout = 25 - }, - { - .path = "/crypto/pbkdf/rfc6070/sha1/iter4096b", - .hash = QCRYPTO_HASH_ALG_SHA1, - .iterations = 4096, - .key = "pass\0word", - .nkey = 9, - .salt = "sa\0lt", - .nsalt = 5, - .out = "\x56\xfa\x6a\xa7\x55\x48\x09\x9d\xcc\x37" - "\xd7\xf0\x34\x25\xe0\xc3", - .nout = 16 - }, - - /* non-RFC misc test data */ -#ifdef CONFIG_NETTLE - { - /* empty password test. - * Broken with libgcrypt <= 1.5.0, hence CONFIG_NETTLE */ - .path = "/crypto/pbkdf/nonrfc/sha1/iter2", - .hash = QCRYPTO_HASH_ALG_SHA1, - .iterations = 2, - .key = "", - .nkey = 0, - .salt = "salt", - .nsalt = 4, - .out = "\x13\x3a\x4c\xe8\x37\xb4\xd2\x52\x1e\xe2" - "\xbf\x03\xe1\x1c\x71\xca\x79\x4e\x07\x97", - .nout = 20 - }, -#endif - { - /* Password exceeds block size test */ - .path = "/crypto/pbkdf/nonrfc/sha256/iter1200", - .hash = QCRYPTO_HASH_ALG_SHA256, - .iterations = 1200, - .key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", - .nkey = 65, - .salt = "pass phrase exceeds block size", - .nsalt = 30, - .out = "\x22\x34\x4b\xc4\xb6\xe3\x26\x75" - "\xa8\x09\x0f\x3e\xa8\x0b\xe0\x1d" - "\x5f\x95\x12\x6a\x2c\xdd\xc3\xfa" - "\xcc\x4a\x5e\x6d\xca\x04\xec\x58", - .nout = 32 - }, - { - .path = "/crypto/pbkdf/nonrfc/sha512/iter1200", - .hash = QCRYPTO_HASH_ALG_SHA512, - .iterations = 1200, - .key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", - .nkey = 129, - .salt = "pass phrase exceeds block size", - .nsalt = 30, - .out = "\x0f\xb2\xed\x2c\x0e\x6e\xfb\x7d" - "\x7d\x8e\xdd\x58\x01\xb4\x59\x72" - "\x99\x92\x16\x30\x5e\xa4\x36\x8d" - "\x76\x14\x80\xf3\xe3\x7a\x22\xb9", - .nout = 32 - }, - { - .path = "/crypto/pbkdf/nonrfc/sha224/iter1200", - .hash = QCRYPTO_HASH_ALG_SHA224, - .iterations = 1200, - .key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", - .nkey = 129, - .salt = "pass phrase exceeds block size", - .nsalt = 30, - .out = "\x13\x3b\x88\x0c\x0e\x52\xa2\x41" - "\x49\x33\x35\xa6\xc3\x83\xae\x23" - "\xf6\x77\x43\x9e\x5b\x30\x92\x3e" - "\x4a\x3a\xaa\x24\x69\x3c\xed\x20", - .nout = 32 - }, - { - .path = "/crypto/pbkdf/nonrfc/sha384/iter1200", - .hash = QCRYPTO_HASH_ALG_SHA384, - .iterations = 1200, - .key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", - .nkey = 129, - .salt = "pass phrase exceeds block size", - .nsalt = 30, - .out = "\xfe\xe3\xe1\x84\xc9\x25\x3e\x10" - "\x47\xc8\x7d\x53\xc6\xa5\xe3\x77" - "\x29\x41\x76\xbd\x4b\xe3\x9b\xac" - "\x05\x6c\x11\xdd\x17\xc5\x93\x80", - .nout = 32 - }, - { - .path = "/crypto/pbkdf/nonrfc/ripemd160/iter1200", - .hash = QCRYPTO_HASH_ALG_RIPEMD160, - .iterations = 1200, - .key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", - .nkey = 129, - .salt = "pass phrase exceeds block size", - .nsalt = 30, - .out = "\xd6\xcb\xd8\xa7\xdb\x0c\xa2\x2a" - "\x23\x5e\x47\xaf\xdb\xda\xa8\xef" - "\xe4\x01\x0d\x6f\xb5\x33\xc8\xbd" - "\xce\xbf\x91\x14\x8b\x5c\x48\x41", - .nout = 32 - }, -#if 0 - { - .path = "/crypto/pbkdf/nonrfc/whirlpool/iter1200", - .hash = QCRYPTO_HASH_ALG_WHIRLPOOL, - .iterations = 1200, - .key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", - .nkey = 65, - .salt = "pass phrase exceeds block size", - .nsalt = 30, - .out = "\x9c\x1c\x74\xf5\x88\x26\xe7\x6a" - "\x53\x58\xf4\x0c\x39\xe7\x80\x89" - "\x07\xc0\x31\x19\x9a\x50\xa2\x48" - "\xf1\xd9\xfe\x78\x64\xe5\x84\x50", - .nout = 32 - } -#endif -}; - - -static inline char hex(int i) -{ - if (i < 10) { - return '0' + i; - } - return 'a' + (i - 10); -} - -static char *hex_string(const uint8_t *bytes, - size_t len) -{ - char *hexstr = g_new0(char, len * 2 + 1); - size_t i; - - for (i = 0; i < len; i++) { - hexstr[i * 2] = hex((bytes[i] >> 4) & 0xf); - hexstr[i * 2 + 1] = hex(bytes[i] & 0xf); - } - hexstr[len * 2] = '\0'; - - return hexstr; -} - -static void test_pbkdf(const void *opaque) -{ - const QCryptoPbkdfTestData *data = opaque; - size_t nout = data->nout; - uint8_t *out = g_new0(uint8_t, nout); - gchar *expect, *actual; - - qcrypto_pbkdf2(data->hash, - (uint8_t *)data->key, data->nkey, - (uint8_t *)data->salt, data->nsalt, - data->iterations, - (uint8_t *)out, nout, - &error_abort); - - expect = hex_string((const uint8_t *)data->out, data->nout); - actual = hex_string(out, nout); - - g_assert_cmpstr(actual, ==, expect); - - g_free(actual); - g_free(expect); - g_free(out); -} - - -static void test_pbkdf_timing(void) -{ - uint8_t key[32]; - uint8_t salt[32]; - int iters; - - memset(key, 0x5d, sizeof(key)); - memset(salt, 0x7c, sizeof(salt)); - - iters = qcrypto_pbkdf2_count_iters(QCRYPTO_HASH_ALG_SHA256, - key, sizeof(key), - salt, sizeof(salt), - 32, - &error_abort); - - g_assert(iters >= (1 << 15)); -} - - -int main(int argc, char **argv) -{ - size_t i; - - g_test_init(&argc, &argv, NULL); - - g_assert(qcrypto_init(NULL) == 0); - - for (i = 0; i < G_N_ELEMENTS(test_data); i++) { - if (!test_data[i].slow || - g_test_slow()) { - g_test_add_data_func(test_data[i].path, &test_data[i], test_pbkdf); - } - } - - if (g_test_slow()) { - g_test_add_func("/crypt0/pbkdf/timing", test_pbkdf_timing); - } - - return g_test_run(); -} -#else -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - return g_test_run(); -} -#endif diff --git a/tests/test-crypto-secret.c b/tests/test-crypto-secret.c deleted file mode 100644 index 34a4aecc12..0000000000 --- a/tests/test-crypto-secret.c +++ /dev/null @@ -1,614 +0,0 @@ -/* - * QEMU Crypto secret handling - * - * Copyright (c) 2015 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - * - */ - -#include "qemu/osdep.h" - -#include "crypto/init.h" -#include "crypto/secret.h" -#include "qapi/error.h" -#include "qemu/module.h" -#ifdef CONFIG_KEYUTILS -#include "crypto/secret_keyring.h" -#include -#endif - -static void test_secret_direct(void) -{ - Object *sec = object_new_with_props( - TYPE_QCRYPTO_SECRET, - object_get_objects_root(), - "sec0", - &error_abort, - "data", "123456", - NULL); - - char *pw = qcrypto_secret_lookup_as_utf8("sec0", - &error_abort); - - g_assert_cmpstr(pw, ==, "123456"); - - object_unparent(sec); - g_free(pw); -} - - -static void test_secret_indirect_good(void) -{ - Object *sec; - char *fname = NULL; - int fd = g_file_open_tmp("qemu-test-crypto-secret-XXXXXX", - &fname, - NULL); - - g_assert(fd >= 0); - g_assert_nonnull(fname); - - g_assert(write(fd, "123456", 6) == 6); - - sec = object_new_with_props( - TYPE_QCRYPTO_SECRET, - object_get_objects_root(), - "sec0", - &error_abort, - "file", fname, - NULL); - - char *pw = qcrypto_secret_lookup_as_utf8("sec0", - &error_abort); - - g_assert_cmpstr(pw, ==, "123456"); - - object_unparent(sec); - g_free(pw); - close(fd); - unlink(fname); - g_free(fname); -} - - -static void test_secret_indirect_badfile(void) -{ - Object *sec = object_new_with_props( - TYPE_QCRYPTO_SECRET, - object_get_objects_root(), - "sec0", - NULL, - "file", "does-not-exist", - NULL); - - g_assert(sec == NULL); -} - - -static void test_secret_indirect_emptyfile(void) -{ - Object *sec; - char *fname = NULL; - int fd = g_file_open_tmp("qemu-test-crypto-secretXXXXXX", - &fname, - NULL); - - g_assert(fd >= 0); - g_assert_nonnull(fname); - - sec = object_new_with_props( - TYPE_QCRYPTO_SECRET, - object_get_objects_root(), - "sec0", - &error_abort, - "file", fname, - NULL); - - char *pw = qcrypto_secret_lookup_as_utf8("sec0", - &error_abort); - - g_assert_cmpstr(pw, ==, ""); - - object_unparent(sec); - g_free(pw); - close(fd); - unlink(fname); - g_free(fname); -} - -#ifdef CONFIG_KEYUTILS - -#define DESCRIPTION "qemu_test_secret" -#define PAYLOAD "Test Payload" - - -static void test_secret_keyring_good(void) -{ - char key_str[16]; - Object *sec; - int32_t key = add_key("user", DESCRIPTION, PAYLOAD, - strlen(PAYLOAD), KEY_SPEC_PROCESS_KEYRING); - - g_assert(key >= 0); - - snprintf(key_str, sizeof(key_str), "0x%08x", key); - sec = object_new_with_props( - TYPE_QCRYPTO_SECRET_KEYRING, - object_get_objects_root(), - "sec0", - &error_abort, - "serial", key_str, - NULL); - - assert(0 <= keyctl_unlink(key, KEY_SPEC_PROCESS_KEYRING)); - char *pw = qcrypto_secret_lookup_as_utf8("sec0", - &error_abort); - g_assert_cmpstr(pw, ==, PAYLOAD); - - object_unparent(sec); - g_free(pw); -} - - -static void test_secret_keyring_revoked_key(void) -{ - char key_str[16]; - Object *sec; - int32_t key = add_key("user", DESCRIPTION, PAYLOAD, - strlen(PAYLOAD), KEY_SPEC_PROCESS_KEYRING); - g_assert(key >= 0); - g_assert_false(keyctl_revoke(key)); - - snprintf(key_str, sizeof(key_str), "0x%08x", key); - sec = object_new_with_props( - TYPE_QCRYPTO_SECRET_KEYRING, - object_get_objects_root(), - "sec0", - NULL, - "serial", key_str, - NULL); - - g_assert(errno == EKEYREVOKED); - g_assert(sec == NULL); - - keyctl_unlink(key, KEY_SPEC_PROCESS_KEYRING); -} - - -static void test_secret_keyring_expired_key(void) -{ - char key_str[16]; - Object *sec; - int32_t key = add_key("user", DESCRIPTION, PAYLOAD, - strlen(PAYLOAD), KEY_SPEC_PROCESS_KEYRING); - g_assert(key >= 0); - g_assert_false(keyctl_set_timeout(key, 1)); - sleep(1); - - snprintf(key_str, sizeof(key_str), "0x%08x", key); - sec = object_new_with_props( - TYPE_QCRYPTO_SECRET_KEYRING, - object_get_objects_root(), - "sec0", - NULL, - "serial", key_str, - NULL); - - g_assert(errno == EKEYEXPIRED); - g_assert(sec == NULL); - - keyctl_unlink(key, KEY_SPEC_PROCESS_KEYRING); -} - - -static void test_secret_keyring_bad_serial_key(void) -{ - Object *sec; - - sec = object_new_with_props( - TYPE_QCRYPTO_SECRET_KEYRING, - object_get_objects_root(), - "sec0", - NULL, - "serial", "1", - NULL); - - g_assert(errno == ENOKEY); - g_assert(sec == NULL); -} - -/* - * TODO - * test_secret_keyring_bad_key_access_right() is not working yet. - * We don't know yet if this due a bug in the Linux kernel or - * whether it's normal syscall behavior. - * We've requested information from kernel maintainers. - * See: - * Thread: 'security/keys: remove possessor verify after key permission check' - */ - -static void test_secret_keyring_bad_key_access_right(void) -{ - char key_str[16]; - Object *sec; - - g_test_skip("TODO: Need responce from Linux kernel maintainers"); - return; - - int32_t key = add_key("user", DESCRIPTION, PAYLOAD, - strlen(PAYLOAD), KEY_SPEC_PROCESS_KEYRING); - g_assert(key >= 0); - g_assert_false(keyctl_setperm(key, KEY_POS_ALL & (~KEY_POS_READ))); - - snprintf(key_str, sizeof(key_str), "0x%08x", key); - - sec = object_new_with_props( - TYPE_QCRYPTO_SECRET_KEYRING, - object_get_objects_root(), - "sec0", - NULL, - "serial", key_str, - NULL); - - g_assert(errno == EACCES); - g_assert(sec == NULL); - - keyctl_unlink(key, KEY_SPEC_PROCESS_KEYRING); -} - -#endif /* CONFIG_KEYUTILS */ - -static void test_secret_noconv_base64_good(void) -{ - Object *sec = object_new_with_props( - TYPE_QCRYPTO_SECRET, - object_get_objects_root(), - "sec0", - &error_abort, - "data", "MTIzNDU2", - "format", "base64", - NULL); - - char *pw = qcrypto_secret_lookup_as_base64("sec0", - &error_abort); - - g_assert_cmpstr(pw, ==, "MTIzNDU2"); - - object_unparent(sec); - g_free(pw); -} - - -static void test_secret_noconv_base64_bad(void) -{ - Object *sec = object_new_with_props( - TYPE_QCRYPTO_SECRET, - object_get_objects_root(), - "sec0", - NULL, - "data", "MTI$NDU2", - "format", "base64", - NULL); - - g_assert(sec == NULL); -} - - -static void test_secret_noconv_utf8(void) -{ - Object *sec = object_new_with_props( - TYPE_QCRYPTO_SECRET, - object_get_objects_root(), - "sec0", - &error_abort, - "data", "123456", - "format", "raw", - NULL); - - char *pw = qcrypto_secret_lookup_as_utf8("sec0", - &error_abort); - - g_assert_cmpstr(pw, ==, "123456"); - - object_unparent(sec); - g_free(pw); -} - - -static void test_secret_conv_base64_utf8valid(void) -{ - Object *sec = object_new_with_props( - TYPE_QCRYPTO_SECRET, - object_get_objects_root(), - "sec0", - &error_abort, - "data", "MTIzNDU2", - "format", "base64", - NULL); - - char *pw = qcrypto_secret_lookup_as_utf8("sec0", - &error_abort); - - g_assert_cmpstr(pw, ==, "123456"); - - object_unparent(sec); - g_free(pw); -} - - -static void test_secret_conv_base64_utf8invalid(void) -{ - Object *sec = object_new_with_props( - TYPE_QCRYPTO_SECRET, - object_get_objects_root(), - "sec0", - &error_abort, - "data", "f0VMRgIBAQAAAA==", - "format", "base64", - NULL); - - char *pw = qcrypto_secret_lookup_as_utf8("sec0", - NULL); - g_assert(pw == NULL); - - object_unparent(sec); -} - - -static void test_secret_conv_utf8_base64(void) -{ - Object *sec = object_new_with_props( - TYPE_QCRYPTO_SECRET, - object_get_objects_root(), - "sec0", - &error_abort, - "data", "123456", - NULL); - - char *pw = qcrypto_secret_lookup_as_base64("sec0", - &error_abort); - - g_assert_cmpstr(pw, ==, "MTIzNDU2"); - - object_unparent(sec); - g_free(pw); -} - - -static void test_secret_crypt_raw(void) -{ - Object *master = object_new_with_props( - TYPE_QCRYPTO_SECRET, - object_get_objects_root(), - "master", - &error_abort, - "data", "9miloPQCzGy+TL6aonfzVcptibCmCIhKzrnlfwiWivk=", - "format", "base64", - NULL); - Object *sec = object_new_with_props( - TYPE_QCRYPTO_SECRET, - object_get_objects_root(), - "sec0", - &error_abort, - "data", - "\xCC\xBF\xF7\x09\x46\x19\x0B\x52\x2A\x3A\xB4\x6B\xCD\x7A\xB0\xB0", - "format", "raw", - "keyid", "master", - "iv", "0I7Gw/TKuA+Old2W2apQ3g==", - NULL); - - char *pw = qcrypto_secret_lookup_as_utf8("sec0", - &error_abort); - - g_assert_cmpstr(pw, ==, "123456"); - - object_unparent(sec); - object_unparent(master); - g_free(pw); -} - - -static void test_secret_crypt_base64(void) -{ - Object *master = object_new_with_props( - TYPE_QCRYPTO_SECRET, - object_get_objects_root(), - "master", - &error_abort, - "data", "9miloPQCzGy+TL6aonfzVcptibCmCIhKzrnlfwiWivk=", - "format", "base64", - NULL); - Object *sec = object_new_with_props( - TYPE_QCRYPTO_SECRET, - object_get_objects_root(), - "sec0", - &error_abort, - "data", "zL/3CUYZC1IqOrRrzXqwsA==", - "format", "base64", - "keyid", "master", - "iv", "0I7Gw/TKuA+Old2W2apQ3g==", - NULL); - - char *pw = qcrypto_secret_lookup_as_utf8("sec0", - &error_abort); - - g_assert_cmpstr(pw, ==, "123456"); - - object_unparent(sec); - object_unparent(master); - g_free(pw); -} - - -static void test_secret_crypt_short_key(void) -{ - Object *master = object_new_with_props( - TYPE_QCRYPTO_SECRET, - object_get_objects_root(), - "master", - &error_abort, - "data", "9miloPQCzGy+TL6aonfzVc", - "format", "base64", - NULL); - Object *sec = object_new_with_props( - TYPE_QCRYPTO_SECRET, - object_get_objects_root(), - "sec0", - NULL, - "data", "zL/3CUYZC1IqOrRrzXqwsA==", - "format", "raw", - "keyid", "master", - "iv", "0I7Gw/TKuA+Old2W2apQ3g==", - NULL); - - g_assert(sec == NULL); - object_unparent(master); -} - - -static void test_secret_crypt_short_iv(void) -{ - Object *master = object_new_with_props( - TYPE_QCRYPTO_SECRET, - object_get_objects_root(), - "master", - &error_abort, - "data", "9miloPQCzGy+TL6aonfzVcptibCmCIhKzrnlfwiWivk=", - "format", "base64", - NULL); - Object *sec = object_new_with_props( - TYPE_QCRYPTO_SECRET, - object_get_objects_root(), - "sec0", - NULL, - "data", "zL/3CUYZC1IqOrRrzXqwsA==", - "format", "raw", - "keyid", "master", - "iv", "0I7Gw/TKuA+Old2W2a", - NULL); - - g_assert(sec == NULL); - object_unparent(master); -} - - -static void test_secret_crypt_missing_iv(void) -{ - Object *master = object_new_with_props( - TYPE_QCRYPTO_SECRET, - object_get_objects_root(), - "master", - &error_abort, - "data", "9miloPQCzGy+TL6aonfzVcptibCmCIhKzrnlfwiWivk=", - "format", "base64", - NULL); - Object *sec = object_new_with_props( - TYPE_QCRYPTO_SECRET, - object_get_objects_root(), - "sec0", - NULL, - "data", "zL/3CUYZC1IqOrRrzXqwsA==", - "format", "raw", - "keyid", "master", - NULL); - - g_assert(sec == NULL); - object_unparent(master); -} - - -static void test_secret_crypt_bad_iv(void) -{ - Object *master = object_new_with_props( - TYPE_QCRYPTO_SECRET, - object_get_objects_root(), - "master", - &error_abort, - "data", "9miloPQCzGy+TL6aonfzVcptibCmCIhKzrnlfwiWivk=", - "format", "base64", - NULL); - Object *sec = object_new_with_props( - TYPE_QCRYPTO_SECRET, - object_get_objects_root(), - "sec0", - NULL, - "data", "zL/3CUYZC1IqOrRrzXqwsA==", - "format", "raw", - "keyid", "master", - "iv", "0I7Gw/TK$$uA+Old2W2a", - NULL); - - g_assert(sec == NULL); - object_unparent(master); -} - - -int main(int argc, char **argv) -{ - module_call_init(MODULE_INIT_QOM); - g_test_init(&argc, &argv, NULL); - - g_assert(qcrypto_init(NULL) == 0); - - g_test_add_func("/crypto/secret/direct", - test_secret_direct); - g_test_add_func("/crypto/secret/indirect/good", - test_secret_indirect_good); - g_test_add_func("/crypto/secret/indirect/badfile", - test_secret_indirect_badfile); - g_test_add_func("/crypto/secret/indirect/emptyfile", - test_secret_indirect_emptyfile); - -#ifdef CONFIG_KEYUTILS - g_test_add_func("/crypto/secret/keyring/good", - test_secret_keyring_good); - g_test_add_func("/crypto/secret/keyring/revoked_key", - test_secret_keyring_revoked_key); - g_test_add_func("/crypto/secret/keyring/expired_key", - test_secret_keyring_expired_key); - g_test_add_func("/crypto/secret/keyring/bad_serial_key", - test_secret_keyring_bad_serial_key); - g_test_add_func("/crypto/secret/keyring/bad_key_access_right", - test_secret_keyring_bad_key_access_right); -#endif /* CONFIG_KEYUTILS */ - - g_test_add_func("/crypto/secret/noconv/base64/good", - test_secret_noconv_base64_good); - g_test_add_func("/crypto/secret/noconv/base64/bad", - test_secret_noconv_base64_bad); - g_test_add_func("/crypto/secret/noconv/utf8", - test_secret_noconv_utf8); - g_test_add_func("/crypto/secret/conv/base64/utf8valid", - test_secret_conv_base64_utf8valid); - g_test_add_func("/crypto/secret/conv/base64/utf8invalid", - test_secret_conv_base64_utf8invalid); - g_test_add_func("/crypto/secret/conv/utf8/base64", - test_secret_conv_utf8_base64); - - g_test_add_func("/crypto/secret/crypt/raw", - test_secret_crypt_raw); - g_test_add_func("/crypto/secret/crypt/base64", - test_secret_crypt_base64); - g_test_add_func("/crypto/secret/crypt/shortkey", - test_secret_crypt_short_key); - g_test_add_func("/crypto/secret/crypt/shortiv", - test_secret_crypt_short_iv); - g_test_add_func("/crypto/secret/crypt/missingiv", - test_secret_crypt_missing_iv); - g_test_add_func("/crypto/secret/crypt/badiv", - test_secret_crypt_bad_iv); - - return g_test_run(); -} diff --git a/tests/test-crypto-tlscredsx509.c b/tests/test-crypto-tlscredsx509.c deleted file mode 100644 index f487349c32..0000000000 --- a/tests/test-crypto-tlscredsx509.c +++ /dev/null @@ -1,718 +0,0 @@ -/* - * Copyright (C) 2015 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see - * . - * - * Author: Daniel P. Berrange - */ - -#include "qemu/osdep.h" - -#include "crypto-tls-x509-helpers.h" -#include "crypto/tlscredsx509.h" -#include "qapi/error.h" -#include "qemu/module.h" - -#ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT - -#define WORKDIR "tests/test-crypto-tlscredsx509-work/" -#define KEYFILE WORKDIR "key-ctx.pem" - -struct QCryptoTLSCredsTestData { - bool isServer; - const char *cacrt; - const char *crt; - bool expectFail; -}; - - -static QCryptoTLSCreds *test_tls_creds_create(QCryptoTLSCredsEndpoint endpoint, - const char *certdir, - Error **errp) -{ - Object *parent = object_get_objects_root(); - Object *creds = object_new_with_props( - TYPE_QCRYPTO_TLS_CREDS_X509, - parent, - "testtlscreds", - errp, - "endpoint", (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ? - "server" : "client"), - "dir", certdir, - "verify-peer", "yes", - "sanity-check", "yes", - NULL); - - if (!creds) { - return NULL; - } - return QCRYPTO_TLS_CREDS(creds); -} - -/* - * This tests sanity checking of our own certificates - * - * The code being tested is used when TLS creds are created, - * and aim to ensure QMEU has been configured with sane - * certificates. This allows us to give much much much - * clearer error messages to the admin when they misconfigure - * things. - */ -static void test_tls_creds(const void *opaque) -{ - struct QCryptoTLSCredsTestData *data = - (struct QCryptoTLSCredsTestData *)opaque; - QCryptoTLSCreds *creds; - -#define CERT_DIR "tests/test-crypto-tlscredsx509-certs/" - mkdir(CERT_DIR, 0700); - - unlink(CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT); - if (data->isServer) { - unlink(CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT); - unlink(CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY); - } else { - unlink(CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT); - unlink(CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY); - } - - if (access(data->cacrt, R_OK) == 0) { - g_assert(link(data->cacrt, - CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT) == 0); - } - if (data->isServer) { - if (access(data->crt, R_OK) == 0) { - g_assert(link(data->crt, - CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT) == 0); - } - g_assert(link(KEYFILE, - CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY) == 0); - } else { - if (access(data->crt, R_OK) == 0) { - g_assert(link(data->crt, - CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT) == 0); - } - g_assert(link(KEYFILE, - CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY) == 0); - } - - creds = test_tls_creds_create( - (data->isServer ? - QCRYPTO_TLS_CREDS_ENDPOINT_SERVER : - QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT), - CERT_DIR, - data->expectFail ? NULL : &error_abort); - - if (data->expectFail) { - g_assert(creds == NULL); - } else { - g_assert(creds != NULL); - } - - unlink(CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT); - if (data->isServer) { - unlink(CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT); - unlink(CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY); - } else { - unlink(CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT); - unlink(CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY); - } - rmdir(CERT_DIR); - if (creds) { - object_unparent(OBJECT(creds)); - } -} - -int main(int argc, char **argv) -{ - int ret; - - module_call_init(MODULE_INIT_QOM); - g_test_init(&argc, &argv, NULL); - g_setenv("GNUTLS_FORCE_FIPS_MODE", "2", 1); - - mkdir(WORKDIR, 0700); - - test_tls_init(KEYFILE); - -# define TLS_TEST_REG(name, isServer, caCrt, crt, expectFail) \ - struct QCryptoTLSCredsTestData name = { \ - isServer, caCrt, crt, expectFail \ - }; \ - g_test_add_data_func("/qcrypto/tlscredsx509/" # name, \ - &name, test_tls_creds); \ - - /* A perfect CA, perfect client & perfect server */ - - /* Basic:CA:critical */ - TLS_ROOT_REQ(cacertreq, - "UK", "qemu CA", NULL, NULL, NULL, NULL, - true, true, true, - true, true, GNUTLS_KEY_KEY_CERT_SIGN, - false, false, NULL, NULL, - 0, 0); - - TLS_CERT_REQ(servercertreq, cacertreq, - "UK", "qemu.org", NULL, NULL, NULL, NULL, - true, true, false, - true, true, - GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, - true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, - 0, 0); - TLS_CERT_REQ(clientcertreq, cacertreq, - "UK", "qemu", NULL, NULL, NULL, NULL, - true, true, false, - true, true, - GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, - true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL, - 0, 0); - - TLS_TEST_REG(perfectserver, true, - cacertreq.filename, servercertreq.filename, false); - TLS_TEST_REG(perfectclient, false, - cacertreq.filename, clientcertreq.filename, false); - - - /* Some other CAs which are good */ - - /* Basic:CA:critical */ - TLS_ROOT_REQ(cacert1req, - "UK", "qemu CA 1", NULL, NULL, NULL, NULL, - true, true, true, - false, false, 0, - false, false, NULL, NULL, - 0, 0); - TLS_CERT_REQ(servercert1req, cacert1req, - "UK", "qemu.org", NULL, NULL, NULL, NULL, - true, true, false, - true, true, - GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, - true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, - 0, 0); - - /* Basic:CA:not-critical */ - TLS_ROOT_REQ(cacert2req, - "UK", "qemu CA 2", NULL, NULL, NULL, NULL, - true, false, true, - false, false, 0, - false, false, NULL, NULL, - 0, 0); - TLS_CERT_REQ(servercert2req, cacert2req, - "UK", "qemu.org", NULL, NULL, NULL, NULL, - true, true, false, - true, true, - GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, - true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, - 0, 0); - - /* Key usage:cert-sign:critical */ - TLS_ROOT_REQ(cacert3req, - "UK", "qemu CA 3", NULL, NULL, NULL, NULL, - true, true, true, - true, true, GNUTLS_KEY_KEY_CERT_SIGN, - false, false, NULL, NULL, - 0, 0); - TLS_CERT_REQ(servercert3req, cacert3req, - "UK", "qemu.org", NULL, NULL, NULL, NULL, - true, true, false, - true, true, - GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, - true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, - 0, 0); - - TLS_TEST_REG(goodca1, true, - cacert1req.filename, servercert1req.filename, false); - TLS_TEST_REG(goodca2, true, - cacert2req.filename, servercert2req.filename, false); - TLS_TEST_REG(goodca3, true, - cacert3req.filename, servercert3req.filename, false); - - /* Now some bad certs */ - - /* Key usage:dig-sig:not-critical */ - TLS_ROOT_REQ(cacert4req, - "UK", "qemu CA 4", NULL, NULL, NULL, NULL, - true, true, true, - true, false, GNUTLS_KEY_DIGITAL_SIGNATURE, - false, false, NULL, NULL, - 0, 0); - TLS_CERT_REQ(servercert4req, cacert4req, - "UK", "qemu.org", NULL, NULL, NULL, NULL, - true, true, false, - true, true, - GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, - true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, - 0, 0); - /* no-basic */ - TLS_ROOT_REQ(cacert5req, - "UK", "qemu CA 5", NULL, NULL, NULL, NULL, - false, false, false, - false, false, 0, - false, false, NULL, NULL, - 0, 0); - TLS_CERT_REQ(servercert5req, cacert5req, - "UK", "qemu.org", NULL, NULL, NULL, NULL, - true, true, false, - true, true, - GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, - true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, - 0, 0); - /* Key usage:dig-sig:critical */ - TLS_ROOT_REQ(cacert6req, - "UK", "qemu CA 6", NULL, NULL, NULL, NULL, - true, true, true, - true, true, GNUTLS_KEY_DIGITAL_SIGNATURE, - false, false, NULL, NULL, - 0, 0); - TLS_CERT_REQ(servercert6req, cacert6req, - "UK", "qemu.org", NULL, NULL, NULL, NULL, - true, true, false, - true, true, - GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, - true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, - 0, 0); - - TLS_TEST_REG(badca1, true, cacert4req.filename, servercert4req.filename, - true); - TLS_TEST_REG(badca2, true, - cacert5req.filename, servercert5req.filename, true); - TLS_TEST_REG(badca3, true, - cacert6req.filename, servercert6req.filename, true); - - - /* Various good servers */ - /* no usage or purpose */ - TLS_CERT_REQ(servercert7req, cacertreq, - "UK", "qemu", NULL, NULL, NULL, NULL, - true, true, false, - false, false, 0, - false, false, NULL, NULL, - 0, 0); - /* usage:cert-sign+dig-sig+encipher:critical */ - TLS_CERT_REQ(servercert8req, cacertreq, - "UK", "qemu", NULL, NULL, NULL, NULL, - true, true, false, - true, true, - GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT | - GNUTLS_KEY_KEY_CERT_SIGN, - false, false, NULL, NULL, - 0, 0); - /* usage:cert-sign:not-critical */ - TLS_CERT_REQ(servercert9req, cacertreq, - "UK", "qemu", NULL, NULL, NULL, NULL, - true, true, false, - true, false, GNUTLS_KEY_KEY_CERT_SIGN, - false, false, NULL, NULL, - 0, 0); - /* purpose:server:critical */ - TLS_CERT_REQ(servercert10req, cacertreq, - "UK", "qemu", NULL, NULL, NULL, NULL, - true, true, false, - false, false, 0, - true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, - 0, 0); - /* purpose:server:not-critical */ - TLS_CERT_REQ(servercert11req, cacertreq, - "UK", "qemu", NULL, NULL, NULL, NULL, - true, true, false, - false, false, 0, - true, false, GNUTLS_KP_TLS_WWW_SERVER, NULL, - 0, 0); - /* purpose:client+server:critical */ - TLS_CERT_REQ(servercert12req, cacertreq, - "UK", "qemu", NULL, NULL, NULL, NULL, - true, true, false, - false, false, 0, - true, true, - GNUTLS_KP_TLS_WWW_CLIENT, GNUTLS_KP_TLS_WWW_SERVER, - 0, 0); - /* purpose:client+server:not-critical */ - TLS_CERT_REQ(servercert13req, cacertreq, - "UK", "qemu", NULL, NULL, NULL, NULL, - true, true, false, - false, false, 0, - true, false, - GNUTLS_KP_TLS_WWW_CLIENT, GNUTLS_KP_TLS_WWW_SERVER, - 0, 0); - - TLS_TEST_REG(goodserver1, true, - cacertreq.filename, servercert7req.filename, false); - TLS_TEST_REG(goodserver2, true, - cacertreq.filename, servercert8req.filename, false); - TLS_TEST_REG(goodserver3, true, - cacertreq.filename, servercert9req.filename, false); - TLS_TEST_REG(goodserver4, true, - cacertreq.filename, servercert10req.filename, false); - TLS_TEST_REG(goodserver5, true, - cacertreq.filename, servercert11req.filename, false); - TLS_TEST_REG(goodserver6, true, - cacertreq.filename, servercert12req.filename, false); - TLS_TEST_REG(goodserver7, true, - cacertreq.filename, servercert13req.filename, false); - - /* Bad servers */ - - /* usage:cert-sign:critical */ - TLS_CERT_REQ(servercert14req, cacertreq, - "UK", "qemu", NULL, NULL, NULL, NULL, - true, true, false, - true, true, GNUTLS_KEY_KEY_CERT_SIGN, - false, false, NULL, NULL, - 0, 0); - /* purpose:client:critical */ - TLS_CERT_REQ(servercert15req, cacertreq, - "UK", "qemu", NULL, NULL, NULL, NULL, - true, true, false, - false, false, 0, - true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL, - 0, 0); - /* usage: none:critical */ - TLS_CERT_REQ(servercert16req, cacertreq, - "UK", "qemu", NULL, NULL, NULL, NULL, - true, true, false, - true, true, 0, - false, false, NULL, NULL, - 0, 0); - - TLS_TEST_REG(badserver1, true, - cacertreq.filename, servercert14req.filename, true); - TLS_TEST_REG(badserver2, true, - cacertreq.filename, servercert15req.filename, true); - TLS_TEST_REG(badserver3, true, - cacertreq.filename, servercert16req.filename, true); - - - - /* Various good clients */ - /* no usage or purpose */ - TLS_CERT_REQ(clientcert1req, cacertreq, - "UK", "qemu", NULL, NULL, NULL, NULL, - true, true, false, - false, false, 0, - false, false, NULL, NULL, - 0, 0); - /* usage:cert-sign+dig-sig+encipher:critical */ - TLS_CERT_REQ(clientcert2req, cacertreq, - "UK", "qemu", NULL, NULL, NULL, NULL, - true, true, false, - true, true, - GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT | - GNUTLS_KEY_KEY_CERT_SIGN, - false, false, NULL, NULL, - 0, 0); - /* usage:cert-sign:not-critical */ - TLS_CERT_REQ(clientcert3req, cacertreq, - "UK", "qemu", NULL, NULL, NULL, NULL, - true, true, false, - true, false, GNUTLS_KEY_KEY_CERT_SIGN, - false, false, NULL, NULL, - 0, 0); - /* purpose:client:critical */ - TLS_CERT_REQ(clientcert4req, cacertreq, - "UK", "qemu", NULL, NULL, NULL, NULL, - true, true, false, - false, false, 0, - true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL, - 0, 0); - /* purpose:client:not-critical */ - TLS_CERT_REQ(clientcert5req, cacertreq, - "UK", "qemu", NULL, NULL, NULL, NULL, - true, true, false, - false, false, 0, - true, false, GNUTLS_KP_TLS_WWW_CLIENT, NULL, - 0, 0); - /* purpose:client+client:critical */ - TLS_CERT_REQ(clientcert6req, cacertreq, - "UK", "qemu", NULL, NULL, NULL, NULL, - true, true, false, - false, false, 0, - true, true, - GNUTLS_KP_TLS_WWW_CLIENT, GNUTLS_KP_TLS_WWW_SERVER, - 0, 0); - /* purpose:client+client:not-critical */ - TLS_CERT_REQ(clientcert7req, cacertreq, - "UK", "qemu", NULL, NULL, NULL, NULL, - true, true, false, - false, false, 0, - true, false, - GNUTLS_KP_TLS_WWW_CLIENT, GNUTLS_KP_TLS_WWW_SERVER, - 0, 0); - - TLS_TEST_REG(goodclient1, false, - cacertreq.filename, clientcert1req.filename, false); - TLS_TEST_REG(goodclient2, false, - cacertreq.filename, clientcert2req.filename, false); - TLS_TEST_REG(goodclient3, false, - cacertreq.filename, clientcert3req.filename, false); - TLS_TEST_REG(goodclient4, false, - cacertreq.filename, clientcert4req.filename, false); - TLS_TEST_REG(goodclient5, false, - cacertreq.filename, clientcert5req.filename, false); - TLS_TEST_REG(goodclient6, false, - cacertreq.filename, clientcert6req.filename, false); - TLS_TEST_REG(goodclient7, false, - cacertreq.filename, clientcert7req.filename, false); - - /* Bad clients */ - - /* usage:cert-sign:critical */ - TLS_CERT_REQ(clientcert8req, cacertreq, - "UK", "qemu", NULL, NULL, NULL, NULL, - true, true, false, - true, true, GNUTLS_KEY_KEY_CERT_SIGN, - false, false, NULL, NULL, - 0, 0); - /* purpose:client:critical */ - TLS_CERT_REQ(clientcert9req, cacertreq, - "UK", "qemu", NULL, NULL, NULL, NULL, - true, true, false, - false, false, 0, - true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, - 0, 0); - /* usage: none:critical */ - TLS_CERT_REQ(clientcert10req, cacertreq, - "UK", "qemu", NULL, NULL, NULL, NULL, - true, true, false, - true, true, 0, - false, false, NULL, NULL, - 0, 0); - - TLS_TEST_REG(badclient1, false, - cacertreq.filename, clientcert8req.filename, true); - TLS_TEST_REG(badclient2, false, - cacertreq.filename, clientcert9req.filename, true); - TLS_TEST_REG(badclient3, false, - cacertreq.filename, clientcert10req.filename, true); - - - - /* Expired stuff */ - - TLS_ROOT_REQ(cacertexpreq, - "UK", "qemu", NULL, NULL, NULL, NULL, - true, true, true, - true, true, GNUTLS_KEY_KEY_CERT_SIGN, - false, false, NULL, NULL, - 0, -1); - TLS_CERT_REQ(servercertexpreq, cacertexpreq, - "UK", "qemu.org", NULL, NULL, NULL, NULL, - true, true, false, - true, true, - GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, - true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, - 0, 0); - TLS_CERT_REQ(servercertexp1req, cacertreq, - "UK", "qemu", NULL, NULL, NULL, NULL, - true, true, false, - true, true, - GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, - true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, - 0, -1); - TLS_CERT_REQ(clientcertexp1req, cacertreq, - "UK", "qemu", NULL, NULL, NULL, NULL, - true, true, false, - true, true, - GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, - true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL, - 0, -1); - - TLS_TEST_REG(expired1, true, - cacertexpreq.filename, servercertexpreq.filename, true); - TLS_TEST_REG(expired2, true, - cacertreq.filename, servercertexp1req.filename, true); - TLS_TEST_REG(expired3, false, - cacertreq.filename, clientcertexp1req.filename, true); - - - /* Not activated stuff */ - - TLS_ROOT_REQ(cacertnewreq, - "UK", "qemu", NULL, NULL, NULL, NULL, - true, true, true, - true, true, GNUTLS_KEY_KEY_CERT_SIGN, - false, false, NULL, NULL, - 1, 2); - TLS_CERT_REQ(servercertnewreq, cacertnewreq, - "UK", "qemu", NULL, NULL, NULL, NULL, - true, true, false, - true, true, - GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, - true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, - 0, 0); - TLS_CERT_REQ(servercertnew1req, cacertreq, - "UK", "qemu", NULL, NULL, NULL, NULL, - true, true, false, - true, true, - GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, - true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, - 1, 2); - TLS_CERT_REQ(clientcertnew1req, cacertreq, - "UK", "qemu", NULL, NULL, NULL, NULL, - true, true, false, - true, true, - GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, - true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL, - 1, 2); - - TLS_TEST_REG(inactive1, true, - cacertnewreq.filename, servercertnewreq.filename, true); - TLS_TEST_REG(inactive2, true, - cacertreq.filename, servercertnew1req.filename, true); - TLS_TEST_REG(inactive3, false, - cacertreq.filename, clientcertnew1req.filename, true); - - TLS_ROOT_REQ(cacertrootreq, - "UK", "qemu root", NULL, NULL, NULL, NULL, - true, true, true, - true, true, GNUTLS_KEY_KEY_CERT_SIGN, - false, false, NULL, NULL, - 0, 0); - TLS_CERT_REQ(cacertlevel1areq, cacertrootreq, - "UK", "qemu level 1a", NULL, NULL, NULL, NULL, - true, true, true, - true, true, GNUTLS_KEY_KEY_CERT_SIGN, - false, false, NULL, NULL, - 0, 0); - TLS_CERT_REQ(cacertlevel1breq, cacertrootreq, - "UK", "qemu level 1b", NULL, NULL, NULL, NULL, - true, true, true, - true, true, GNUTLS_KEY_KEY_CERT_SIGN, - false, false, NULL, NULL, - 0, 0); - TLS_CERT_REQ(cacertlevel2areq, cacertlevel1areq, - "UK", "qemu level 2a", NULL, NULL, NULL, NULL, - true, true, true, - true, true, GNUTLS_KEY_KEY_CERT_SIGN, - false, false, NULL, NULL, - 0, 0); - TLS_CERT_REQ(servercertlevel3areq, cacertlevel2areq, - "UK", "qemu.org", NULL, NULL, NULL, NULL, - true, true, false, - true, true, - GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, - true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, - 0, 0); - TLS_CERT_REQ(clientcertlevel2breq, cacertlevel1breq, - "UK", "qemu client level 2b", NULL, NULL, NULL, NULL, - true, true, false, - true, true, - GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, - true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL, - 0, 0); - - gnutls_x509_crt_t certchain[] = { - cacertrootreq.crt, - cacertlevel1areq.crt, - cacertlevel1breq.crt, - cacertlevel2areq.crt, - }; - - test_tls_write_cert_chain(WORKDIR "cacertchain-ctx.pem", - certchain, - G_N_ELEMENTS(certchain)); - - TLS_TEST_REG(chain1, true, - WORKDIR "cacertchain-ctx.pem", - servercertlevel3areq.filename, false); - TLS_TEST_REG(chain2, false, - WORKDIR "cacertchain-ctx.pem", - clientcertlevel2breq.filename, false); - - /* Some missing certs - first two are fatal, the last - * is ok - */ - TLS_TEST_REG(missingca, true, - "cacertdoesnotexist.pem", - servercert1req.filename, true); - TLS_TEST_REG(missingserver, true, - cacert1req.filename, - "servercertdoesnotexist.pem", true); - TLS_TEST_REG(missingclient, false, - cacert1req.filename, - "clientcertdoesnotexist.pem", false); - - ret = g_test_run(); - - test_tls_discard_cert(&cacertreq); - test_tls_discard_cert(&cacert1req); - test_tls_discard_cert(&cacert2req); - test_tls_discard_cert(&cacert3req); - test_tls_discard_cert(&cacert4req); - test_tls_discard_cert(&cacert5req); - test_tls_discard_cert(&cacert6req); - - test_tls_discard_cert(&servercertreq); - test_tls_discard_cert(&servercert1req); - test_tls_discard_cert(&servercert2req); - test_tls_discard_cert(&servercert3req); - test_tls_discard_cert(&servercert4req); - test_tls_discard_cert(&servercert5req); - test_tls_discard_cert(&servercert6req); - test_tls_discard_cert(&servercert7req); - test_tls_discard_cert(&servercert8req); - test_tls_discard_cert(&servercert9req); - test_tls_discard_cert(&servercert10req); - test_tls_discard_cert(&servercert11req); - test_tls_discard_cert(&servercert12req); - test_tls_discard_cert(&servercert13req); - test_tls_discard_cert(&servercert14req); - test_tls_discard_cert(&servercert15req); - test_tls_discard_cert(&servercert16req); - - test_tls_discard_cert(&clientcertreq); - test_tls_discard_cert(&clientcert1req); - test_tls_discard_cert(&clientcert2req); - test_tls_discard_cert(&clientcert3req); - test_tls_discard_cert(&clientcert4req); - test_tls_discard_cert(&clientcert5req); - test_tls_discard_cert(&clientcert6req); - test_tls_discard_cert(&clientcert7req); - test_tls_discard_cert(&clientcert8req); - test_tls_discard_cert(&clientcert9req); - test_tls_discard_cert(&clientcert10req); - - test_tls_discard_cert(&cacertexpreq); - test_tls_discard_cert(&servercertexpreq); - test_tls_discard_cert(&servercertexp1req); - test_tls_discard_cert(&clientcertexp1req); - - test_tls_discard_cert(&cacertnewreq); - test_tls_discard_cert(&servercertnewreq); - test_tls_discard_cert(&servercertnew1req); - test_tls_discard_cert(&clientcertnew1req); - - test_tls_discard_cert(&cacertrootreq); - test_tls_discard_cert(&cacertlevel1areq); - test_tls_discard_cert(&cacertlevel1breq); - test_tls_discard_cert(&cacertlevel2areq); - test_tls_discard_cert(&servercertlevel3areq); - test_tls_discard_cert(&clientcertlevel2breq); - unlink(WORKDIR "cacertchain-ctx.pem"); - - test_tls_cleanup(KEYFILE); - rmdir(WORKDIR); - - return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; -} - -#else /* ! QCRYPTO_HAVE_TLS_TEST_SUPPORT */ - -int -main(void) -{ - return EXIT_SUCCESS; -} - -#endif /* ! QCRYPTO_HAVE_TLS_TEST_SUPPORT */ diff --git a/tests/test-crypto-tlssession.c b/tests/test-crypto-tlssession.c deleted file mode 100644 index 8b2453fa79..0000000000 --- a/tests/test-crypto-tlssession.c +++ /dev/null @@ -1,660 +0,0 @@ -/* - * Copyright (C) 2015 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see - * . - * - * Author: Daniel P. Berrange - */ - -#include "qemu/osdep.h" - -#include "crypto-tls-x509-helpers.h" -#include "crypto-tls-psk-helpers.h" -#include "crypto/tlscredsx509.h" -#include "crypto/tlscredspsk.h" -#include "crypto/tlssession.h" -#include "qom/object_interfaces.h" -#include "qapi/error.h" -#include "qemu/module.h" -#include "qemu/sockets.h" -#include "authz/list.h" - -#ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT - -#define WORKDIR "tests/test-crypto-tlssession-work/" -#define PSKFILE WORKDIR "keys.psk" -#define KEYFILE WORKDIR "key-ctx.pem" - -static ssize_t testWrite(const char *buf, size_t len, void *opaque) -{ - int *fd = opaque; - - return write(*fd, buf, len); -} - -static ssize_t testRead(char *buf, size_t len, void *opaque) -{ - int *fd = opaque; - - return read(*fd, buf, len); -} - -static QCryptoTLSCreds *test_tls_creds_psk_create( - QCryptoTLSCredsEndpoint endpoint, - const char *dir) -{ - Object *parent = object_get_objects_root(); - Object *creds = object_new_with_props( - TYPE_QCRYPTO_TLS_CREDS_PSK, - parent, - (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ? - "testtlscredsserver" : "testtlscredsclient"), - &error_abort, - "endpoint", (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ? - "server" : "client"), - "dir", dir, - "priority", "NORMAL", - NULL - ); - return QCRYPTO_TLS_CREDS(creds); -} - - -static void test_crypto_tls_session_psk(void) -{ - QCryptoTLSCreds *clientCreds; - QCryptoTLSCreds *serverCreds; - QCryptoTLSSession *clientSess = NULL; - QCryptoTLSSession *serverSess = NULL; - int channel[2]; - bool clientShake = false; - bool serverShake = false; - int ret; - - /* We'll use this for our fake client-server connection */ - ret = socketpair(AF_UNIX, SOCK_STREAM, 0, channel); - g_assert(ret == 0); - - /* - * We have an evil loop to do the handshake in a single - * thread, so we need these non-blocking to avoid deadlock - * of ourselves - */ - qemu_set_nonblock(channel[0]); - qemu_set_nonblock(channel[1]); - - clientCreds = test_tls_creds_psk_create( - QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, - WORKDIR); - g_assert(clientCreds != NULL); - - serverCreds = test_tls_creds_psk_create( - QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, - WORKDIR); - g_assert(serverCreds != NULL); - - /* Now the real part of the test, setup the sessions */ - clientSess = qcrypto_tls_session_new( - clientCreds, NULL, NULL, - QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, &error_abort); - g_assert(clientSess != NULL); - - serverSess = qcrypto_tls_session_new( - serverCreds, NULL, NULL, - QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, &error_abort); - g_assert(serverSess != NULL); - - /* For handshake to work, we need to set the I/O callbacks - * to read/write over the socketpair - */ - qcrypto_tls_session_set_callbacks(serverSess, - testWrite, testRead, - &channel[0]); - qcrypto_tls_session_set_callbacks(clientSess, - testWrite, testRead, - &channel[1]); - - /* - * Finally we loop around & around doing handshake on each - * session until we get an error, or the handshake completes. - * This relies on the socketpair being nonblocking to avoid - * deadlocking ourselves upon handshake - */ - do { - int rv; - if (!serverShake) { - rv = qcrypto_tls_session_handshake(serverSess, - &error_abort); - g_assert(rv >= 0); - if (qcrypto_tls_session_get_handshake_status(serverSess) == - QCRYPTO_TLS_HANDSHAKE_COMPLETE) { - serverShake = true; - } - } - if (!clientShake) { - rv = qcrypto_tls_session_handshake(clientSess, - &error_abort); - g_assert(rv >= 0); - if (qcrypto_tls_session_get_handshake_status(clientSess) == - QCRYPTO_TLS_HANDSHAKE_COMPLETE) { - clientShake = true; - } - } - } while (!clientShake || !serverShake); - - - /* Finally make sure the server & client validation is successful. */ - g_assert(qcrypto_tls_session_check_credentials(serverSess, - &error_abort) == 0); - g_assert(qcrypto_tls_session_check_credentials(clientSess, - &error_abort) == 0); - - object_unparent(OBJECT(serverCreds)); - object_unparent(OBJECT(clientCreds)); - - qcrypto_tls_session_free(serverSess); - qcrypto_tls_session_free(clientSess); - - close(channel[0]); - close(channel[1]); -} - - -struct QCryptoTLSSessionTestData { - const char *servercacrt; - const char *clientcacrt; - const char *servercrt; - const char *clientcrt; - bool expectServerFail; - bool expectClientFail; - const char *hostname; - const char *const *wildcards; -}; - -static QCryptoTLSCreds *test_tls_creds_x509_create( - QCryptoTLSCredsEndpoint endpoint, - const char *certdir) -{ - Object *parent = object_get_objects_root(); - Object *creds = object_new_with_props( - TYPE_QCRYPTO_TLS_CREDS_X509, - parent, - (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ? - "testtlscredsserver" : "testtlscredsclient"), - &error_abort, - "endpoint", (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ? - "server" : "client"), - "dir", certdir, - "verify-peer", "yes", - "priority", "NORMAL", - /* We skip initial sanity checks here because we - * want to make sure that problems are being - * detected at the TLS session validation stage, - * and the test-crypto-tlscreds test already - * validate the sanity check code. - */ - "sanity-check", "no", - NULL - ); - return QCRYPTO_TLS_CREDS(creds); -} - - -/* - * This tests validation checking of peer certificates - * - * This is replicating the checks that are done for an - * active TLS session after handshake completes. To - * simulate that we create our TLS contexts, skipping - * sanity checks. We then get a socketpair, and - * initiate a TLS session across them. Finally do - * do actual cert validation tests - */ -static void test_crypto_tls_session_x509(const void *opaque) -{ - struct QCryptoTLSSessionTestData *data = - (struct QCryptoTLSSessionTestData *)opaque; - QCryptoTLSCreds *clientCreds; - QCryptoTLSCreds *serverCreds; - QCryptoTLSSession *clientSess = NULL; - QCryptoTLSSession *serverSess = NULL; - QAuthZList *auth; - const char * const *wildcards; - int channel[2]; - bool clientShake = false; - bool serverShake = false; - int ret; - - /* We'll use this for our fake client-server connection */ - ret = socketpair(AF_UNIX, SOCK_STREAM, 0, channel); - g_assert(ret == 0); - - /* - * We have an evil loop to do the handshake in a single - * thread, so we need these non-blocking to avoid deadlock - * of ourselves - */ - qemu_set_nonblock(channel[0]); - qemu_set_nonblock(channel[1]); - -#define CLIENT_CERT_DIR "tests/test-crypto-tlssession-client/" -#define SERVER_CERT_DIR "tests/test-crypto-tlssession-server/" - mkdir(CLIENT_CERT_DIR, 0700); - mkdir(SERVER_CERT_DIR, 0700); - - unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT); - unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT); - unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY); - - unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT); - unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT); - unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY); - - g_assert(link(data->servercacrt, - SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT) == 0); - g_assert(link(data->servercrt, - SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT) == 0); - g_assert(link(KEYFILE, - SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY) == 0); - - g_assert(link(data->clientcacrt, - CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT) == 0); - g_assert(link(data->clientcrt, - CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT) == 0); - g_assert(link(KEYFILE, - CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY) == 0); - - clientCreds = test_tls_creds_x509_create( - QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, - CLIENT_CERT_DIR); - g_assert(clientCreds != NULL); - - serverCreds = test_tls_creds_x509_create( - QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, - SERVER_CERT_DIR); - g_assert(serverCreds != NULL); - - auth = qauthz_list_new("tlssessionacl", - QAUTHZ_LIST_POLICY_DENY, - &error_abort); - wildcards = data->wildcards; - while (wildcards && *wildcards) { - qauthz_list_append_rule(auth, *wildcards, - QAUTHZ_LIST_POLICY_ALLOW, - QAUTHZ_LIST_FORMAT_GLOB, - &error_abort); - wildcards++; - } - - /* Now the real part of the test, setup the sessions */ - clientSess = qcrypto_tls_session_new( - clientCreds, data->hostname, NULL, - QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, &error_abort); - g_assert(clientSess != NULL); - - serverSess = qcrypto_tls_session_new( - serverCreds, NULL, - data->wildcards ? "tlssessionacl" : NULL, - QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, &error_abort); - g_assert(serverSess != NULL); - - /* For handshake to work, we need to set the I/O callbacks - * to read/write over the socketpair - */ - qcrypto_tls_session_set_callbacks(serverSess, - testWrite, testRead, - &channel[0]); - qcrypto_tls_session_set_callbacks(clientSess, - testWrite, testRead, - &channel[1]); - - /* - * Finally we loop around & around doing handshake on each - * session until we get an error, or the handshake completes. - * This relies on the socketpair being nonblocking to avoid - * deadlocking ourselves upon handshake - */ - do { - int rv; - if (!serverShake) { - rv = qcrypto_tls_session_handshake(serverSess, - &error_abort); - g_assert(rv >= 0); - if (qcrypto_tls_session_get_handshake_status(serverSess) == - QCRYPTO_TLS_HANDSHAKE_COMPLETE) { - serverShake = true; - } - } - if (!clientShake) { - rv = qcrypto_tls_session_handshake(clientSess, - &error_abort); - g_assert(rv >= 0); - if (qcrypto_tls_session_get_handshake_status(clientSess) == - QCRYPTO_TLS_HANDSHAKE_COMPLETE) { - clientShake = true; - } - } - } while (!clientShake || !serverShake); - - - /* Finally make sure the server validation does what - * we were expecting - */ - if (qcrypto_tls_session_check_credentials( - serverSess, data->expectServerFail ? NULL : &error_abort) < 0) { - g_assert(data->expectServerFail); - } else { - g_assert(!data->expectServerFail); - } - - /* - * And the same for the client validation check - */ - if (qcrypto_tls_session_check_credentials( - clientSess, data->expectClientFail ? NULL : &error_abort) < 0) { - g_assert(data->expectClientFail); - } else { - g_assert(!data->expectClientFail); - } - - unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT); - unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT); - unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY); - - unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT); - unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT); - unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY); - - rmdir(CLIENT_CERT_DIR); - rmdir(SERVER_CERT_DIR); - - object_unparent(OBJECT(serverCreds)); - object_unparent(OBJECT(clientCreds)); - object_unparent(OBJECT(auth)); - - qcrypto_tls_session_free(serverSess); - qcrypto_tls_session_free(clientSess); - - close(channel[0]); - close(channel[1]); -} - - -int main(int argc, char **argv) -{ - int ret; - - module_call_init(MODULE_INIT_QOM); - g_test_init(&argc, &argv, NULL); - g_setenv("GNUTLS_FORCE_FIPS_MODE", "2", 1); - - mkdir(WORKDIR, 0700); - - test_tls_init(KEYFILE); - test_tls_psk_init(PSKFILE); - - /* Simple initial test using Pre-Shared Keys. */ - g_test_add_func("/qcrypto/tlssession/psk", - test_crypto_tls_session_psk); - - /* More complex tests using X.509 certificates. */ -# define TEST_SESS_REG(name, caCrt, \ - serverCrt, clientCrt, \ - expectServerFail, expectClientFail, \ - hostname, wildcards) \ - struct QCryptoTLSSessionTestData name = { \ - caCrt, caCrt, serverCrt, clientCrt, \ - expectServerFail, expectClientFail, \ - hostname, wildcards \ - }; \ - g_test_add_data_func("/qcrypto/tlssession/" # name, \ - &name, test_crypto_tls_session_x509); \ - - -# define TEST_SESS_REG_EXT(name, serverCaCrt, clientCaCrt, \ - serverCrt, clientCrt, \ - expectServerFail, expectClientFail, \ - hostname, wildcards) \ - struct QCryptoTLSSessionTestData name = { \ - serverCaCrt, clientCaCrt, serverCrt, clientCrt, \ - expectServerFail, expectClientFail, \ - hostname, wildcards \ - }; \ - g_test_add_data_func("/qcrypto/tlssession/" # name, \ - &name, test_crypto_tls_session_x509); \ - - /* A perfect CA, perfect client & perfect server */ - - /* Basic:CA:critical */ - TLS_ROOT_REQ(cacertreq, - "UK", "qemu CA", NULL, NULL, NULL, NULL, - true, true, true, - true, true, GNUTLS_KEY_KEY_CERT_SIGN, - false, false, NULL, NULL, - 0, 0); - - TLS_ROOT_REQ(altcacertreq, - "UK", "qemu CA 1", NULL, NULL, NULL, NULL, - true, true, true, - false, false, 0, - false, false, NULL, NULL, - 0, 0); - - TLS_CERT_REQ(servercertreq, cacertreq, - "UK", "qemu.org", NULL, NULL, NULL, NULL, - true, true, false, - true, true, - GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, - true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, - 0, 0); - TLS_CERT_REQ(clientcertreq, cacertreq, - "UK", "qemu", NULL, NULL, NULL, NULL, - true, true, false, - true, true, - GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, - true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL, - 0, 0); - - TLS_CERT_REQ(clientcertaltreq, altcacertreq, - "UK", "qemu", NULL, NULL, NULL, NULL, - true, true, false, - true, true, - GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, - true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL, - 0, 0); - - TEST_SESS_REG(basicca, cacertreq.filename, - servercertreq.filename, clientcertreq.filename, - false, false, "qemu.org", NULL); - TEST_SESS_REG_EXT(differentca, cacertreq.filename, - altcacertreq.filename, servercertreq.filename, - clientcertaltreq.filename, true, true, "qemu.org", NULL); - - - /* When an altname is set, the CN is ignored, so it must be duplicated - * as an altname for it to match */ - TLS_CERT_REQ(servercertalt1req, cacertreq, - "UK", "qemu.org", "www.qemu.org", "qemu.org", - "192.168.122.1", "fec0::dead:beaf", - true, true, false, - true, true, - GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, - true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, - 0, 0); - /* This intentionally doesn't replicate */ - TLS_CERT_REQ(servercertalt2req, cacertreq, - "UK", "qemu.org", "www.qemu.org", "wiki.qemu.org", - "192.168.122.1", "fec0::dead:beaf", - true, true, false, - true, true, - GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, - true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, - 0, 0); - - TEST_SESS_REG(altname1, cacertreq.filename, - servercertalt1req.filename, clientcertreq.filename, - false, false, "qemu.org", NULL); - TEST_SESS_REG(altname2, cacertreq.filename, - servercertalt1req.filename, clientcertreq.filename, - false, false, "www.qemu.org", NULL); - TEST_SESS_REG(altname3, cacertreq.filename, - servercertalt1req.filename, clientcertreq.filename, - false, true, "wiki.qemu.org", NULL); - - TEST_SESS_REG(altname4, cacertreq.filename, - servercertalt2req.filename, clientcertreq.filename, - false, true, "qemu.org", NULL); - TEST_SESS_REG(altname5, cacertreq.filename, - servercertalt2req.filename, clientcertreq.filename, - false, false, "www.qemu.org", NULL); - TEST_SESS_REG(altname6, cacertreq.filename, - servercertalt2req.filename, clientcertreq.filename, - false, false, "wiki.qemu.org", NULL); - - const char *const wildcards1[] = { - "C=UK,CN=dogfood", - NULL, - }; - const char *const wildcards2[] = { - "C=UK,CN=qemu", - NULL, - }; - const char *const wildcards3[] = { - "C=UK,CN=dogfood", - "C=UK,CN=qemu", - NULL, - }; - const char *const wildcards4[] = { - "C=UK,CN=qemustuff", - NULL, - }; - const char *const wildcards5[] = { - "C=UK,CN=qemu*", - NULL, - }; - const char *const wildcards6[] = { - "C=UK,CN=*emu*", - NULL, - }; - - TEST_SESS_REG(wildcard1, cacertreq.filename, - servercertreq.filename, clientcertreq.filename, - true, false, "qemu.org", wildcards1); - TEST_SESS_REG(wildcard2, cacertreq.filename, - servercertreq.filename, clientcertreq.filename, - false, false, "qemu.org", wildcards2); - TEST_SESS_REG(wildcard3, cacertreq.filename, - servercertreq.filename, clientcertreq.filename, - false, false, "qemu.org", wildcards3); - TEST_SESS_REG(wildcard4, cacertreq.filename, - servercertreq.filename, clientcertreq.filename, - true, false, "qemu.org", wildcards4); - TEST_SESS_REG(wildcard5, cacertreq.filename, - servercertreq.filename, clientcertreq.filename, - false, false, "qemu.org", wildcards5); - TEST_SESS_REG(wildcard6, cacertreq.filename, - servercertreq.filename, clientcertreq.filename, - false, false, "qemu.org", wildcards6); - - TLS_ROOT_REQ(cacertrootreq, - "UK", "qemu root", NULL, NULL, NULL, NULL, - true, true, true, - true, true, GNUTLS_KEY_KEY_CERT_SIGN, - false, false, NULL, NULL, - 0, 0); - TLS_CERT_REQ(cacertlevel1areq, cacertrootreq, - "UK", "qemu level 1a", NULL, NULL, NULL, NULL, - true, true, true, - true, true, GNUTLS_KEY_KEY_CERT_SIGN, - false, false, NULL, NULL, - 0, 0); - TLS_CERT_REQ(cacertlevel1breq, cacertrootreq, - "UK", "qemu level 1b", NULL, NULL, NULL, NULL, - true, true, true, - true, true, GNUTLS_KEY_KEY_CERT_SIGN, - false, false, NULL, NULL, - 0, 0); - TLS_CERT_REQ(cacertlevel2areq, cacertlevel1areq, - "UK", "qemu level 2a", NULL, NULL, NULL, NULL, - true, true, true, - true, true, GNUTLS_KEY_KEY_CERT_SIGN, - false, false, NULL, NULL, - 0, 0); - TLS_CERT_REQ(servercertlevel3areq, cacertlevel2areq, - "UK", "qemu.org", NULL, NULL, NULL, NULL, - true, true, false, - true, true, - GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, - true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, - 0, 0); - TLS_CERT_REQ(clientcertlevel2breq, cacertlevel1breq, - "UK", "qemu client level 2b", NULL, NULL, NULL, NULL, - true, true, false, - true, true, - GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, - true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL, - 0, 0); - - gnutls_x509_crt_t certchain[] = { - cacertrootreq.crt, - cacertlevel1areq.crt, - cacertlevel1breq.crt, - cacertlevel2areq.crt, - }; - - test_tls_write_cert_chain(WORKDIR "cacertchain-sess.pem", - certchain, - G_N_ELEMENTS(certchain)); - - TEST_SESS_REG(cachain, WORKDIR "cacertchain-sess.pem", - servercertlevel3areq.filename, clientcertlevel2breq.filename, - false, false, "qemu.org", NULL); - - ret = g_test_run(); - - test_tls_discard_cert(&clientcertreq); - test_tls_discard_cert(&clientcertaltreq); - - test_tls_discard_cert(&servercertreq); - test_tls_discard_cert(&servercertalt1req); - test_tls_discard_cert(&servercertalt2req); - - test_tls_discard_cert(&cacertreq); - test_tls_discard_cert(&altcacertreq); - - test_tls_discard_cert(&cacertrootreq); - test_tls_discard_cert(&cacertlevel1areq); - test_tls_discard_cert(&cacertlevel1breq); - test_tls_discard_cert(&cacertlevel2areq); - test_tls_discard_cert(&servercertlevel3areq); - test_tls_discard_cert(&clientcertlevel2breq); - unlink(WORKDIR "cacertchain-sess.pem"); - - test_tls_psk_cleanup(PSKFILE); - test_tls_cleanup(KEYFILE); - rmdir(WORKDIR); - - return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; -} - -#else /* ! QCRYPTO_HAVE_TLS_TEST_SUPPORT */ - -int -main(void) -{ - return EXIT_SUCCESS; -} - -#endif /* ! QCRYPTO_HAVE_TLS_TEST_SUPPORT */ diff --git a/tests/test-crypto-xts.c b/tests/test-crypto-xts.c deleted file mode 100644 index 7acbc956fd..0000000000 --- a/tests/test-crypto-xts.c +++ /dev/null @@ -1,529 +0,0 @@ -/* - * QEMU Crypto XTS cipher mode - * - * Copyright (c) 2015-2018 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - * - * This code is originally derived from public domain / WTFPL code in - * LibTomCrypt crytographic library http://libtom.org. The XTS code - * was donated by Elliptic Semiconductor Inc (www.ellipticsemi.com) - * to the LibTom Projects - * - */ - -#include "qemu/osdep.h" -#include "crypto/init.h" -#include "crypto/xts.h" -#include "crypto/aes.h" - -typedef struct { - const char *path; - int keylen; - unsigned char key1[32]; - unsigned char key2[32]; - uint64_t seqnum; - unsigned long PTLEN; - unsigned char PTX[512], CTX[512]; -} QCryptoXTSTestData; - -static const QCryptoXTSTestData test_data[] = { - /* #1 32 byte key, 32 byte PTX */ - { - "/crypto/xts/t-1-key-32-ptx-32", - 32, - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - 0, - 32, - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - { 0x91, 0x7c, 0xf6, 0x9e, 0xbd, 0x68, 0xb2, 0xec, - 0x9b, 0x9f, 0xe9, 0xa3, 0xea, 0xdd, 0xa6, 0x92, - 0xcd, 0x43, 0xd2, 0xf5, 0x95, 0x98, 0xed, 0x85, - 0x8c, 0x02, 0xc2, 0x65, 0x2f, 0xbf, 0x92, 0x2e }, - }, - - /* #2, 32 byte key, 32 byte PTX */ - { - "/crypto/xts/t-2-key-32-ptx-32", - 32, - { 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, - 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11 }, - { 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, - 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22 }, - 0x3333333333LL, - 32, - { 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, - 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, - 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, - 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44 }, - { 0xc4, 0x54, 0x18, 0x5e, 0x6a, 0x16, 0x93, 0x6e, - 0x39, 0x33, 0x40, 0x38, 0xac, 0xef, 0x83, 0x8b, - 0xfb, 0x18, 0x6f, 0xff, 0x74, 0x80, 0xad, 0xc4, - 0x28, 0x93, 0x82, 0xec, 0xd6, 0xd3, 0x94, 0xf0 }, - }, - - /* #5 from xts.7, 32 byte key, 32 byte PTX */ - { - "/crypto/xts/t-5-key-32-ptx-32", - 32, - { 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, - 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0 }, - { 0xbf, 0xbe, 0xbd, 0xbc, 0xbb, 0xba, 0xb9, 0xb8, - 0xb7, 0xb6, 0xb5, 0xb4, 0xb3, 0xb2, 0xb1, 0xb0 }, - 0x123456789aLL, - 32, - { 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, - 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, - 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, - 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44 }, - { 0xb0, 0x1f, 0x86, 0xf8, 0xed, 0xc1, 0x86, 0x37, - 0x06, 0xfa, 0x8a, 0x42, 0x53, 0xe3, 0x4f, 0x28, - 0xaf, 0x31, 0x9d, 0xe3, 0x83, 0x34, 0x87, 0x0f, - 0x4d, 0xd1, 0xf9, 0x4c, 0xbe, 0x98, 0x32, 0xf1 }, - }, - - /* #4, 32 byte key, 512 byte PTX */ - { - "/crypto/xts/t-4-key-32-ptx-512", - 32, - { 0x27, 0x18, 0x28, 0x18, 0x28, 0x45, 0x90, 0x45, - 0x23, 0x53, 0x60, 0x28, 0x74, 0x71, 0x35, 0x26 }, - { 0x31, 0x41, 0x59, 0x26, 0x53, 0x58, 0x97, 0x93, - 0x23, 0x84, 0x62, 0x64, 0x33, 0x83, 0x27, 0x95 }, - 0, - 512, - { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, - 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, - 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, - 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, - 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, - 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, - 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, - 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, - 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, - 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, - 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, - 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, - 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, - 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, - 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, - 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, - 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, - 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, - 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, - 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, - 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, - 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, - 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, - 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, - 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, - 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, - 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, - 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, - 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, - 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, - 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, - 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, - 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, - 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, - 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, - 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, - 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, - 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, - 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, - 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, - 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, - 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, - 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, - 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, - 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, - 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, - 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, - 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, - 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, - 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, - 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, - 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, - 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, - 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, - 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, - 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, - 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, - 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, - 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, - }, - { - 0x27, 0xa7, 0x47, 0x9b, 0xef, 0xa1, 0xd4, 0x76, - 0x48, 0x9f, 0x30, 0x8c, 0xd4, 0xcf, 0xa6, 0xe2, - 0xa9, 0x6e, 0x4b, 0xbe, 0x32, 0x08, 0xff, 0x25, - 0x28, 0x7d, 0xd3, 0x81, 0x96, 0x16, 0xe8, 0x9c, - 0xc7, 0x8c, 0xf7, 0xf5, 0xe5, 0x43, 0x44, 0x5f, - 0x83, 0x33, 0xd8, 0xfa, 0x7f, 0x56, 0x00, 0x00, - 0x05, 0x27, 0x9f, 0xa5, 0xd8, 0xb5, 0xe4, 0xad, - 0x40, 0xe7, 0x36, 0xdd, 0xb4, 0xd3, 0x54, 0x12, - 0x32, 0x80, 0x63, 0xfd, 0x2a, 0xab, 0x53, 0xe5, - 0xea, 0x1e, 0x0a, 0x9f, 0x33, 0x25, 0x00, 0xa5, - 0xdf, 0x94, 0x87, 0xd0, 0x7a, 0x5c, 0x92, 0xcc, - 0x51, 0x2c, 0x88, 0x66, 0xc7, 0xe8, 0x60, 0xce, - 0x93, 0xfd, 0xf1, 0x66, 0xa2, 0x49, 0x12, 0xb4, - 0x22, 0x97, 0x61, 0x46, 0xae, 0x20, 0xce, 0x84, - 0x6b, 0xb7, 0xdc, 0x9b, 0xa9, 0x4a, 0x76, 0x7a, - 0xae, 0xf2, 0x0c, 0x0d, 0x61, 0xad, 0x02, 0x65, - 0x5e, 0xa9, 0x2d, 0xc4, 0xc4, 0xe4, 0x1a, 0x89, - 0x52, 0xc6, 0x51, 0xd3, 0x31, 0x74, 0xbe, 0x51, - 0xa1, 0x0c, 0x42, 0x11, 0x10, 0xe6, 0xd8, 0x15, - 0x88, 0xed, 0xe8, 0x21, 0x03, 0xa2, 0x52, 0xd8, - 0xa7, 0x50, 0xe8, 0x76, 0x8d, 0xef, 0xff, 0xed, - 0x91, 0x22, 0x81, 0x0a, 0xae, 0xb9, 0x9f, 0x91, - 0x72, 0xaf, 0x82, 0xb6, 0x04, 0xdc, 0x4b, 0x8e, - 0x51, 0xbc, 0xb0, 0x82, 0x35, 0xa6, 0xf4, 0x34, - 0x13, 0x32, 0xe4, 0xca, 0x60, 0x48, 0x2a, 0x4b, - 0xa1, 0xa0, 0x3b, 0x3e, 0x65, 0x00, 0x8f, 0xc5, - 0xda, 0x76, 0xb7, 0x0b, 0xf1, 0x69, 0x0d, 0xb4, - 0xea, 0xe2, 0x9c, 0x5f, 0x1b, 0xad, 0xd0, 0x3c, - 0x5c, 0xcf, 0x2a, 0x55, 0xd7, 0x05, 0xdd, 0xcd, - 0x86, 0xd4, 0x49, 0x51, 0x1c, 0xeb, 0x7e, 0xc3, - 0x0b, 0xf1, 0x2b, 0x1f, 0xa3, 0x5b, 0x91, 0x3f, - 0x9f, 0x74, 0x7a, 0x8a, 0xfd, 0x1b, 0x13, 0x0e, - 0x94, 0xbf, 0xf9, 0x4e, 0xff, 0xd0, 0x1a, 0x91, - 0x73, 0x5c, 0xa1, 0x72, 0x6a, 0xcd, 0x0b, 0x19, - 0x7c, 0x4e, 0x5b, 0x03, 0x39, 0x36, 0x97, 0xe1, - 0x26, 0x82, 0x6f, 0xb6, 0xbb, 0xde, 0x8e, 0xcc, - 0x1e, 0x08, 0x29, 0x85, 0x16, 0xe2, 0xc9, 0xed, - 0x03, 0xff, 0x3c, 0x1b, 0x78, 0x60, 0xf6, 0xde, - 0x76, 0xd4, 0xce, 0xcd, 0x94, 0xc8, 0x11, 0x98, - 0x55, 0xef, 0x52, 0x97, 0xca, 0x67, 0xe9, 0xf3, - 0xe7, 0xff, 0x72, 0xb1, 0xe9, 0x97, 0x85, 0xca, - 0x0a, 0x7e, 0x77, 0x20, 0xc5, 0xb3, 0x6d, 0xc6, - 0xd7, 0x2c, 0xac, 0x95, 0x74, 0xc8, 0xcb, 0xbc, - 0x2f, 0x80, 0x1e, 0x23, 0xe5, 0x6f, 0xd3, 0x44, - 0xb0, 0x7f, 0x22, 0x15, 0x4b, 0xeb, 0xa0, 0xf0, - 0x8c, 0xe8, 0x89, 0x1e, 0x64, 0x3e, 0xd9, 0x95, - 0xc9, 0x4d, 0x9a, 0x69, 0xc9, 0xf1, 0xb5, 0xf4, - 0x99, 0x02, 0x7a, 0x78, 0x57, 0x2a, 0xee, 0xbd, - 0x74, 0xd2, 0x0c, 0xc3, 0x98, 0x81, 0xc2, 0x13, - 0xee, 0x77, 0x0b, 0x10, 0x10, 0xe4, 0xbe, 0xa7, - 0x18, 0x84, 0x69, 0x77, 0xae, 0x11, 0x9f, 0x7a, - 0x02, 0x3a, 0xb5, 0x8c, 0xca, 0x0a, 0xd7, 0x52, - 0xaf, 0xe6, 0x56, 0xbb, 0x3c, 0x17, 0x25, 0x6a, - 0x9f, 0x6e, 0x9b, 0xf1, 0x9f, 0xdd, 0x5a, 0x38, - 0xfc, 0x82, 0xbb, 0xe8, 0x72, 0xc5, 0x53, 0x9e, - 0xdb, 0x60, 0x9e, 0xf4, 0xf7, 0x9c, 0x20, 0x3e, - 0xbb, 0x14, 0x0f, 0x2e, 0x58, 0x3c, 0xb2, 0xad, - 0x15, 0xb4, 0xaa, 0x5b, 0x65, 0x50, 0x16, 0xa8, - 0x44, 0x92, 0x77, 0xdb, 0xd4, 0x77, 0xef, 0x2c, - 0x8d, 0x6c, 0x01, 0x7d, 0xb7, 0x38, 0xb1, 0x8d, - 0xeb, 0x4a, 0x42, 0x7d, 0x19, 0x23, 0xce, 0x3f, - 0xf2, 0x62, 0x73, 0x57, 0x79, 0xa4, 0x18, 0xf2, - 0x0a, 0x28, 0x2d, 0xf9, 0x20, 0x14, 0x7b, 0xea, - 0xbe, 0x42, 0x1e, 0xe5, 0x31, 0x9d, 0x05, 0x68, - } - }, - - /* #7, 32 byte key, 17 byte PTX */ - { - "/crypto/xts/t-7-key-32-ptx-17", - 32, - { 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, - 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0 }, - { 0xbf, 0xbe, 0xbd, 0xbc, 0xbb, 0xba, 0xb9, 0xb8, - 0xb7, 0xb6, 0xb5, 0xb4, 0xb3, 0xb2, 0xb1, 0xb0 }, - 0x123456789aLL, - 17, - { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 }, - { 0x6c, 0x16, 0x25, 0xdb, 0x46, 0x71, 0x52, 0x2d, - 0x3d, 0x75, 0x99, 0x60, 0x1d, 0xe7, 0xca, 0x09, 0xed }, - }, - - /* #15, 32 byte key, 25 byte PTX */ - { - "/crypto/xts/t-15-key-32-ptx-25", - 32, - { 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, - 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0 }, - { 0xbf, 0xbe, 0xbd, 0xbc, 0xbb, 0xba, 0xb9, 0xb8, - 0xb7, 0xb6, 0xb5, 0xb4, 0xb3, 0xb2, 0xb1, 0xb0 }, - 0x123456789aLL, - 25, - { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18 }, - { 0x8f, 0x4d, 0xcb, 0xad, 0x55, 0x55, 0x8d, 0x7b, - 0x4e, 0x01, 0xd9, 0x37, 0x9c, 0xd4, 0xea, 0x22, - 0xed, 0xbf, 0x9d, 0xac, 0xe4, 0x5d, 0x6f, 0x6a, 0x73 }, - }, - - /* #21, 32 byte key, 31 byte PTX */ - { - "/crypto/xts/t-21-key-32-ptx-31", - 32, - { 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, - 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0 }, - { 0xbf, 0xbe, 0xbd, 0xbc, 0xbb, 0xba, 0xb9, 0xb8, - 0xb7, 0xb6, 0xb5, 0xb4, 0xb3, 0xb2, 0xb1, 0xb0 }, - 0x123456789aLL, - 31, - { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, - 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e }, - { 0xd0, 0x5b, 0xc0, 0x90, 0xa8, 0xe0, 0x4f, 0x1b, - 0x3d, 0x3e, 0xcd, 0xd5, 0xba, 0xec, 0x0f, 0xd4, - 0xed, 0xbf, 0x9d, 0xac, 0xe4, 0x5d, 0x6f, 0x6a, - 0x73, 0x06, 0xe6, 0x4b, 0xe5, 0xdd, 0x82 }, - }, -}; - -#define STORE64L(x, y) \ - do { \ - (y)[7] = (unsigned char)(((x) >> 56) & 255); \ - (y)[6] = (unsigned char)(((x) >> 48) & 255); \ - (y)[5] = (unsigned char)(((x) >> 40) & 255); \ - (y)[4] = (unsigned char)(((x) >> 32) & 255); \ - (y)[3] = (unsigned char)(((x) >> 24) & 255); \ - (y)[2] = (unsigned char)(((x) >> 16) & 255); \ - (y)[1] = (unsigned char)(((x) >> 8) & 255); \ - (y)[0] = (unsigned char)((x) & 255); \ - } while (0) - -struct TestAES { - AES_KEY enc; - AES_KEY dec; -}; - -static void test_xts_aes_encrypt(const void *ctx, - size_t length, - uint8_t *dst, - const uint8_t *src) -{ - const struct TestAES *aesctx = ctx; - - AES_encrypt(src, dst, &aesctx->enc); -} - - -static void test_xts_aes_decrypt(const void *ctx, - size_t length, - uint8_t *dst, - const uint8_t *src) -{ - const struct TestAES *aesctx = ctx; - - AES_decrypt(src, dst, &aesctx->dec); -} - - -static void test_xts(const void *opaque) -{ - const QCryptoXTSTestData *data = opaque; - uint8_t out[512], Torg[16], T[16]; - uint64_t seq; - struct TestAES aesdata; - struct TestAES aestweak; - - AES_set_encrypt_key(data->key1, data->keylen / 2 * 8, &aesdata.enc); - AES_set_decrypt_key(data->key1, data->keylen / 2 * 8, &aesdata.dec); - AES_set_encrypt_key(data->key2, data->keylen / 2 * 8, &aestweak.enc); - AES_set_decrypt_key(data->key2, data->keylen / 2 * 8, &aestweak.dec); - - seq = data->seqnum; - STORE64L(seq, Torg); - memset(Torg + 8, 0, 8); - - memcpy(T, Torg, sizeof(T)); - xts_encrypt(&aesdata, &aestweak, - test_xts_aes_encrypt, - test_xts_aes_decrypt, - T, data->PTLEN, out, data->PTX); - - g_assert(memcmp(out, data->CTX, data->PTLEN) == 0); - - memcpy(T, Torg, sizeof(T)); - xts_decrypt(&aesdata, &aestweak, - test_xts_aes_encrypt, - test_xts_aes_decrypt, - T, data->PTLEN, out, data->CTX); - - g_assert(memcmp(out, data->PTX, data->PTLEN) == 0); -} - - -static void test_xts_split(const void *opaque) -{ - const QCryptoXTSTestData *data = opaque; - uint8_t out[512], Torg[16], T[16]; - uint64_t seq; - unsigned long len = data->PTLEN / 2; - struct TestAES aesdata; - struct TestAES aestweak; - - AES_set_encrypt_key(data->key1, data->keylen / 2 * 8, &aesdata.enc); - AES_set_decrypt_key(data->key1, data->keylen / 2 * 8, &aesdata.dec); - AES_set_encrypt_key(data->key2, data->keylen / 2 * 8, &aestweak.enc); - AES_set_decrypt_key(data->key2, data->keylen / 2 * 8, &aestweak.dec); - - seq = data->seqnum; - STORE64L(seq, Torg); - memset(Torg + 8, 0, 8); - - memcpy(T, Torg, sizeof(T)); - xts_encrypt(&aesdata, &aestweak, - test_xts_aes_encrypt, - test_xts_aes_decrypt, - T, len, out, data->PTX); - xts_encrypt(&aesdata, &aestweak, - test_xts_aes_encrypt, - test_xts_aes_decrypt, - T, len, &out[len], &data->PTX[len]); - - g_assert(memcmp(out, data->CTX, data->PTLEN) == 0); - - memcpy(T, Torg, sizeof(T)); - xts_decrypt(&aesdata, &aestweak, - test_xts_aes_encrypt, - test_xts_aes_decrypt, - T, len, out, data->CTX); - xts_decrypt(&aesdata, &aestweak, - test_xts_aes_encrypt, - test_xts_aes_decrypt, - T, len, &out[len], &data->CTX[len]); - - g_assert(memcmp(out, data->PTX, data->PTLEN) == 0); -} - - -static void test_xts_unaligned(const void *opaque) -{ -#define BAD_ALIGN 3 - const QCryptoXTSTestData *data = opaque; - uint8_t in[512 + BAD_ALIGN], out[512 + BAD_ALIGN]; - uint8_t Torg[16], T[16 + BAD_ALIGN]; - uint64_t seq; - struct TestAES aesdata; - struct TestAES aestweak; - - AES_set_encrypt_key(data->key1, data->keylen / 2 * 8, &aesdata.enc); - AES_set_decrypt_key(data->key1, data->keylen / 2 * 8, &aesdata.dec); - AES_set_encrypt_key(data->key2, data->keylen / 2 * 8, &aestweak.enc); - AES_set_decrypt_key(data->key2, data->keylen / 2 * 8, &aestweak.dec); - - seq = data->seqnum; - STORE64L(seq, Torg); - memset(Torg + 8, 0, 8); - - /* IV not aligned */ - memcpy(T + BAD_ALIGN, Torg, 16); - memcpy(in, data->PTX, data->PTLEN); - xts_encrypt(&aesdata, &aestweak, - test_xts_aes_encrypt, - test_xts_aes_decrypt, - T + BAD_ALIGN, data->PTLEN, out, in); - - g_assert(memcmp(out, data->CTX, data->PTLEN) == 0); - - /* plain text not aligned */ - memcpy(T, Torg, 16); - memcpy(in + BAD_ALIGN, data->PTX, data->PTLEN); - xts_encrypt(&aesdata, &aestweak, - test_xts_aes_encrypt, - test_xts_aes_decrypt, - T, data->PTLEN, out, in + BAD_ALIGN); - - g_assert(memcmp(out, data->CTX, data->PTLEN) == 0); - - /* cipher text not aligned */ - memcpy(T, Torg, 16); - memcpy(in, data->PTX, data->PTLEN); - xts_encrypt(&aesdata, &aestweak, - test_xts_aes_encrypt, - test_xts_aes_decrypt, - T, data->PTLEN, out + BAD_ALIGN, in); - - g_assert(memcmp(out + BAD_ALIGN, data->CTX, data->PTLEN) == 0); - - - /* IV not aligned */ - memcpy(T + BAD_ALIGN, Torg, 16); - memcpy(in, data->CTX, data->PTLEN); - xts_decrypt(&aesdata, &aestweak, - test_xts_aes_encrypt, - test_xts_aes_decrypt, - T + BAD_ALIGN, data->PTLEN, out, in); - - g_assert(memcmp(out, data->PTX, data->PTLEN) == 0); - - /* cipher text not aligned */ - memcpy(T, Torg, 16); - memcpy(in + BAD_ALIGN, data->CTX, data->PTLEN); - xts_decrypt(&aesdata, &aestweak, - test_xts_aes_encrypt, - test_xts_aes_decrypt, - T, data->PTLEN, out, in + BAD_ALIGN); - - g_assert(memcmp(out, data->PTX, data->PTLEN) == 0); - - /* plain text not aligned */ - memcpy(T, Torg, 16); - memcpy(in, data->CTX, data->PTLEN); - xts_decrypt(&aesdata, &aestweak, - test_xts_aes_encrypt, - test_xts_aes_decrypt, - T, data->PTLEN, out + BAD_ALIGN, in); - - g_assert(memcmp(out + BAD_ALIGN, data->PTX, data->PTLEN) == 0); -} - - -int main(int argc, char **argv) -{ - size_t i; - - g_test_init(&argc, &argv, NULL); - - g_assert(qcrypto_init(NULL) == 0); - - for (i = 0; i < G_N_ELEMENTS(test_data); i++) { - gchar *path = g_strdup_printf("%s/basic", test_data[i].path); - g_test_add_data_func(path, &test_data[i], test_xts); - g_free(path); - - /* skip the cases where the length is smaller than 2*blocklen - * or the length is not a multiple of 32 - */ - if ((test_data[i].PTLEN >= 32) && !(test_data[i].PTLEN % 32)) { - path = g_strdup_printf("%s/split", test_data[i].path); - g_test_add_data_func(path, &test_data[i], test_xts_split); - g_free(path); - } - - path = g_strdup_printf("%s/unaligned", test_data[i].path); - g_test_add_data_func(path, &test_data[i], test_xts_unaligned); - g_free(path); - } - - return g_test_run(); -} diff --git a/tests/test-cutils.c b/tests/test-cutils.c deleted file mode 100644 index 1aa8351520..0000000000 --- a/tests/test-cutils.c +++ /dev/null @@ -1,2460 +0,0 @@ -/* - * cutils.c unit-tests - * - * Copyright (C) 2013 Red Hat Inc. - * - * Authors: - * Eduardo Habkost - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "qemu/units.h" -#include "qemu/cutils.h" -#include "qemu/units.h" - -static void test_parse_uint_null(void) -{ - unsigned long long i = 999; - char f = 'X'; - char *endptr = &f; - int r; - - r = parse_uint(NULL, &i, &endptr, 0); - - g_assert_cmpint(r, ==, -EINVAL); - g_assert_cmpint(i, ==, 0); - g_assert(endptr == NULL); -} - -static void test_parse_uint_empty(void) -{ - unsigned long long i = 999; - char f = 'X'; - char *endptr = &f; - const char *str = ""; - int r; - - r = parse_uint(str, &i, &endptr, 0); - - g_assert_cmpint(r, ==, -EINVAL); - g_assert_cmpint(i, ==, 0); - g_assert(endptr == str); -} - -static void test_parse_uint_whitespace(void) -{ - unsigned long long i = 999; - char f = 'X'; - char *endptr = &f; - const char *str = " \t "; - int r; - - r = parse_uint(str, &i, &endptr, 0); - - g_assert_cmpint(r, ==, -EINVAL); - g_assert_cmpint(i, ==, 0); - g_assert(endptr == str); -} - - -static void test_parse_uint_invalid(void) -{ - unsigned long long i = 999; - char f = 'X'; - char *endptr = &f; - const char *str = " \t xxx"; - int r; - - r = parse_uint(str, &i, &endptr, 0); - - g_assert_cmpint(r, ==, -EINVAL); - g_assert_cmpint(i, ==, 0); - g_assert(endptr == str); -} - - -static void test_parse_uint_trailing(void) -{ - unsigned long long i = 999; - char f = 'X'; - char *endptr = &f; - const char *str = "123xxx"; - int r; - - r = parse_uint(str, &i, &endptr, 0); - - g_assert_cmpint(r, ==, 0); - g_assert_cmpint(i, ==, 123); - g_assert(endptr == str + 3); -} - -static void test_parse_uint_correct(void) -{ - unsigned long long i = 999; - char f = 'X'; - char *endptr = &f; - const char *str = "123"; - int r; - - r = parse_uint(str, &i, &endptr, 0); - - g_assert_cmpint(r, ==, 0); - g_assert_cmpint(i, ==, 123); - g_assert(endptr == str + strlen(str)); -} - -static void test_parse_uint_octal(void) -{ - unsigned long long i = 999; - char f = 'X'; - char *endptr = &f; - const char *str = "0123"; - int r; - - r = parse_uint(str, &i, &endptr, 0); - - g_assert_cmpint(r, ==, 0); - g_assert_cmpint(i, ==, 0123); - g_assert(endptr == str + strlen(str)); -} - -static void test_parse_uint_decimal(void) -{ - unsigned long long i = 999; - char f = 'X'; - char *endptr = &f; - const char *str = "0123"; - int r; - - r = parse_uint(str, &i, &endptr, 10); - - g_assert_cmpint(r, ==, 0); - g_assert_cmpint(i, ==, 123); - g_assert(endptr == str + strlen(str)); -} - - -static void test_parse_uint_llong_max(void) -{ - unsigned long long i = 999; - char f = 'X'; - char *endptr = &f; - char *str = g_strdup_printf("%llu", (unsigned long long)LLONG_MAX + 1); - int r; - - r = parse_uint(str, &i, &endptr, 0); - - g_assert_cmpint(r, ==, 0); - g_assert_cmpint(i, ==, (unsigned long long)LLONG_MAX + 1); - g_assert(endptr == str + strlen(str)); - - g_free(str); -} - -static void test_parse_uint_overflow(void) -{ - unsigned long long i = 999; - char f = 'X'; - char *endptr = &f; - const char *str = "99999999999999999999999999999999999999"; - int r; - - r = parse_uint(str, &i, &endptr, 0); - - g_assert_cmpint(r, ==, -ERANGE); - g_assert_cmpint(i, ==, ULLONG_MAX); - g_assert(endptr == str + strlen(str)); -} - -static void test_parse_uint_negative(void) -{ - unsigned long long i = 999; - char f = 'X'; - char *endptr = &f; - const char *str = " \t -321"; - int r; - - r = parse_uint(str, &i, &endptr, 0); - - g_assert_cmpint(r, ==, -ERANGE); - g_assert_cmpint(i, ==, 0); - g_assert(endptr == str + strlen(str)); -} - - -static void test_parse_uint_full_trailing(void) -{ - unsigned long long i = 999; - const char *str = "123xxx"; - int r; - - r = parse_uint_full(str, &i, 0); - - g_assert_cmpint(r, ==, -EINVAL); - g_assert_cmpint(i, ==, 0); -} - -static void test_parse_uint_full_correct(void) -{ - unsigned long long i = 999; - const char *str = "123"; - int r; - - r = parse_uint_full(str, &i, 0); - - g_assert_cmpint(r, ==, 0); - g_assert_cmpint(i, ==, 123); -} - -static void test_qemu_strtoi_correct(void) -{ - const char *str = "12345 foo"; - char f = 'X'; - const char *endptr = &f; - int res = 999; - int err; - - err = qemu_strtoi(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 12345); - g_assert(endptr == str + 5); -} - -static void test_qemu_strtoi_null(void) -{ - char f = 'X'; - const char *endptr = &f; - int res = 999; - int err; - - err = qemu_strtoi(NULL, &endptr, 0, &res); - - g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == NULL); -} - -static void test_qemu_strtoi_empty(void) -{ - const char *str = ""; - char f = 'X'; - const char *endptr = &f; - int res = 999; - int err; - - err = qemu_strtoi(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); -} - -static void test_qemu_strtoi_whitespace(void) -{ - const char *str = " \t "; - char f = 'X'; - const char *endptr = &f; - int res = 999; - int err; - - err = qemu_strtoi(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); -} - -static void test_qemu_strtoi_invalid(void) -{ - const char *str = " xxxx \t abc"; - char f = 'X'; - const char *endptr = &f; - int res = 999; - int err; - - err = qemu_strtoi(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); -} - -static void test_qemu_strtoi_trailing(void) -{ - const char *str = "123xxx"; - char f = 'X'; - const char *endptr = &f; - int res = 999; - int err; - - err = qemu_strtoi(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 123); - g_assert(endptr == str + 3); -} - -static void test_qemu_strtoi_octal(void) -{ - const char *str = "0123"; - char f = 'X'; - const char *endptr = &f; - int res = 999; - int err; - - err = qemu_strtoi(str, &endptr, 8, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 0123); - g_assert(endptr == str + strlen(str)); - - res = 999; - endptr = &f; - err = qemu_strtoi(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 0123); - g_assert(endptr == str + strlen(str)); -} - -static void test_qemu_strtoi_decimal(void) -{ - const char *str = "0123"; - char f = 'X'; - const char *endptr = &f; - int res = 999; - int err; - - err = qemu_strtoi(str, &endptr, 10, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 123); - g_assert(endptr == str + strlen(str)); - - str = "123"; - res = 999; - endptr = &f; - err = qemu_strtoi(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 123); - g_assert(endptr == str + strlen(str)); -} - -static void test_qemu_strtoi_hex(void) -{ - const char *str = "0123"; - char f = 'X'; - const char *endptr = &f; - int res = 999; - int err; - - err = qemu_strtoi(str, &endptr, 16, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 0x123); - g_assert(endptr == str + strlen(str)); - - str = "0x123"; - res = 999; - endptr = &f; - err = qemu_strtoi(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 0x123); - g_assert(endptr == str + strlen(str)); -} - -static void test_qemu_strtoi_max(void) -{ - char *str = g_strdup_printf("%d", INT_MAX); - char f = 'X'; - const char *endptr = &f; - int res = 999; - int err; - - err = qemu_strtoi(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, INT_MAX); - g_assert(endptr == str + strlen(str)); - g_free(str); -} - -static void test_qemu_strtoi_overflow(void) -{ - char *str = g_strdup_printf("%lld", (long long)INT_MAX + 1ll); - char f = 'X'; - const char *endptr = &f; - int res = 999; - int err; - - err = qemu_strtoi(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, -ERANGE); - g_assert_cmpint(res, ==, INT_MAX); - g_assert(endptr == str + strlen(str)); - g_free(str); -} - -static void test_qemu_strtoi_underflow(void) -{ - char *str = g_strdup_printf("%lld", (long long)INT_MIN - 1ll); - char f = 'X'; - const char *endptr = &f; - int res = 999; - int err; - - err = qemu_strtoi(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, -ERANGE); - g_assert_cmpint(res, ==, INT_MIN); - g_assert(endptr == str + strlen(str)); - g_free(str); -} - -static void test_qemu_strtoi_negative(void) -{ - const char *str = " \t -321"; - char f = 'X'; - const char *endptr = &f; - int res = 999; - int err; - - err = qemu_strtoi(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, -321); - g_assert(endptr == str + strlen(str)); -} - -static void test_qemu_strtoi_full_correct(void) -{ - const char *str = "123"; - int res = 999; - int err; - - err = qemu_strtoi(str, NULL, 0, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 123); -} - -static void test_qemu_strtoi_full_null(void) -{ - char f = 'X'; - const char *endptr = &f; - int res = 999; - int err; - - err = qemu_strtoi(NULL, &endptr, 0, &res); - - g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == NULL); -} - -static void test_qemu_strtoi_full_empty(void) -{ - const char *str = ""; - int res = 999L; - int err; - - err = qemu_strtoi(str, NULL, 0, &res); - - g_assert_cmpint(err, ==, -EINVAL); -} - -static void test_qemu_strtoi_full_negative(void) -{ - const char *str = " \t -321"; - int res = 999; - int err; - - err = qemu_strtoi(str, NULL, 0, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, -321); -} - -static void test_qemu_strtoi_full_trailing(void) -{ - const char *str = "123xxx"; - int res; - int err; - - err = qemu_strtoi(str, NULL, 0, &res); - - g_assert_cmpint(err, ==, -EINVAL); -} - -static void test_qemu_strtoi_full_max(void) -{ - char *str = g_strdup_printf("%d", INT_MAX); - int res; - int err; - - err = qemu_strtoi(str, NULL, 0, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, INT_MAX); - g_free(str); -} - -static void test_qemu_strtoui_correct(void) -{ - const char *str = "12345 foo"; - char f = 'X'; - const char *endptr = &f; - unsigned int res = 999; - int err; - - err = qemu_strtoui(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, 12345); - g_assert(endptr == str + 5); -} - -static void test_qemu_strtoui_null(void) -{ - char f = 'X'; - const char *endptr = &f; - unsigned int res = 999; - int err; - - err = qemu_strtoui(NULL, &endptr, 0, &res); - - g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == NULL); -} - -static void test_qemu_strtoui_empty(void) -{ - const char *str = ""; - char f = 'X'; - const char *endptr = &f; - unsigned int res = 999; - int err; - - err = qemu_strtoui(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); -} - -static void test_qemu_strtoui_whitespace(void) -{ - const char *str = " \t "; - char f = 'X'; - const char *endptr = &f; - unsigned int res = 999; - int err; - - err = qemu_strtoui(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); -} - -static void test_qemu_strtoui_invalid(void) -{ - const char *str = " xxxx \t abc"; - char f = 'X'; - const char *endptr = &f; - unsigned int res = 999; - int err; - - err = qemu_strtoui(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); -} - -static void test_qemu_strtoui_trailing(void) -{ - const char *str = "123xxx"; - char f = 'X'; - const char *endptr = &f; - unsigned int res = 999; - int err; - - err = qemu_strtoui(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, 123); - g_assert(endptr == str + 3); -} - -static void test_qemu_strtoui_octal(void) -{ - const char *str = "0123"; - char f = 'X'; - const char *endptr = &f; - unsigned int res = 999; - int err; - - err = qemu_strtoui(str, &endptr, 8, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, 0123); - g_assert(endptr == str + strlen(str)); - - res = 999; - endptr = &f; - err = qemu_strtoui(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, 0123); - g_assert(endptr == str + strlen(str)); -} - -static void test_qemu_strtoui_decimal(void) -{ - const char *str = "0123"; - char f = 'X'; - const char *endptr = &f; - unsigned int res = 999; - int err; - - err = qemu_strtoui(str, &endptr, 10, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, 123); - g_assert(endptr == str + strlen(str)); - - str = "123"; - res = 999; - endptr = &f; - err = qemu_strtoui(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, 123); - g_assert(endptr == str + strlen(str)); -} - -static void test_qemu_strtoui_hex(void) -{ - const char *str = "0123"; - char f = 'X'; - const char *endptr = &f; - unsigned int res = 999; - int err; - - err = qemu_strtoui(str, &endptr, 16, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmphex(res, ==, 0x123); - g_assert(endptr == str + strlen(str)); - - str = "0x123"; - res = 999; - endptr = &f; - err = qemu_strtoui(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmphex(res, ==, 0x123); - g_assert(endptr == str + strlen(str)); -} - -static void test_qemu_strtoui_max(void) -{ - char *str = g_strdup_printf("%u", UINT_MAX); - char f = 'X'; - const char *endptr = &f; - unsigned int res = 999; - int err; - - err = qemu_strtoui(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmphex(res, ==, UINT_MAX); - g_assert(endptr == str + strlen(str)); - g_free(str); -} - -static void test_qemu_strtoui_overflow(void) -{ - char *str = g_strdup_printf("%lld", (long long)UINT_MAX + 1ll); - char f = 'X'; - const char *endptr = &f; - unsigned int res = 999; - int err; - - err = qemu_strtoui(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, -ERANGE); - g_assert_cmphex(res, ==, UINT_MAX); - g_assert(endptr == str + strlen(str)); - g_free(str); -} - -static void test_qemu_strtoui_underflow(void) -{ - char *str = g_strdup_printf("%lld", (long long)INT_MIN - 1ll); - char f = 'X'; - const char *endptr = &f; - unsigned int res = 999; - int err; - - err = qemu_strtoui(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, -ERANGE); - g_assert_cmpuint(res, ==, (unsigned int)-1); - g_assert(endptr == str + strlen(str)); - g_free(str); -} - -static void test_qemu_strtoui_negative(void) -{ - const char *str = " \t -321"; - char f = 'X'; - const char *endptr = &f; - unsigned int res = 999; - int err; - - err = qemu_strtoui(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, (unsigned int)-321); - g_assert(endptr == str + strlen(str)); -} - -static void test_qemu_strtoui_full_correct(void) -{ - const char *str = "123"; - unsigned int res = 999; - int err; - - err = qemu_strtoui(str, NULL, 0, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, 123); -} - -static void test_qemu_strtoui_full_null(void) -{ - unsigned int res = 999; - int err; - - err = qemu_strtoui(NULL, NULL, 0, &res); - - g_assert_cmpint(err, ==, -EINVAL); -} - -static void test_qemu_strtoui_full_empty(void) -{ - const char *str = ""; - unsigned int res = 999; - int err; - - err = qemu_strtoui(str, NULL, 0, &res); - - g_assert_cmpint(err, ==, -EINVAL); -} -static void test_qemu_strtoui_full_negative(void) -{ - const char *str = " \t -321"; - unsigned int res = 999; - int err; - - err = qemu_strtoui(str, NULL, 0, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, (unsigned int)-321); -} - -static void test_qemu_strtoui_full_trailing(void) -{ - const char *str = "123xxx"; - unsigned int res; - int err; - - err = qemu_strtoui(str, NULL, 0, &res); - - g_assert_cmpint(err, ==, -EINVAL); -} - -static void test_qemu_strtoui_full_max(void) -{ - char *str = g_strdup_printf("%u", UINT_MAX); - unsigned int res = 999; - int err; - - err = qemu_strtoui(str, NULL, 0, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmphex(res, ==, UINT_MAX); - g_free(str); -} - -static void test_qemu_strtol_correct(void) -{ - const char *str = "12345 foo"; - char f = 'X'; - const char *endptr = &f; - long res = 999; - int err; - - err = qemu_strtol(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 12345); - g_assert(endptr == str + 5); -} - -static void test_qemu_strtol_null(void) -{ - char f = 'X'; - const char *endptr = &f; - long res = 999; - int err; - - err = qemu_strtol(NULL, &endptr, 0, &res); - - g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == NULL); -} - -static void test_qemu_strtol_empty(void) -{ - const char *str = ""; - char f = 'X'; - const char *endptr = &f; - long res = 999; - int err; - - err = qemu_strtol(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); -} - -static void test_qemu_strtol_whitespace(void) -{ - const char *str = " \t "; - char f = 'X'; - const char *endptr = &f; - long res = 999; - int err; - - err = qemu_strtol(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); -} - -static void test_qemu_strtol_invalid(void) -{ - const char *str = " xxxx \t abc"; - char f = 'X'; - const char *endptr = &f; - long res = 999; - int err; - - err = qemu_strtol(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); -} - -static void test_qemu_strtol_trailing(void) -{ - const char *str = "123xxx"; - char f = 'X'; - const char *endptr = &f; - long res = 999; - int err; - - err = qemu_strtol(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 123); - g_assert(endptr == str + 3); -} - -static void test_qemu_strtol_octal(void) -{ - const char *str = "0123"; - char f = 'X'; - const char *endptr = &f; - long res = 999; - int err; - - err = qemu_strtol(str, &endptr, 8, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 0123); - g_assert(endptr == str + strlen(str)); - - res = 999; - endptr = &f; - err = qemu_strtol(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 0123); - g_assert(endptr == str + strlen(str)); -} - -static void test_qemu_strtol_decimal(void) -{ - const char *str = "0123"; - char f = 'X'; - const char *endptr = &f; - long res = 999; - int err; - - err = qemu_strtol(str, &endptr, 10, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 123); - g_assert(endptr == str + strlen(str)); - - str = "123"; - res = 999; - endptr = &f; - err = qemu_strtol(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 123); - g_assert(endptr == str + strlen(str)); -} - -static void test_qemu_strtol_hex(void) -{ - const char *str = "0123"; - char f = 'X'; - const char *endptr = &f; - long res = 999; - int err; - - err = qemu_strtol(str, &endptr, 16, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 0x123); - g_assert(endptr == str + strlen(str)); - - str = "0x123"; - res = 999; - endptr = &f; - err = qemu_strtol(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 0x123); - g_assert(endptr == str + strlen(str)); -} - -static void test_qemu_strtol_max(void) -{ - char *str = g_strdup_printf("%ld", LONG_MAX); - char f = 'X'; - const char *endptr = &f; - long res = 999; - int err; - - err = qemu_strtol(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, LONG_MAX); - g_assert(endptr == str + strlen(str)); - g_free(str); -} - -static void test_qemu_strtol_overflow(void) -{ - const char *str = "99999999999999999999999999999999999999999999"; - char f = 'X'; - const char *endptr = &f; - long res = 999; - int err; - - err = qemu_strtol(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, -ERANGE); - g_assert_cmpint(res, ==, LONG_MAX); - g_assert(endptr == str + strlen(str)); -} - -static void test_qemu_strtol_underflow(void) -{ - const char *str = "-99999999999999999999999999999999999999999999"; - char f = 'X'; - const char *endptr = &f; - long res = 999; - int err; - - err = qemu_strtol(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, -ERANGE); - g_assert_cmpint(res, ==, LONG_MIN); - g_assert(endptr == str + strlen(str)); -} - -static void test_qemu_strtol_negative(void) -{ - const char *str = " \t -321"; - char f = 'X'; - const char *endptr = &f; - long res = 999; - int err; - - err = qemu_strtol(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, -321); - g_assert(endptr == str + strlen(str)); -} - -static void test_qemu_strtol_full_correct(void) -{ - const char *str = "123"; - long res = 999; - int err; - - err = qemu_strtol(str, NULL, 0, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 123); -} - -static void test_qemu_strtol_full_null(void) -{ - char f = 'X'; - const char *endptr = &f; - long res = 999; - int err; - - err = qemu_strtol(NULL, &endptr, 0, &res); - - g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == NULL); -} - -static void test_qemu_strtol_full_empty(void) -{ - const char *str = ""; - long res = 999L; - int err; - - err = qemu_strtol(str, NULL, 0, &res); - - g_assert_cmpint(err, ==, -EINVAL); -} - -static void test_qemu_strtol_full_negative(void) -{ - const char *str = " \t -321"; - long res = 999; - int err; - - err = qemu_strtol(str, NULL, 0, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, -321); -} - -static void test_qemu_strtol_full_trailing(void) -{ - const char *str = "123xxx"; - long res; - int err; - - err = qemu_strtol(str, NULL, 0, &res); - - g_assert_cmpint(err, ==, -EINVAL); -} - -static void test_qemu_strtol_full_max(void) -{ - char *str = g_strdup_printf("%ld", LONG_MAX); - long res; - int err; - - err = qemu_strtol(str, NULL, 0, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, LONG_MAX); - g_free(str); -} - -static void test_qemu_strtoul_correct(void) -{ - const char *str = "12345 foo"; - char f = 'X'; - const char *endptr = &f; - unsigned long res = 999; - int err; - - err = qemu_strtoul(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, 12345); - g_assert(endptr == str + 5); -} - -static void test_qemu_strtoul_null(void) -{ - char f = 'X'; - const char *endptr = &f; - unsigned long res = 999; - int err; - - err = qemu_strtoul(NULL, &endptr, 0, &res); - - g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == NULL); -} - -static void test_qemu_strtoul_empty(void) -{ - const char *str = ""; - char f = 'X'; - const char *endptr = &f; - unsigned long res = 999; - int err; - - err = qemu_strtoul(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); -} - -static void test_qemu_strtoul_whitespace(void) -{ - const char *str = " \t "; - char f = 'X'; - const char *endptr = &f; - unsigned long res = 999; - int err; - - err = qemu_strtoul(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); -} - -static void test_qemu_strtoul_invalid(void) -{ - const char *str = " xxxx \t abc"; - char f = 'X'; - const char *endptr = &f; - unsigned long res = 999; - int err; - - err = qemu_strtoul(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); -} - -static void test_qemu_strtoul_trailing(void) -{ - const char *str = "123xxx"; - char f = 'X'; - const char *endptr = &f; - unsigned long res = 999; - int err; - - err = qemu_strtoul(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, 123); - g_assert(endptr == str + 3); -} - -static void test_qemu_strtoul_octal(void) -{ - const char *str = "0123"; - char f = 'X'; - const char *endptr = &f; - unsigned long res = 999; - int err; - - err = qemu_strtoul(str, &endptr, 8, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, 0123); - g_assert(endptr == str + strlen(str)); - - res = 999; - endptr = &f; - err = qemu_strtoul(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, 0123); - g_assert(endptr == str + strlen(str)); -} - -static void test_qemu_strtoul_decimal(void) -{ - const char *str = "0123"; - char f = 'X'; - const char *endptr = &f; - unsigned long res = 999; - int err; - - err = qemu_strtoul(str, &endptr, 10, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, 123); - g_assert(endptr == str + strlen(str)); - - str = "123"; - res = 999; - endptr = &f; - err = qemu_strtoul(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, 123); - g_assert(endptr == str + strlen(str)); -} - -static void test_qemu_strtoul_hex(void) -{ - const char *str = "0123"; - char f = 'X'; - const char *endptr = &f; - unsigned long res = 999; - int err; - - err = qemu_strtoul(str, &endptr, 16, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmphex(res, ==, 0x123); - g_assert(endptr == str + strlen(str)); - - str = "0x123"; - res = 999; - endptr = &f; - err = qemu_strtoul(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmphex(res, ==, 0x123); - g_assert(endptr == str + strlen(str)); -} - -static void test_qemu_strtoul_max(void) -{ - char *str = g_strdup_printf("%lu", ULONG_MAX); - char f = 'X'; - const char *endptr = &f; - unsigned long res = 999; - int err; - - err = qemu_strtoul(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmphex(res, ==, ULONG_MAX); - g_assert(endptr == str + strlen(str)); - g_free(str); -} - -static void test_qemu_strtoul_overflow(void) -{ - const char *str = "99999999999999999999999999999999999999999999"; - char f = 'X'; - const char *endptr = &f; - unsigned long res = 999; - int err; - - err = qemu_strtoul(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, -ERANGE); - g_assert_cmphex(res, ==, ULONG_MAX); - g_assert(endptr == str + strlen(str)); -} - -static void test_qemu_strtoul_underflow(void) -{ - const char *str = "-99999999999999999999999999999999999999999999"; - char f = 'X'; - const char *endptr = &f; - unsigned long res = 999; - int err; - - err = qemu_strtoul(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, -ERANGE); - g_assert_cmpuint(res, ==, -1ul); - g_assert(endptr == str + strlen(str)); -} - -static void test_qemu_strtoul_negative(void) -{ - const char *str = " \t -321"; - char f = 'X'; - const char *endptr = &f; - unsigned long res = 999; - int err; - - err = qemu_strtoul(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, -321ul); - g_assert(endptr == str + strlen(str)); -} - -static void test_qemu_strtoul_full_correct(void) -{ - const char *str = "123"; - unsigned long res = 999; - int err; - - err = qemu_strtoul(str, NULL, 0, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, 123); -} - -static void test_qemu_strtoul_full_null(void) -{ - unsigned long res = 999; - int err; - - err = qemu_strtoul(NULL, NULL, 0, &res); - - g_assert_cmpint(err, ==, -EINVAL); -} - -static void test_qemu_strtoul_full_empty(void) -{ - const char *str = ""; - unsigned long res = 999; - int err; - - err = qemu_strtoul(str, NULL, 0, &res); - - g_assert_cmpint(err, ==, -EINVAL); -} -static void test_qemu_strtoul_full_negative(void) -{ - const char *str = " \t -321"; - unsigned long res = 999; - int err; - - err = qemu_strtoul(str, NULL, 0, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, -321ul); -} - -static void test_qemu_strtoul_full_trailing(void) -{ - const char *str = "123xxx"; - unsigned long res; - int err; - - err = qemu_strtoul(str, NULL, 0, &res); - - g_assert_cmpint(err, ==, -EINVAL); -} - -static void test_qemu_strtoul_full_max(void) -{ - char *str = g_strdup_printf("%lu", ULONG_MAX); - unsigned long res = 999; - int err; - - err = qemu_strtoul(str, NULL, 0, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmphex(res, ==, ULONG_MAX); - g_free(str); -} - -static void test_qemu_strtoi64_correct(void) -{ - const char *str = "12345 foo"; - char f = 'X'; - const char *endptr = &f; - int64_t res = 999; - int err; - - err = qemu_strtoi64(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 12345); - g_assert(endptr == str + 5); -} - -static void test_qemu_strtoi64_null(void) -{ - char f = 'X'; - const char *endptr = &f; - int64_t res = 999; - int err; - - err = qemu_strtoi64(NULL, &endptr, 0, &res); - - g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == NULL); -} - -static void test_qemu_strtoi64_empty(void) -{ - const char *str = ""; - char f = 'X'; - const char *endptr = &f; - int64_t res = 999; - int err; - - err = qemu_strtoi64(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); -} - -static void test_qemu_strtoi64_whitespace(void) -{ - const char *str = " \t "; - char f = 'X'; - const char *endptr = &f; - int64_t res = 999; - int err; - - err = qemu_strtoi64(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); -} - -static void test_qemu_strtoi64_invalid(void) -{ - const char *str = " xxxx \t abc"; - char f = 'X'; - const char *endptr = &f; - int64_t res = 999; - int err; - - err = qemu_strtoi64(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); -} - -static void test_qemu_strtoi64_trailing(void) -{ - const char *str = "123xxx"; - char f = 'X'; - const char *endptr = &f; - int64_t res = 999; - int err; - - err = qemu_strtoi64(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 123); - g_assert(endptr == str + 3); -} - -static void test_qemu_strtoi64_octal(void) -{ - const char *str = "0123"; - char f = 'X'; - const char *endptr = &f; - int64_t res = 999; - int err; - - err = qemu_strtoi64(str, &endptr, 8, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 0123); - g_assert(endptr == str + strlen(str)); - - endptr = &f; - res = 999; - err = qemu_strtoi64(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 0123); - g_assert(endptr == str + strlen(str)); -} - -static void test_qemu_strtoi64_decimal(void) -{ - const char *str = "0123"; - char f = 'X'; - const char *endptr = &f; - int64_t res = 999; - int err; - - err = qemu_strtoi64(str, &endptr, 10, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 123); - g_assert(endptr == str + strlen(str)); - - str = "123"; - endptr = &f; - res = 999; - err = qemu_strtoi64(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 123); - g_assert(endptr == str + strlen(str)); -} - -static void test_qemu_strtoi64_hex(void) -{ - const char *str = "0123"; - char f = 'X'; - const char *endptr = &f; - int64_t res = 999; - int err; - - err = qemu_strtoi64(str, &endptr, 16, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 0x123); - g_assert(endptr == str + strlen(str)); - - str = "0x123"; - endptr = &f; - res = 999; - err = qemu_strtoi64(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 0x123); - g_assert(endptr == str + strlen(str)); -} - -static void test_qemu_strtoi64_max(void) -{ - char *str = g_strdup_printf("%lld", LLONG_MAX); - char f = 'X'; - const char *endptr = &f; - int64_t res = 999; - int err; - - err = qemu_strtoi64(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, LLONG_MAX); - g_assert(endptr == str + strlen(str)); - g_free(str); -} - -static void test_qemu_strtoi64_overflow(void) -{ - const char *str = "99999999999999999999999999999999999999999999"; - char f = 'X'; - const char *endptr = &f; - int64_t res = 999; - int err; - - err = qemu_strtoi64(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, -ERANGE); - g_assert_cmpint(res, ==, LLONG_MAX); - g_assert(endptr == str + strlen(str)); -} - -static void test_qemu_strtoi64_underflow(void) -{ - const char *str = "-99999999999999999999999999999999999999999999"; - char f = 'X'; - const char *endptr = &f; - int64_t res = 999; - int err; - - err = qemu_strtoi64(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, -ERANGE); - g_assert_cmpint(res, ==, LLONG_MIN); - g_assert(endptr == str + strlen(str)); -} - -static void test_qemu_strtoi64_negative(void) -{ - const char *str = " \t -321"; - char f = 'X'; - const char *endptr = &f; - int64_t res = 999; - int err; - - err = qemu_strtoi64(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, -321); - g_assert(endptr == str + strlen(str)); -} - -static void test_qemu_strtoi64_full_correct(void) -{ - const char *str = "123"; - int64_t res = 999; - int err; - - err = qemu_strtoi64(str, NULL, 0, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 123); -} - -static void test_qemu_strtoi64_full_null(void) -{ - int64_t res = 999; - int err; - - err = qemu_strtoi64(NULL, NULL, 0, &res); - - g_assert_cmpint(err, ==, -EINVAL); -} - -static void test_qemu_strtoi64_full_empty(void) -{ - const char *str = ""; - int64_t res = 999; - int err; - - err = qemu_strtoi64(str, NULL, 0, &res); - - g_assert_cmpint(err, ==, -EINVAL); -} - -static void test_qemu_strtoi64_full_negative(void) -{ - const char *str = " \t -321"; - int64_t res = 999; - int err; - - err = qemu_strtoi64(str, NULL, 0, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, -321); -} - -static void test_qemu_strtoi64_full_trailing(void) -{ - const char *str = "123xxx"; - int64_t res = 999; - int err; - - err = qemu_strtoi64(str, NULL, 0, &res); - - g_assert_cmpint(err, ==, -EINVAL); -} - -static void test_qemu_strtoi64_full_max(void) -{ - - char *str = g_strdup_printf("%lld", LLONG_MAX); - int64_t res; - int err; - - err = qemu_strtoi64(str, NULL, 0, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, LLONG_MAX); - g_free(str); -} - -static void test_qemu_strtou64_correct(void) -{ - const char *str = "12345 foo"; - char f = 'X'; - const char *endptr = &f; - uint64_t res = 999; - int err; - - err = qemu_strtou64(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, 12345); - g_assert(endptr == str + 5); -} - -static void test_qemu_strtou64_null(void) -{ - char f = 'X'; - const char *endptr = &f; - uint64_t res = 999; - int err; - - err = qemu_strtou64(NULL, &endptr, 0, &res); - - g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == NULL); -} - -static void test_qemu_strtou64_empty(void) -{ - const char *str = ""; - char f = 'X'; - const char *endptr = &f; - uint64_t res = 999; - int err; - - err = qemu_strtou64(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); -} - -static void test_qemu_strtou64_whitespace(void) -{ - const char *str = " \t "; - char f = 'X'; - const char *endptr = &f; - uint64_t res = 999; - int err; - - err = qemu_strtou64(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); -} - -static void test_qemu_strtou64_invalid(void) -{ - const char *str = " xxxx \t abc"; - char f = 'X'; - const char *endptr = &f; - uint64_t res = 999; - int err; - - err = qemu_strtou64(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); -} - -static void test_qemu_strtou64_trailing(void) -{ - const char *str = "123xxx"; - char f = 'X'; - const char *endptr = &f; - uint64_t res = 999; - int err; - - err = qemu_strtou64(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, 123); - g_assert(endptr == str + 3); -} - -static void test_qemu_strtou64_octal(void) -{ - const char *str = "0123"; - char f = 'X'; - const char *endptr = &f; - uint64_t res = 999; - int err; - - err = qemu_strtou64(str, &endptr, 8, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, 0123); - g_assert(endptr == str + strlen(str)); - - endptr = &f; - res = 999; - err = qemu_strtou64(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, 0123); - g_assert(endptr == str + strlen(str)); -} - -static void test_qemu_strtou64_decimal(void) -{ - const char *str = "0123"; - char f = 'X'; - const char *endptr = &f; - uint64_t res = 999; - int err; - - err = qemu_strtou64(str, &endptr, 10, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, 123); - g_assert(endptr == str + strlen(str)); - - str = "123"; - endptr = &f; - res = 999; - err = qemu_strtou64(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, 123); - g_assert(endptr == str + strlen(str)); -} - -static void test_qemu_strtou64_hex(void) -{ - const char *str = "0123"; - char f = 'X'; - const char *endptr = &f; - uint64_t res = 999; - int err; - - err = qemu_strtou64(str, &endptr, 16, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmphex(res, ==, 0x123); - g_assert(endptr == str + strlen(str)); - - str = "0x123"; - endptr = &f; - res = 999; - err = qemu_strtou64(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmphex(res, ==, 0x123); - g_assert(endptr == str + strlen(str)); -} - -static void test_qemu_strtou64_max(void) -{ - char *str = g_strdup_printf("%llu", ULLONG_MAX); - char f = 'X'; - const char *endptr = &f; - uint64_t res = 999; - int err; - - err = qemu_strtou64(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmphex(res, ==, ULLONG_MAX); - g_assert(endptr == str + strlen(str)); - g_free(str); -} - -static void test_qemu_strtou64_overflow(void) -{ - const char *str = "99999999999999999999999999999999999999999999"; - char f = 'X'; - const char *endptr = &f; - uint64_t res = 999; - int err; - - err = qemu_strtou64(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, -ERANGE); - g_assert_cmphex(res, ==, ULLONG_MAX); - g_assert(endptr == str + strlen(str)); -} - -static void test_qemu_strtou64_underflow(void) -{ - const char *str = "-99999999999999999999999999999999999999999999"; - char f = 'X'; - const char *endptr = &f; - uint64_t res = 999; - int err; - - err = qemu_strtou64(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, -ERANGE); - g_assert_cmphex(res, ==, -1ull); - g_assert(endptr == str + strlen(str)); -} - -static void test_qemu_strtou64_negative(void) -{ - const char *str = " \t -321"; - char f = 'X'; - const char *endptr = &f; - uint64_t res = 999; - int err; - - err = qemu_strtou64(str, &endptr, 0, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, -321ull); - g_assert(endptr == str + strlen(str)); -} - -static void test_qemu_strtou64_full_correct(void) -{ - const char *str = "18446744073709551614"; - uint64_t res = 999; - int err; - - err = qemu_strtou64(str, NULL, 0, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, 18446744073709551614ull); -} - -static void test_qemu_strtou64_full_null(void) -{ - uint64_t res = 999; - int err; - - err = qemu_strtou64(NULL, NULL, 0, &res); - - g_assert_cmpint(err, ==, -EINVAL); -} - -static void test_qemu_strtou64_full_empty(void) -{ - const char *str = ""; - uint64_t res = 999; - int err; - - err = qemu_strtou64(str, NULL, 0, &res); - - g_assert_cmpint(err, ==, -EINVAL); -} - -static void test_qemu_strtou64_full_negative(void) -{ - const char *str = " \t -321"; - uint64_t res = 999; - int err; - - err = qemu_strtou64(str, NULL, 0, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, -321ull); -} - -static void test_qemu_strtou64_full_trailing(void) -{ - const char *str = "18446744073709551614xxxxxx"; - uint64_t res = 999; - int err; - - err = qemu_strtou64(str, NULL, 0, &res); - - g_assert_cmpint(err, ==, -EINVAL); -} - -static void test_qemu_strtou64_full_max(void) -{ - char *str = g_strdup_printf("%lld", ULLONG_MAX); - uint64_t res = 999; - int err; - - err = qemu_strtou64(str, NULL, 0, &res); - - g_assert_cmpint(err, ==, 0); - g_assert_cmphex(res, ==, ULLONG_MAX); - g_free(str); -} - -static void test_qemu_strtosz_simple(void) -{ - const char *str; - const char *endptr; - int err; - uint64_t res = 0xbaadf00d; - - str = "0"; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 0); - g_assert(endptr == str + 1); - - str = "12345"; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 12345); - g_assert(endptr == str + 5); - - err = qemu_strtosz(str, NULL, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 12345); - - /* Note: precision is 53 bits since we're parsing with strtod() */ - - str = "9007199254740991"; /* 2^53-1 */ - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 0x1fffffffffffff); - g_assert(endptr == str + 16); - - str = "9007199254740992"; /* 2^53 */ - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 0x20000000000000); - g_assert(endptr == str + 16); - - str = "9007199254740993"; /* 2^53+1 */ - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 0x20000000000000); /* rounded to 53 bits */ - g_assert(endptr == str + 16); - - str = "18446744073709549568"; /* 0xfffffffffffff800 (53 msbs set) */ - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 0xfffffffffffff800); - g_assert(endptr == str + 20); - - str = "18446744073709550591"; /* 0xfffffffffffffbff */ - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 0xfffffffffffff800); /* rounded to 53 bits */ - g_assert(endptr == str + 20); - - /* 0x7ffffffffffffe00..0x7fffffffffffffff get rounded to - * 0x8000000000000000, thus -ERANGE; see test_qemu_strtosz_erange() */ -} - -static void test_qemu_strtosz_units(void) -{ - const char *none = "1"; - const char *b = "1B"; - const char *k = "1K"; - const char *m = "1M"; - const char *g = "1G"; - const char *t = "1T"; - const char *p = "1P"; - const char *e = "1E"; - int err; - const char *endptr; - uint64_t res = 0xbaadf00d; - - /* default is M */ - err = qemu_strtosz_MiB(none, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, MiB); - g_assert(endptr == none + 1); - - err = qemu_strtosz(b, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 1); - g_assert(endptr == b + 2); - - err = qemu_strtosz(k, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, KiB); - g_assert(endptr == k + 2); - - err = qemu_strtosz(m, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, MiB); - g_assert(endptr == m + 2); - - err = qemu_strtosz(g, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, GiB); - g_assert(endptr == g + 2); - - err = qemu_strtosz(t, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, TiB); - g_assert(endptr == t + 2); - - err = qemu_strtosz(p, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, PiB); - g_assert(endptr == p + 2); - - err = qemu_strtosz(e, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, EiB); - g_assert(endptr == e + 2); -} - -static void test_qemu_strtosz_float(void) -{ - const char *str = "12.345M"; - int err; - const char *endptr; - uint64_t res = 0xbaadf00d; - - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 12.345 * MiB); - g_assert(endptr == str + 7); -} - -static void test_qemu_strtosz_invalid(void) -{ - const char *str; - const char *endptr; - int err; - uint64_t res = 0xbaadf00d; - - str = ""; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); - - str = " \t "; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); - - str = "crap"; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); - - str = "inf"; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); - - str = "NaN"; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); -} - -static void test_qemu_strtosz_trailing(void) -{ - const char *str; - const char *endptr; - int err; - uint64_t res = 0xbaadf00d; - - str = "123xxx"; - err = qemu_strtosz_MiB(str, &endptr, &res); - g_assert_cmpint(res, ==, 123 * MiB); - g_assert(endptr == str + 3); - - err = qemu_strtosz(str, NULL, &res); - g_assert_cmpint(err, ==, -EINVAL); - - str = "1kiB"; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 1024); - g_assert(endptr == str + 2); - - err = qemu_strtosz(str, NULL, &res); - g_assert_cmpint(err, ==, -EINVAL); -} - -static void test_qemu_strtosz_erange(void) -{ - const char *str; - const char *endptr; - int err; - uint64_t res = 0xbaadf00d; - - str = "-1"; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -ERANGE); - g_assert(endptr == str + 2); - - str = "18446744073709550592"; /* 0xfffffffffffffc00 */ - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -ERANGE); - g_assert(endptr == str + 20); - - str = "18446744073709551615"; /* 2^64-1 */ - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -ERANGE); - g_assert(endptr == str + 20); - - str = "18446744073709551616"; /* 2^64 */ - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -ERANGE); - g_assert(endptr == str + 20); - - str = "20E"; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -ERANGE); - g_assert(endptr == str + 3); -} - -static void test_qemu_strtosz_metric(void) -{ - const char *str = "12345k"; - int err; - const char *endptr; - uint64_t res = 0xbaadf00d; - - err = qemu_strtosz_metric(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 12345000); - g_assert(endptr == str + 6); -} - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - - g_test_add_func("/cutils/parse_uint/null", test_parse_uint_null); - g_test_add_func("/cutils/parse_uint/empty", test_parse_uint_empty); - g_test_add_func("/cutils/parse_uint/whitespace", - test_parse_uint_whitespace); - g_test_add_func("/cutils/parse_uint/invalid", test_parse_uint_invalid); - g_test_add_func("/cutils/parse_uint/trailing", test_parse_uint_trailing); - g_test_add_func("/cutils/parse_uint/correct", test_parse_uint_correct); - g_test_add_func("/cutils/parse_uint/octal", test_parse_uint_octal); - g_test_add_func("/cutils/parse_uint/decimal", test_parse_uint_decimal); - g_test_add_func("/cutils/parse_uint/llong_max", test_parse_uint_llong_max); - g_test_add_func("/cutils/parse_uint/overflow", test_parse_uint_overflow); - g_test_add_func("/cutils/parse_uint/negative", test_parse_uint_negative); - g_test_add_func("/cutils/parse_uint_full/trailing", - test_parse_uint_full_trailing); - g_test_add_func("/cutils/parse_uint_full/correct", - test_parse_uint_full_correct); - - /* qemu_strtoi() tests */ - g_test_add_func("/cutils/qemu_strtoi/correct", - test_qemu_strtoi_correct); - g_test_add_func("/cutils/qemu_strtoi/null", - test_qemu_strtoi_null); - g_test_add_func("/cutils/qemu_strtoi/empty", - test_qemu_strtoi_empty); - g_test_add_func("/cutils/qemu_strtoi/whitespace", - test_qemu_strtoi_whitespace); - g_test_add_func("/cutils/qemu_strtoi/invalid", - test_qemu_strtoi_invalid); - g_test_add_func("/cutils/qemu_strtoi/trailing", - test_qemu_strtoi_trailing); - g_test_add_func("/cutils/qemu_strtoi/octal", - test_qemu_strtoi_octal); - g_test_add_func("/cutils/qemu_strtoi/decimal", - test_qemu_strtoi_decimal); - g_test_add_func("/cutils/qemu_strtoi/hex", - test_qemu_strtoi_hex); - g_test_add_func("/cutils/qemu_strtoi/max", - test_qemu_strtoi_max); - g_test_add_func("/cutils/qemu_strtoi/overflow", - test_qemu_strtoi_overflow); - g_test_add_func("/cutils/qemu_strtoi/underflow", - test_qemu_strtoi_underflow); - g_test_add_func("/cutils/qemu_strtoi/negative", - test_qemu_strtoi_negative); - g_test_add_func("/cutils/qemu_strtoi_full/correct", - test_qemu_strtoi_full_correct); - g_test_add_func("/cutils/qemu_strtoi_full/null", - test_qemu_strtoi_full_null); - g_test_add_func("/cutils/qemu_strtoi_full/empty", - test_qemu_strtoi_full_empty); - g_test_add_func("/cutils/qemu_strtoi_full/negative", - test_qemu_strtoi_full_negative); - g_test_add_func("/cutils/qemu_strtoi_full/trailing", - test_qemu_strtoi_full_trailing); - g_test_add_func("/cutils/qemu_strtoi_full/max", - test_qemu_strtoi_full_max); - - /* qemu_strtoui() tests */ - g_test_add_func("/cutils/qemu_strtoui/correct", - test_qemu_strtoui_correct); - g_test_add_func("/cutils/qemu_strtoui/null", - test_qemu_strtoui_null); - g_test_add_func("/cutils/qemu_strtoui/empty", - test_qemu_strtoui_empty); - g_test_add_func("/cutils/qemu_strtoui/whitespace", - test_qemu_strtoui_whitespace); - g_test_add_func("/cutils/qemu_strtoui/invalid", - test_qemu_strtoui_invalid); - g_test_add_func("/cutils/qemu_strtoui/trailing", - test_qemu_strtoui_trailing); - g_test_add_func("/cutils/qemu_strtoui/octal", - test_qemu_strtoui_octal); - g_test_add_func("/cutils/qemu_strtoui/decimal", - test_qemu_strtoui_decimal); - g_test_add_func("/cutils/qemu_strtoui/hex", - test_qemu_strtoui_hex); - g_test_add_func("/cutils/qemu_strtoui/max", - test_qemu_strtoui_max); - g_test_add_func("/cutils/qemu_strtoui/overflow", - test_qemu_strtoui_overflow); - g_test_add_func("/cutils/qemu_strtoui/underflow", - test_qemu_strtoui_underflow); - g_test_add_func("/cutils/qemu_strtoui/negative", - test_qemu_strtoui_negative); - g_test_add_func("/cutils/qemu_strtoui_full/correct", - test_qemu_strtoui_full_correct); - g_test_add_func("/cutils/qemu_strtoui_full/null", - test_qemu_strtoui_full_null); - g_test_add_func("/cutils/qemu_strtoui_full/empty", - test_qemu_strtoui_full_empty); - g_test_add_func("/cutils/qemu_strtoui_full/negative", - test_qemu_strtoui_full_negative); - g_test_add_func("/cutils/qemu_strtoui_full/trailing", - test_qemu_strtoui_full_trailing); - g_test_add_func("/cutils/qemu_strtoui_full/max", - test_qemu_strtoui_full_max); - - /* qemu_strtol() tests */ - g_test_add_func("/cutils/qemu_strtol/correct", - test_qemu_strtol_correct); - g_test_add_func("/cutils/qemu_strtol/null", - test_qemu_strtol_null); - g_test_add_func("/cutils/qemu_strtol/empty", - test_qemu_strtol_empty); - g_test_add_func("/cutils/qemu_strtol/whitespace", - test_qemu_strtol_whitespace); - g_test_add_func("/cutils/qemu_strtol/invalid", - test_qemu_strtol_invalid); - g_test_add_func("/cutils/qemu_strtol/trailing", - test_qemu_strtol_trailing); - g_test_add_func("/cutils/qemu_strtol/octal", - test_qemu_strtol_octal); - g_test_add_func("/cutils/qemu_strtol/decimal", - test_qemu_strtol_decimal); - g_test_add_func("/cutils/qemu_strtol/hex", - test_qemu_strtol_hex); - g_test_add_func("/cutils/qemu_strtol/max", - test_qemu_strtol_max); - g_test_add_func("/cutils/qemu_strtol/overflow", - test_qemu_strtol_overflow); - g_test_add_func("/cutils/qemu_strtol/underflow", - test_qemu_strtol_underflow); - g_test_add_func("/cutils/qemu_strtol/negative", - test_qemu_strtol_negative); - g_test_add_func("/cutils/qemu_strtol_full/correct", - test_qemu_strtol_full_correct); - g_test_add_func("/cutils/qemu_strtol_full/null", - test_qemu_strtol_full_null); - g_test_add_func("/cutils/qemu_strtol_full/empty", - test_qemu_strtol_full_empty); - g_test_add_func("/cutils/qemu_strtol_full/negative", - test_qemu_strtol_full_negative); - g_test_add_func("/cutils/qemu_strtol_full/trailing", - test_qemu_strtol_full_trailing); - g_test_add_func("/cutils/qemu_strtol_full/max", - test_qemu_strtol_full_max); - - /* qemu_strtoul() tests */ - g_test_add_func("/cutils/qemu_strtoul/correct", - test_qemu_strtoul_correct); - g_test_add_func("/cutils/qemu_strtoul/null", - test_qemu_strtoul_null); - g_test_add_func("/cutils/qemu_strtoul/empty", - test_qemu_strtoul_empty); - g_test_add_func("/cutils/qemu_strtoul/whitespace", - test_qemu_strtoul_whitespace); - g_test_add_func("/cutils/qemu_strtoul/invalid", - test_qemu_strtoul_invalid); - g_test_add_func("/cutils/qemu_strtoul/trailing", - test_qemu_strtoul_trailing); - g_test_add_func("/cutils/qemu_strtoul/octal", - test_qemu_strtoul_octal); - g_test_add_func("/cutils/qemu_strtoul/decimal", - test_qemu_strtoul_decimal); - g_test_add_func("/cutils/qemu_strtoul/hex", - test_qemu_strtoul_hex); - g_test_add_func("/cutils/qemu_strtoul/max", - test_qemu_strtoul_max); - g_test_add_func("/cutils/qemu_strtoul/overflow", - test_qemu_strtoul_overflow); - g_test_add_func("/cutils/qemu_strtoul/underflow", - test_qemu_strtoul_underflow); - g_test_add_func("/cutils/qemu_strtoul/negative", - test_qemu_strtoul_negative); - g_test_add_func("/cutils/qemu_strtoul_full/correct", - test_qemu_strtoul_full_correct); - g_test_add_func("/cutils/qemu_strtoul_full/null", - test_qemu_strtoul_full_null); - g_test_add_func("/cutils/qemu_strtoul_full/empty", - test_qemu_strtoul_full_empty); - g_test_add_func("/cutils/qemu_strtoul_full/negative", - test_qemu_strtoul_full_negative); - g_test_add_func("/cutils/qemu_strtoul_full/trailing", - test_qemu_strtoul_full_trailing); - g_test_add_func("/cutils/qemu_strtoul_full/max", - test_qemu_strtoul_full_max); - - /* qemu_strtoi64() tests */ - g_test_add_func("/cutils/qemu_strtoi64/correct", - test_qemu_strtoi64_correct); - g_test_add_func("/cutils/qemu_strtoi64/null", - test_qemu_strtoi64_null); - g_test_add_func("/cutils/qemu_strtoi64/empty", - test_qemu_strtoi64_empty); - g_test_add_func("/cutils/qemu_strtoi64/whitespace", - test_qemu_strtoi64_whitespace); - g_test_add_func("/cutils/qemu_strtoi64/invalid" - , - test_qemu_strtoi64_invalid); - g_test_add_func("/cutils/qemu_strtoi64/trailing", - test_qemu_strtoi64_trailing); - g_test_add_func("/cutils/qemu_strtoi64/octal", - test_qemu_strtoi64_octal); - g_test_add_func("/cutils/qemu_strtoi64/decimal", - test_qemu_strtoi64_decimal); - g_test_add_func("/cutils/qemu_strtoi64/hex", - test_qemu_strtoi64_hex); - g_test_add_func("/cutils/qemu_strtoi64/max", - test_qemu_strtoi64_max); - g_test_add_func("/cutils/qemu_strtoi64/overflow", - test_qemu_strtoi64_overflow); - g_test_add_func("/cutils/qemu_strtoi64/underflow", - test_qemu_strtoi64_underflow); - g_test_add_func("/cutils/qemu_strtoi64/negative", - test_qemu_strtoi64_negative); - g_test_add_func("/cutils/qemu_strtoi64_full/correct", - test_qemu_strtoi64_full_correct); - g_test_add_func("/cutils/qemu_strtoi64_full/null", - test_qemu_strtoi64_full_null); - g_test_add_func("/cutils/qemu_strtoi64_full/empty", - test_qemu_strtoi64_full_empty); - g_test_add_func("/cutils/qemu_strtoi64_full/negative", - test_qemu_strtoi64_full_negative); - g_test_add_func("/cutils/qemu_strtoi64_full/trailing", - test_qemu_strtoi64_full_trailing); - g_test_add_func("/cutils/qemu_strtoi64_full/max", - test_qemu_strtoi64_full_max); - - /* qemu_strtou64() tests */ - g_test_add_func("/cutils/qemu_strtou64/correct", - test_qemu_strtou64_correct); - g_test_add_func("/cutils/qemu_strtou64/null", - test_qemu_strtou64_null); - g_test_add_func("/cutils/qemu_strtou64/empty", - test_qemu_strtou64_empty); - g_test_add_func("/cutils/qemu_strtou64/whitespace", - test_qemu_strtou64_whitespace); - g_test_add_func("/cutils/qemu_strtou64/invalid", - test_qemu_strtou64_invalid); - g_test_add_func("/cutils/qemu_strtou64/trailing", - test_qemu_strtou64_trailing); - g_test_add_func("/cutils/qemu_strtou64/octal", - test_qemu_strtou64_octal); - g_test_add_func("/cutils/qemu_strtou64/decimal", - test_qemu_strtou64_decimal); - g_test_add_func("/cutils/qemu_strtou64/hex", - test_qemu_strtou64_hex); - g_test_add_func("/cutils/qemu_strtou64/max", - test_qemu_strtou64_max); - g_test_add_func("/cutils/qemu_strtou64/overflow", - test_qemu_strtou64_overflow); - g_test_add_func("/cutils/qemu_strtou64/underflow", - test_qemu_strtou64_underflow); - g_test_add_func("/cutils/qemu_strtou64/negative", - test_qemu_strtou64_negative); - g_test_add_func("/cutils/qemu_strtou64_full/correct", - test_qemu_strtou64_full_correct); - g_test_add_func("/cutils/qemu_strtou64_full/null", - test_qemu_strtou64_full_null); - g_test_add_func("/cutils/qemu_strtou64_full/empty", - test_qemu_strtou64_full_empty); - g_test_add_func("/cutils/qemu_strtou64_full/negative", - test_qemu_strtou64_full_negative); - g_test_add_func("/cutils/qemu_strtou64_full/trailing", - test_qemu_strtou64_full_trailing); - g_test_add_func("/cutils/qemu_strtou64_full/max", - test_qemu_strtou64_full_max); - - g_test_add_func("/cutils/strtosz/simple", - test_qemu_strtosz_simple); - g_test_add_func("/cutils/strtosz/units", - test_qemu_strtosz_units); - g_test_add_func("/cutils/strtosz/float", - test_qemu_strtosz_float); - g_test_add_func("/cutils/strtosz/invalid", - test_qemu_strtosz_invalid); - g_test_add_func("/cutils/strtosz/trailing", - test_qemu_strtosz_trailing); - g_test_add_func("/cutils/strtosz/erange", - test_qemu_strtosz_erange); - g_test_add_func("/cutils/strtosz/metric", - test_qemu_strtosz_metric); - - return g_test_run(); -} diff --git a/tests/test-fdmon-epoll.c b/tests/test-fdmon-epoll.c deleted file mode 100644 index 11fd8a2fa9..0000000000 --- a/tests/test-fdmon-epoll.c +++ /dev/null @@ -1,73 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * fdmon-epoll tests - * - * Copyright (c) 2020 Red Hat, Inc. - */ - -#include "qemu/osdep.h" -#include "block/aio.h" -#include "qapi/error.h" -#include "qemu/main-loop.h" - -static AioContext *ctx; - -static void dummy_fd_handler(EventNotifier *notifier) -{ - event_notifier_test_and_clear(notifier); -} - -static void add_event_notifiers(EventNotifier *notifiers, size_t n) -{ - for (size_t i = 0; i < n; i++) { - event_notifier_init(¬ifiers[i], false); - aio_set_event_notifier(ctx, ¬ifiers[i], false, - dummy_fd_handler, NULL); - } -} - -static void remove_event_notifiers(EventNotifier *notifiers, size_t n) -{ - for (size_t i = 0; i < n; i++) { - aio_set_event_notifier(ctx, ¬ifiers[i], false, NULL, NULL); - event_notifier_cleanup(¬ifiers[i]); - } -} - -/* Check that fd handlers work when external clients are disabled */ -static void test_external_disabled(void) -{ - EventNotifier notifiers[100]; - - /* fdmon-epoll is only enabled when many fd handlers are registered */ - add_event_notifiers(notifiers, G_N_ELEMENTS(notifiers)); - - event_notifier_set(¬ifiers[0]); - assert(aio_poll(ctx, true)); - - aio_disable_external(ctx); - event_notifier_set(¬ifiers[0]); - assert(aio_poll(ctx, true)); - aio_enable_external(ctx); - - remove_event_notifiers(notifiers, G_N_ELEMENTS(notifiers)); -} - -int main(int argc, char **argv) -{ - /* - * This code relies on the fact that fdmon-io_uring disables itself when - * the glib main loop is in use. The main loop uses fdmon-poll and upgrades - * to fdmon-epoll when the number of fds exceeds a threshold. - */ - qemu_init_main_loop(&error_fatal); - ctx = qemu_get_aio_context(); - - while (g_main_context_iteration(NULL, false)) { - /* Do nothing */ - } - - g_test_init(&argc, &argv, NULL); - g_test_add_func("/fdmon-epoll/external-disabled", test_external_disabled); - return g_test_run(); -} diff --git a/tests/test-hbitmap.c b/tests/test-hbitmap.c deleted file mode 100644 index b6726cf76b..0000000000 --- a/tests/test-hbitmap.c +++ /dev/null @@ -1,1119 +0,0 @@ -/* - * Hierarchical bitmap unit-tests. - * - * Copyright (C) 2012 Red Hat Inc. - * - * Author: Paolo Bonzini - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "qemu/hbitmap.h" -#include "qemu/bitmap.h" -#include "block/block.h" - -#define LOG_BITS_PER_LONG (BITS_PER_LONG == 32 ? 5 : 6) - -#define L1 BITS_PER_LONG -#define L2 (BITS_PER_LONG * L1) -#define L3 (BITS_PER_LONG * L2) - -typedef struct TestHBitmapData { - HBitmap *hb; - unsigned long *bits; - size_t size; - size_t old_size; - int granularity; -} TestHBitmapData; - - -/* Check that the HBitmap and the shadow bitmap contain the same data, - * ignoring the same "first" bits. - */ -static void hbitmap_test_check(TestHBitmapData *data, - uint64_t first) -{ - uint64_t count = 0; - size_t pos; - int bit; - HBitmapIter hbi; - int64_t i, next; - - hbitmap_iter_init(&hbi, data->hb, first); - - i = first; - for (;;) { - next = hbitmap_iter_next(&hbi); - if (next < 0) { - next = data->size; - } - - while (i < next) { - pos = i >> LOG_BITS_PER_LONG; - bit = i & (BITS_PER_LONG - 1); - i++; - g_assert_cmpint(data->bits[pos] & (1UL << bit), ==, 0); - } - - if (next == data->size) { - break; - } - - pos = i >> LOG_BITS_PER_LONG; - bit = i & (BITS_PER_LONG - 1); - i++; - count++; - g_assert_cmpint(data->bits[pos] & (1UL << bit), !=, 0); - } - - if (first == 0) { - g_assert_cmpint(count << data->granularity, ==, hbitmap_count(data->hb)); - } -} - -/* This is provided instead of a test setup function so that the sizes - are kept in the test functions (and not in main()) */ -static void hbitmap_test_init(TestHBitmapData *data, - uint64_t size, int granularity) -{ - size_t n; - data->hb = hbitmap_alloc(size, granularity); - - n = DIV_ROUND_UP(size, BITS_PER_LONG); - if (n == 0) { - n = 1; - } - data->bits = g_new0(unsigned long, n); - data->size = size; - data->granularity = granularity; - if (size) { - hbitmap_test_check(data, 0); - } -} - -static inline size_t hbitmap_test_array_size(size_t bits) -{ - size_t n = DIV_ROUND_UP(bits, BITS_PER_LONG); - return n ? n : 1; -} - -static void hbitmap_test_truncate_impl(TestHBitmapData *data, - size_t size) -{ - size_t n; - size_t m; - data->old_size = data->size; - data->size = size; - - if (data->size == data->old_size) { - return; - } - - n = hbitmap_test_array_size(size); - m = hbitmap_test_array_size(data->old_size); - data->bits = g_realloc(data->bits, sizeof(unsigned long) * n); - if (n > m) { - memset(&data->bits[m], 0x00, sizeof(unsigned long) * (n - m)); - } - - /* If we shrink to an uneven multiple of sizeof(unsigned long), - * scrub the leftover memory. */ - if (data->size < data->old_size) { - m = size % (sizeof(unsigned long) * 8); - if (m) { - unsigned long mask = (1ULL << m) - 1; - data->bits[n-1] &= mask; - } - } - - hbitmap_truncate(data->hb, size); -} - -static void hbitmap_test_teardown(TestHBitmapData *data, - const void *unused) -{ - if (data->hb) { - hbitmap_free(data->hb); - data->hb = NULL; - } - g_free(data->bits); - data->bits = NULL; -} - -/* Set a range in the HBitmap and in the shadow "simple" bitmap. - * The two bitmaps are then tested against each other. - */ -static void hbitmap_test_set(TestHBitmapData *data, - uint64_t first, uint64_t count) -{ - hbitmap_set(data->hb, first, count); - while (count-- != 0) { - size_t pos = first >> LOG_BITS_PER_LONG; - int bit = first & (BITS_PER_LONG - 1); - first++; - - data->bits[pos] |= 1UL << bit; - } - - if (data->granularity == 0) { - hbitmap_test_check(data, 0); - } -} - -/* Reset a range in the HBitmap and in the shadow "simple" bitmap. - */ -static void hbitmap_test_reset(TestHBitmapData *data, - uint64_t first, uint64_t count) -{ - hbitmap_reset(data->hb, first, count); - while (count-- != 0) { - size_t pos = first >> LOG_BITS_PER_LONG; - int bit = first & (BITS_PER_LONG - 1); - first++; - - data->bits[pos] &= ~(1UL << bit); - } - - if (data->granularity == 0) { - hbitmap_test_check(data, 0); - } -} - -static void hbitmap_test_reset_all(TestHBitmapData *data) -{ - size_t n; - - hbitmap_reset_all(data->hb); - - n = DIV_ROUND_UP(data->size, BITS_PER_LONG); - if (n == 0) { - n = 1; - } - memset(data->bits, 0, n * sizeof(unsigned long)); - - if (data->granularity == 0) { - hbitmap_test_check(data, 0); - } -} - -static void hbitmap_test_check_get(TestHBitmapData *data) -{ - uint64_t count = 0; - uint64_t i; - - for (i = 0; i < data->size; i++) { - size_t pos = i >> LOG_BITS_PER_LONG; - int bit = i & (BITS_PER_LONG - 1); - unsigned long val = data->bits[pos] & (1UL << bit); - count += hbitmap_get(data->hb, i); - g_assert_cmpint(hbitmap_get(data->hb, i), ==, val != 0); - } - g_assert_cmpint(count, ==, hbitmap_count(data->hb)); -} - -static void test_hbitmap_zero(TestHBitmapData *data, - const void *unused) -{ - hbitmap_test_init(data, 0, 0); -} - -static void test_hbitmap_unaligned(TestHBitmapData *data, - const void *unused) -{ - hbitmap_test_init(data, L3 + 23, 0); - hbitmap_test_set(data, 0, 1); - hbitmap_test_set(data, L3 + 22, 1); -} - -static void test_hbitmap_iter_empty(TestHBitmapData *data, - const void *unused) -{ - hbitmap_test_init(data, L1, 0); -} - -static void test_hbitmap_iter_partial(TestHBitmapData *data, - const void *unused) -{ - hbitmap_test_init(data, L3, 0); - hbitmap_test_set(data, 0, L3); - hbitmap_test_check(data, 1); - hbitmap_test_check(data, L1 - 1); - hbitmap_test_check(data, L1); - hbitmap_test_check(data, L1 * 2 - 1); - hbitmap_test_check(data, L2 - 1); - hbitmap_test_check(data, L2); - hbitmap_test_check(data, L2 + 1); - hbitmap_test_check(data, L2 + L1); - hbitmap_test_check(data, L2 + L1 * 2 - 1); - hbitmap_test_check(data, L2 * 2 - 1); - hbitmap_test_check(data, L2 * 2); - hbitmap_test_check(data, L2 * 2 + 1); - hbitmap_test_check(data, L2 * 2 + L1); - hbitmap_test_check(data, L2 * 2 + L1 * 2 - 1); - hbitmap_test_check(data, L3 / 2); -} - -static void test_hbitmap_set_all(TestHBitmapData *data, - const void *unused) -{ - hbitmap_test_init(data, L3, 0); - hbitmap_test_set(data, 0, L3); -} - -static void test_hbitmap_get_all(TestHBitmapData *data, - const void *unused) -{ - hbitmap_test_init(data, L3, 0); - hbitmap_test_set(data, 0, L3); - hbitmap_test_check_get(data); -} - -static void test_hbitmap_get_some(TestHBitmapData *data, - const void *unused) -{ - hbitmap_test_init(data, 2 * L2, 0); - hbitmap_test_set(data, 10, 1); - hbitmap_test_check_get(data); - hbitmap_test_set(data, L1 - 1, 1); - hbitmap_test_check_get(data); - hbitmap_test_set(data, L1, 1); - hbitmap_test_check_get(data); - hbitmap_test_set(data, L2 - 1, 1); - hbitmap_test_check_get(data); - hbitmap_test_set(data, L2, 1); - hbitmap_test_check_get(data); -} - -static void test_hbitmap_set_one(TestHBitmapData *data, - const void *unused) -{ - hbitmap_test_init(data, 2 * L2, 0); - hbitmap_test_set(data, 10, 1); - hbitmap_test_set(data, L1 - 1, 1); - hbitmap_test_set(data, L1, 1); - hbitmap_test_set(data, L2 - 1, 1); - hbitmap_test_set(data, L2, 1); -} - -static void test_hbitmap_set_two_elem(TestHBitmapData *data, - const void *unused) -{ - hbitmap_test_init(data, 2 * L2, 0); - hbitmap_test_set(data, L1 - 1, 2); - hbitmap_test_set(data, L1 * 2 - 1, 4); - hbitmap_test_set(data, L1 * 4, L1 + 1); - hbitmap_test_set(data, L1 * 8 - 1, L1 + 1); - hbitmap_test_set(data, L2 - 1, 2); - hbitmap_test_set(data, L2 + L1 - 1, 8); - hbitmap_test_set(data, L2 + L1 * 4, L1 + 1); - hbitmap_test_set(data, L2 + L1 * 8 - 1, L1 + 1); -} - -static void test_hbitmap_set(TestHBitmapData *data, - const void *unused) -{ - hbitmap_test_init(data, L3 * 2, 0); - hbitmap_test_set(data, L1 - 1, L1 + 2); - hbitmap_test_set(data, L1 * 3 - 1, L1 + 2); - hbitmap_test_set(data, L1 * 5, L1 * 2 + 1); - hbitmap_test_set(data, L1 * 8 - 1, L1 * 2 + 1); - hbitmap_test_set(data, L2 - 1, L1 + 2); - hbitmap_test_set(data, L2 + L1 * 2 - 1, L1 + 2); - hbitmap_test_set(data, L2 + L1 * 4, L1 * 2 + 1); - hbitmap_test_set(data, L2 + L1 * 7 - 1, L1 * 2 + 1); - hbitmap_test_set(data, L2 * 2 - 1, L3 * 2 - L2 * 2); -} - -static void test_hbitmap_set_twice(TestHBitmapData *data, - const void *unused) -{ - hbitmap_test_init(data, L1 * 3, 0); - hbitmap_test_set(data, 0, L1 * 3); - hbitmap_test_set(data, L1, 1); -} - -static void test_hbitmap_set_overlap(TestHBitmapData *data, - const void *unused) -{ - hbitmap_test_init(data, L3 * 2, 0); - hbitmap_test_set(data, L1 - 1, L1 + 2); - hbitmap_test_set(data, L1 * 2 - 1, L1 * 2 + 2); - hbitmap_test_set(data, 0, L1 * 3); - hbitmap_test_set(data, L1 * 8 - 1, L2); - hbitmap_test_set(data, L2, L1); - hbitmap_test_set(data, L2 - L1 - 1, L1 * 8 + 2); - hbitmap_test_set(data, L2, L3 - L2 + 1); - hbitmap_test_set(data, L3 - L1, L1 * 3); - hbitmap_test_set(data, L3 - 1, 3); - hbitmap_test_set(data, L3 - 1, L2); -} - -static void test_hbitmap_reset_empty(TestHBitmapData *data, - const void *unused) -{ - hbitmap_test_init(data, L3, 0); - hbitmap_test_reset(data, 0, L3); -} - -static void test_hbitmap_reset(TestHBitmapData *data, - const void *unused) -{ - hbitmap_test_init(data, L3 * 2, 0); - hbitmap_test_set(data, L1 - 1, L1 + 2); - hbitmap_test_reset(data, L1 * 2 - 1, L1 * 2 + 2); - hbitmap_test_set(data, 0, L1 * 3); - hbitmap_test_reset(data, L1 * 8 - 1, L2); - hbitmap_test_set(data, L2, L1); - hbitmap_test_reset(data, L2 - L1 - 1, L1 * 8 + 2); - hbitmap_test_set(data, L2, L3 - L2 + 1); - hbitmap_test_reset(data, L3 - L1, L1 * 3); - hbitmap_test_set(data, L3 - 1, 3); - hbitmap_test_reset(data, L3 - 1, L2); - hbitmap_test_set(data, 0, L3 * 2); - hbitmap_test_reset(data, 0, L1); - hbitmap_test_reset(data, 0, L2); - hbitmap_test_reset(data, L3, L3); - hbitmap_test_set(data, L3 / 2, L3); -} - -static void test_hbitmap_reset_all(TestHBitmapData *data, - const void *unused) -{ - hbitmap_test_init(data, L3 * 2, 0); - hbitmap_test_set(data, L1 - 1, L1 + 2); - hbitmap_test_reset_all(data); - hbitmap_test_set(data, 0, L1 * 3); - hbitmap_test_reset_all(data); - hbitmap_test_set(data, L2, L1); - hbitmap_test_reset_all(data); - hbitmap_test_set(data, L2, L3 - L2 + 1); - hbitmap_test_reset_all(data); - hbitmap_test_set(data, L3 - 1, 3); - hbitmap_test_reset_all(data); - hbitmap_test_set(data, 0, L3 * 2); - hbitmap_test_reset_all(data); - hbitmap_test_set(data, L3 / 2, L3); - hbitmap_test_reset_all(data); -} - -static void test_hbitmap_granularity(TestHBitmapData *data, - const void *unused) -{ - /* Note that hbitmap_test_check has to be invoked manually in this test. */ - hbitmap_test_init(data, L1, 1); - hbitmap_test_set(data, 0, 1); - g_assert_cmpint(hbitmap_count(data->hb), ==, 2); - hbitmap_test_check(data, 0); - hbitmap_test_set(data, 2, 1); - g_assert_cmpint(hbitmap_count(data->hb), ==, 4); - hbitmap_test_check(data, 0); - hbitmap_test_set(data, 0, 3); - g_assert_cmpint(hbitmap_count(data->hb), ==, 4); - hbitmap_test_reset(data, 0, 2); - g_assert_cmpint(hbitmap_count(data->hb), ==, 2); -} - -static void test_hbitmap_iter_granularity(TestHBitmapData *data, - const void *unused) -{ - HBitmapIter hbi; - - /* Note that hbitmap_test_check has to be invoked manually in this test. */ - hbitmap_test_init(data, 131072 << 7, 7); - hbitmap_iter_init(&hbi, data->hb, 0); - g_assert_cmpint(hbitmap_iter_next(&hbi), <, 0); - - hbitmap_test_set(data, ((L2 + L1 + 1) << 7) + 8, 8); - hbitmap_iter_init(&hbi, data->hb, 0); - g_assert_cmpint(hbitmap_iter_next(&hbi), ==, (L2 + L1 + 1) << 7); - g_assert_cmpint(hbitmap_iter_next(&hbi), <, 0); - - hbitmap_iter_init(&hbi, data->hb, (L2 + L1 + 2) << 7); - g_assert_cmpint(hbitmap_iter_next(&hbi), <, 0); - - hbitmap_test_set(data, (131072 << 7) - 8, 8); - hbitmap_iter_init(&hbi, data->hb, 0); - g_assert_cmpint(hbitmap_iter_next(&hbi), ==, (L2 + L1 + 1) << 7); - g_assert_cmpint(hbitmap_iter_next(&hbi), ==, 131071 << 7); - g_assert_cmpint(hbitmap_iter_next(&hbi), <, 0); - - hbitmap_iter_init(&hbi, data->hb, (L2 + L1 + 2) << 7); - g_assert_cmpint(hbitmap_iter_next(&hbi), ==, 131071 << 7); - g_assert_cmpint(hbitmap_iter_next(&hbi), <, 0); -} - -static void hbitmap_test_set_boundary_bits(TestHBitmapData *data, ssize_t diff) -{ - size_t size = data->size; - - /* First bit */ - hbitmap_test_set(data, 0, 1); - if (diff < 0) { - /* Last bit in new, shortened map */ - hbitmap_test_set(data, size + diff - 1, 1); - - /* First bit to be truncated away */ - hbitmap_test_set(data, size + diff, 1); - } - /* Last bit */ - hbitmap_test_set(data, size - 1, 1); - if (data->granularity == 0) { - hbitmap_test_check_get(data); - } -} - -static void hbitmap_test_check_boundary_bits(TestHBitmapData *data) -{ - size_t size = MIN(data->size, data->old_size); - - if (data->granularity == 0) { - hbitmap_test_check_get(data); - hbitmap_test_check(data, 0); - } else { - /* If a granularity was set, note that every distinct - * (bit >> granularity) value that was set will increase - * the bit pop count by 2^granularity, not just 1. - * - * The hbitmap_test_check facility does not currently tolerate - * non-zero granularities, so test the boundaries and the population - * count manually. - */ - g_assert(hbitmap_get(data->hb, 0)); - g_assert(hbitmap_get(data->hb, size - 1)); - g_assert_cmpint(2 << data->granularity, ==, hbitmap_count(data->hb)); - } -} - -/* Generic truncate test. */ -static void hbitmap_test_truncate(TestHBitmapData *data, - size_t size, - ssize_t diff, - int granularity) -{ - hbitmap_test_init(data, size, granularity); - hbitmap_test_set_boundary_bits(data, diff); - hbitmap_test_truncate_impl(data, size + diff); - hbitmap_test_check_boundary_bits(data); -} - -static void test_hbitmap_truncate_nop(TestHBitmapData *data, - const void *unused) -{ - hbitmap_test_truncate(data, L2, 0, 0); -} - -/** - * Grow by an amount smaller than the granularity, without crossing - * a granularity alignment boundary. Effectively a NOP. - */ -static void test_hbitmap_truncate_grow_negligible(TestHBitmapData *data, - const void *unused) -{ - size_t size = L2 - 1; - size_t diff = 1; - int granularity = 1; - - hbitmap_test_truncate(data, size, diff, granularity); -} - -/** - * Shrink by an amount smaller than the granularity, without crossing - * a granularity alignment boundary. Effectively a NOP. - */ -static void test_hbitmap_truncate_shrink_negligible(TestHBitmapData *data, - const void *unused) -{ - size_t size = L2; - ssize_t diff = -1; - int granularity = 1; - - hbitmap_test_truncate(data, size, diff, granularity); -} - -/** - * Grow by an amount smaller than the granularity, but crossing over - * a granularity alignment boundary. - */ -static void test_hbitmap_truncate_grow_tiny(TestHBitmapData *data, - const void *unused) -{ - size_t size = L2 - 2; - ssize_t diff = 1; - int granularity = 1; - - hbitmap_test_truncate(data, size, diff, granularity); -} - -/** - * Shrink by an amount smaller than the granularity, but crossing over - * a granularity alignment boundary. - */ -static void test_hbitmap_truncate_shrink_tiny(TestHBitmapData *data, - const void *unused) -{ - size_t size = L2 - 1; - ssize_t diff = -1; - int granularity = 1; - - hbitmap_test_truncate(data, size, diff, granularity); -} - -/** - * Grow by an amount smaller than sizeof(long), and not crossing over - * a sizeof(long) alignment boundary. - */ -static void test_hbitmap_truncate_grow_small(TestHBitmapData *data, - const void *unused) -{ - size_t size = L2 + 1; - size_t diff = sizeof(long) / 2; - - hbitmap_test_truncate(data, size, diff, 0); -} - -/** - * Shrink by an amount smaller than sizeof(long), and not crossing over - * a sizeof(long) alignment boundary. - */ -static void test_hbitmap_truncate_shrink_small(TestHBitmapData *data, - const void *unused) -{ - size_t size = L2; - size_t diff = sizeof(long) / 2; - - hbitmap_test_truncate(data, size, -diff, 0); -} - -/** - * Grow by an amount smaller than sizeof(long), while crossing over - * a sizeof(long) alignment boundary. - */ -static void test_hbitmap_truncate_grow_medium(TestHBitmapData *data, - const void *unused) -{ - size_t size = L2 - 1; - size_t diff = sizeof(long) / 2; - - hbitmap_test_truncate(data, size, diff, 0); -} - -/** - * Shrink by an amount smaller than sizeof(long), while crossing over - * a sizeof(long) alignment boundary. - */ -static void test_hbitmap_truncate_shrink_medium(TestHBitmapData *data, - const void *unused) -{ - size_t size = L2 + 1; - size_t diff = sizeof(long) / 2; - - hbitmap_test_truncate(data, size, -diff, 0); -} - -/** - * Grow by an amount larger than sizeof(long). - */ -static void test_hbitmap_truncate_grow_large(TestHBitmapData *data, - const void *unused) -{ - size_t size = L2; - size_t diff = 8 * sizeof(long); - - hbitmap_test_truncate(data, size, diff, 0); -} - -/** - * Shrink by an amount larger than sizeof(long). - */ -static void test_hbitmap_truncate_shrink_large(TestHBitmapData *data, - const void *unused) -{ - size_t size = L2; - size_t diff = 8 * sizeof(long); - - hbitmap_test_truncate(data, size, -diff, 0); -} - -static void test_hbitmap_serialize_align(TestHBitmapData *data, - const void *unused) -{ - int r; - - hbitmap_test_init(data, L3 * 2, 3); - g_assert(hbitmap_is_serializable(data->hb)); - - r = hbitmap_serialization_align(data->hb); - g_assert_cmpint(r, ==, 64 << 3); -} - -static void hbitmap_test_serialize_range(TestHBitmapData *data, - uint8_t *buf, size_t buf_size, - uint64_t pos, uint64_t count) -{ - size_t i; - unsigned long *el = (unsigned long *)buf; - - assert(hbitmap_granularity(data->hb) == 0); - hbitmap_reset_all(data->hb); - memset(buf, 0, buf_size); - if (count) { - hbitmap_set(data->hb, pos, count); - } - - g_assert(hbitmap_is_serializable(data->hb)); - hbitmap_serialize_part(data->hb, buf, 0, data->size); - - /* Serialized buffer is inherently LE, convert it back manually to test */ - for (i = 0; i < buf_size / sizeof(unsigned long); i++) { - el[i] = (BITS_PER_LONG == 32 ? le32_to_cpu(el[i]) : le64_to_cpu(el[i])); - } - - for (i = 0; i < data->size; i++) { - int is_set = test_bit(i, (unsigned long *)buf); - if (i >= pos && i < pos + count) { - g_assert(is_set); - } else { - g_assert(!is_set); - } - } - - /* Re-serialize for deserialization testing */ - memset(buf, 0, buf_size); - hbitmap_serialize_part(data->hb, buf, 0, data->size); - hbitmap_reset_all(data->hb); - - g_assert(hbitmap_is_serializable(data->hb)); - hbitmap_deserialize_part(data->hb, buf, 0, data->size, true); - - for (i = 0; i < data->size; i++) { - int is_set = hbitmap_get(data->hb, i); - if (i >= pos && i < pos + count) { - g_assert(is_set); - } else { - g_assert(!is_set); - } - } -} - -static void test_hbitmap_serialize_basic(TestHBitmapData *data, - const void *unused) -{ - int i, j; - size_t buf_size; - uint8_t *buf; - uint64_t positions[] = { 0, 1, L1 - 1, L1, L2 - 1, L2, L2 + 1, L3 - 1 }; - int num_positions = ARRAY_SIZE(positions); - - hbitmap_test_init(data, L3, 0); - g_assert(hbitmap_is_serializable(data->hb)); - buf_size = hbitmap_serialization_size(data->hb, 0, data->size); - buf = g_malloc0(buf_size); - - for (i = 0; i < num_positions; i++) { - for (j = 0; j < num_positions; j++) { - hbitmap_test_serialize_range(data, buf, buf_size, - positions[i], - MIN(positions[j], L3 - positions[i])); - } - } - - g_free(buf); -} - -static void test_hbitmap_serialize_part(TestHBitmapData *data, - const void *unused) -{ - int i, j, k; - size_t buf_size; - uint8_t *buf; - uint64_t positions[] = { 0, 1, L1 - 1, L1, L2 - 1, L2, L2 + 1, L3 - 1 }; - int num_positions = ARRAY_SIZE(positions); - - hbitmap_test_init(data, L3, 0); - buf_size = L2; - buf = g_malloc0(buf_size); - - for (i = 0; i < num_positions; i++) { - hbitmap_set(data->hb, positions[i], 1); - } - - g_assert(hbitmap_is_serializable(data->hb)); - - for (i = 0; i < data->size; i += buf_size) { - unsigned long *el = (unsigned long *)buf; - hbitmap_serialize_part(data->hb, buf, i, buf_size); - for (j = 0; j < buf_size / sizeof(unsigned long); j++) { - el[j] = (BITS_PER_LONG == 32 ? le32_to_cpu(el[j]) : le64_to_cpu(el[j])); - } - - for (j = 0; j < buf_size; j++) { - bool should_set = false; - for (k = 0; k < num_positions; k++) { - if (positions[k] == j + i) { - should_set = true; - break; - } - } - g_assert_cmpint(should_set, ==, test_bit(j, (unsigned long *)buf)); - } - } - - g_free(buf); -} - -static void test_hbitmap_serialize_zeroes(TestHBitmapData *data, - const void *unused) -{ - int i; - HBitmapIter iter; - int64_t next; - uint64_t min_l1 = MAX(L1, 64); - uint64_t positions[] = { 0, min_l1, L2, L3 - min_l1}; - int num_positions = ARRAY_SIZE(positions); - - hbitmap_test_init(data, L3, 0); - - for (i = 0; i < num_positions; i++) { - hbitmap_set(data->hb, positions[i], L1); - } - - g_assert(hbitmap_is_serializable(data->hb)); - - for (i = 0; i < num_positions; i++) { - hbitmap_deserialize_zeroes(data->hb, positions[i], min_l1, true); - hbitmap_iter_init(&iter, data->hb, 0); - next = hbitmap_iter_next(&iter); - if (i == num_positions - 1) { - g_assert_cmpint(next, ==, -1); - } else { - g_assert_cmpint(next, ==, positions[i + 1]); - } - } -} - -static void hbitmap_test_add(const char *testpath, - void (*test_func)(TestHBitmapData *data, const void *user_data)) -{ - g_test_add(testpath, TestHBitmapData, NULL, NULL, test_func, - hbitmap_test_teardown); -} - -static void test_hbitmap_iter_and_reset(TestHBitmapData *data, - const void *unused) -{ - HBitmapIter hbi; - - hbitmap_test_init(data, L1 * 2, 0); - hbitmap_set(data->hb, 0, data->size); - - hbitmap_iter_init(&hbi, data->hb, BITS_PER_LONG - 1); - - hbitmap_iter_next(&hbi); - - hbitmap_reset_all(data->hb); - hbitmap_iter_next(&hbi); -} - -static void test_hbitmap_next_x_check_range(TestHBitmapData *data, - int64_t start, - int64_t count) -{ - int64_t next_zero = hbitmap_next_zero(data->hb, start, count); - int64_t next_dirty = hbitmap_next_dirty(data->hb, start, count); - int64_t next; - int64_t end = start >= data->size || data->size - start < count ? - data->size : start + count; - bool first_bit = hbitmap_get(data->hb, start); - - for (next = start; - next < end && hbitmap_get(data->hb, next) == first_bit; - next++) - { - ; - } - - if (next == end) { - next = -1; - } - - g_assert_cmpint(next_dirty, ==, first_bit ? start : next); - g_assert_cmpint(next_zero, ==, first_bit ? next : start); -} - -static void test_hbitmap_next_x_check(TestHBitmapData *data, int64_t start) -{ - test_hbitmap_next_x_check_range(data, start, INT64_MAX); -} - -static void test_hbitmap_next_x_do(TestHBitmapData *data, int granularity) -{ - hbitmap_test_init(data, L3, granularity); - test_hbitmap_next_x_check(data, 0); - test_hbitmap_next_x_check(data, L3 - 1); - test_hbitmap_next_x_check_range(data, 0, 1); - test_hbitmap_next_x_check_range(data, L3 - 1, 1); - - hbitmap_set(data->hb, L2, 1); - test_hbitmap_next_x_check(data, 0); - test_hbitmap_next_x_check(data, L2 - 1); - test_hbitmap_next_x_check(data, L2); - test_hbitmap_next_x_check(data, L2 + 1); - test_hbitmap_next_x_check_range(data, 0, 1); - test_hbitmap_next_x_check_range(data, 0, L2); - test_hbitmap_next_x_check_range(data, L2 - 1, 1); - test_hbitmap_next_x_check_range(data, L2 - 1, 2); - test_hbitmap_next_x_check_range(data, L2, 1); - test_hbitmap_next_x_check_range(data, L2 + 1, 1); - - hbitmap_set(data->hb, L2 + 5, L1); - test_hbitmap_next_x_check(data, 0); - test_hbitmap_next_x_check(data, L2 - L1); - test_hbitmap_next_x_check(data, L2 + 1); - test_hbitmap_next_x_check(data, L2 + 2); - test_hbitmap_next_x_check(data, L2 + 5); - test_hbitmap_next_x_check(data, L2 + L1 - 1); - test_hbitmap_next_x_check(data, L2 + L1); - test_hbitmap_next_x_check(data, L2 + L1 + 1); - test_hbitmap_next_x_check_range(data, L2 - 2, L1); - test_hbitmap_next_x_check_range(data, L2, 4); - test_hbitmap_next_x_check_range(data, L2, 6); - test_hbitmap_next_x_check_range(data, L2 + 1, 3); - test_hbitmap_next_x_check_range(data, L2 + 4, L1); - test_hbitmap_next_x_check_range(data, L2 + 5, L1); - test_hbitmap_next_x_check_range(data, L2 + 5 + L1 - 1, 1); - test_hbitmap_next_x_check_range(data, L2 + 5 + L1, 1); - test_hbitmap_next_x_check_range(data, L2 + 5 + L1 + 1, 1); - - hbitmap_set(data->hb, L2 * 2, L3 - L2 * 2); - test_hbitmap_next_x_check(data, L2 * 2 - L1); - test_hbitmap_next_x_check(data, L2 * 2 - 2); - test_hbitmap_next_x_check(data, L2 * 2 - 1); - test_hbitmap_next_x_check(data, L2 * 2); - test_hbitmap_next_x_check(data, L2 * 2 + 1); - test_hbitmap_next_x_check(data, L2 * 2 + L1); - test_hbitmap_next_x_check(data, L3 - 1); - test_hbitmap_next_x_check_range(data, L2 * 2 - L1, L1 + 1); - test_hbitmap_next_x_check_range(data, L2 * 2, L2); - - hbitmap_set(data->hb, 0, L3); - test_hbitmap_next_x_check(data, 0); -} - -static void test_hbitmap_next_x_0(TestHBitmapData *data, const void *unused) -{ - test_hbitmap_next_x_do(data, 0); -} - -static void test_hbitmap_next_x_4(TestHBitmapData *data, const void *unused) -{ - test_hbitmap_next_x_do(data, 4); -} - -static void test_hbitmap_next_x_after_truncate(TestHBitmapData *data, - const void *unused) -{ - hbitmap_test_init(data, L1, 0); - hbitmap_test_truncate_impl(data, L1 * 2); - hbitmap_set(data->hb, 0, L1); - test_hbitmap_next_x_check(data, 0); -} - -static void test_hbitmap_next_dirty_area_check_limited(TestHBitmapData *data, - int64_t offset, - int64_t count, - int64_t max_dirty) -{ - int64_t off1, off2; - int64_t len1 = 0, len2; - bool ret1, ret2; - int64_t end; - - ret1 = hbitmap_next_dirty_area(data->hb, - offset, count == INT64_MAX ? INT64_MAX : offset + count, max_dirty, - &off1, &len1); - - end = offset > data->size || data->size - offset < count ? data->size : - offset + count; - - for (off2 = offset; off2 < end && !hbitmap_get(data->hb, off2); off2++) { - ; - } - - for (len2 = 1; (off2 + len2 < end && len2 < max_dirty && - hbitmap_get(data->hb, off2 + len2)); len2++) - { - ; - } - - ret2 = off2 < end; - g_assert_cmpint(ret1, ==, ret2); - - if (ret2) { - g_assert_cmpint(off1, ==, off2); - g_assert_cmpint(len1, ==, len2); - } -} - -static void test_hbitmap_next_dirty_area_check(TestHBitmapData *data, - int64_t offset, int64_t count) -{ - test_hbitmap_next_dirty_area_check_limited(data, offset, count, INT64_MAX); -} - -static void test_hbitmap_next_dirty_area_do(TestHBitmapData *data, - int granularity) -{ - hbitmap_test_init(data, L3, granularity); - test_hbitmap_next_dirty_area_check(data, 0, INT64_MAX); - test_hbitmap_next_dirty_area_check(data, 0, 1); - test_hbitmap_next_dirty_area_check(data, L3 - 1, 1); - test_hbitmap_next_dirty_area_check_limited(data, 0, INT64_MAX, 1); - - hbitmap_set(data->hb, L2, 1); - test_hbitmap_next_dirty_area_check(data, 0, 1); - test_hbitmap_next_dirty_area_check(data, 0, L2); - test_hbitmap_next_dirty_area_check(data, 0, INT64_MAX); - test_hbitmap_next_dirty_area_check(data, L2 - 1, INT64_MAX); - test_hbitmap_next_dirty_area_check(data, L2 - 1, 1); - test_hbitmap_next_dirty_area_check(data, L2 - 1, 2); - test_hbitmap_next_dirty_area_check(data, L2 - 1, 3); - test_hbitmap_next_dirty_area_check(data, L2, INT64_MAX); - test_hbitmap_next_dirty_area_check(data, L2, 1); - test_hbitmap_next_dirty_area_check(data, L2 + 1, 1); - test_hbitmap_next_dirty_area_check_limited(data, 0, INT64_MAX, 1); - test_hbitmap_next_dirty_area_check_limited(data, L2 - 1, 2, 1); - - hbitmap_set(data->hb, L2 + 5, L1); - test_hbitmap_next_dirty_area_check(data, 0, INT64_MAX); - test_hbitmap_next_dirty_area_check(data, L2 - 2, 8); - test_hbitmap_next_dirty_area_check(data, L2 + 1, 5); - test_hbitmap_next_dirty_area_check(data, L2 + 1, 3); - test_hbitmap_next_dirty_area_check(data, L2 + 4, L1); - test_hbitmap_next_dirty_area_check(data, L2 + 5, L1); - test_hbitmap_next_dirty_area_check(data, L2 + 7, L1); - test_hbitmap_next_dirty_area_check(data, L2 + L1, L1); - test_hbitmap_next_dirty_area_check(data, L2, 0); - test_hbitmap_next_dirty_area_check(data, L2 + 1, 0); - test_hbitmap_next_dirty_area_check_limited(data, L2 + 3, INT64_MAX, 3); - test_hbitmap_next_dirty_area_check_limited(data, L2 + 3, 7, 10); - - hbitmap_set(data->hb, L2 * 2, L3 - L2 * 2); - test_hbitmap_next_dirty_area_check(data, 0, INT64_MAX); - test_hbitmap_next_dirty_area_check(data, L2, INT64_MAX); - test_hbitmap_next_dirty_area_check(data, L2 + 1, INT64_MAX); - test_hbitmap_next_dirty_area_check(data, L2 + 5 + L1 - 1, INT64_MAX); - test_hbitmap_next_dirty_area_check(data, L2 + 5 + L1, 5); - test_hbitmap_next_dirty_area_check(data, L2 * 2 - L1, L1 + 1); - test_hbitmap_next_dirty_area_check(data, L2 * 2, L2); - test_hbitmap_next_dirty_area_check_limited(data, L2 * 2 + 1, INT64_MAX, 5); - test_hbitmap_next_dirty_area_check_limited(data, L2 * 2 + 1, 10, 5); - test_hbitmap_next_dirty_area_check_limited(data, L2 * 2 + 1, 2, 5); - - hbitmap_set(data->hb, 0, L3); - test_hbitmap_next_dirty_area_check(data, 0, INT64_MAX); -} - -static void test_hbitmap_next_dirty_area_0(TestHBitmapData *data, - const void *unused) -{ - test_hbitmap_next_dirty_area_do(data, 0); -} - -static void test_hbitmap_next_dirty_area_1(TestHBitmapData *data, - const void *unused) -{ - test_hbitmap_next_dirty_area_do(data, 1); -} - -static void test_hbitmap_next_dirty_area_4(TestHBitmapData *data, - const void *unused) -{ - test_hbitmap_next_dirty_area_do(data, 4); -} - -static void test_hbitmap_next_dirty_area_after_truncate(TestHBitmapData *data, - const void *unused) -{ - hbitmap_test_init(data, L1, 0); - hbitmap_test_truncate_impl(data, L1 * 2); - hbitmap_set(data->hb, L1 + 1, 1); - test_hbitmap_next_dirty_area_check(data, 0, INT64_MAX); -} - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - hbitmap_test_add("/hbitmap/size/0", test_hbitmap_zero); - hbitmap_test_add("/hbitmap/size/unaligned", test_hbitmap_unaligned); - hbitmap_test_add("/hbitmap/iter/empty", test_hbitmap_iter_empty); - hbitmap_test_add("/hbitmap/iter/partial", test_hbitmap_iter_partial); - hbitmap_test_add("/hbitmap/iter/granularity", test_hbitmap_iter_granularity); - hbitmap_test_add("/hbitmap/get/all", test_hbitmap_get_all); - hbitmap_test_add("/hbitmap/get/some", test_hbitmap_get_some); - hbitmap_test_add("/hbitmap/set/all", test_hbitmap_set_all); - hbitmap_test_add("/hbitmap/set/one", test_hbitmap_set_one); - hbitmap_test_add("/hbitmap/set/two-elem", test_hbitmap_set_two_elem); - hbitmap_test_add("/hbitmap/set/general", test_hbitmap_set); - hbitmap_test_add("/hbitmap/set/twice", test_hbitmap_set_twice); - hbitmap_test_add("/hbitmap/set/overlap", test_hbitmap_set_overlap); - hbitmap_test_add("/hbitmap/reset/empty", test_hbitmap_reset_empty); - hbitmap_test_add("/hbitmap/reset/general", test_hbitmap_reset); - hbitmap_test_add("/hbitmap/reset/all", test_hbitmap_reset_all); - hbitmap_test_add("/hbitmap/granularity", test_hbitmap_granularity); - - hbitmap_test_add("/hbitmap/truncate/nop", test_hbitmap_truncate_nop); - hbitmap_test_add("/hbitmap/truncate/grow/negligible", - test_hbitmap_truncate_grow_negligible); - hbitmap_test_add("/hbitmap/truncate/shrink/negligible", - test_hbitmap_truncate_shrink_negligible); - hbitmap_test_add("/hbitmap/truncate/grow/tiny", - test_hbitmap_truncate_grow_tiny); - hbitmap_test_add("/hbitmap/truncate/shrink/tiny", - test_hbitmap_truncate_shrink_tiny); - hbitmap_test_add("/hbitmap/truncate/grow/small", - test_hbitmap_truncate_grow_small); - hbitmap_test_add("/hbitmap/truncate/shrink/small", - test_hbitmap_truncate_shrink_small); - hbitmap_test_add("/hbitmap/truncate/grow/medium", - test_hbitmap_truncate_grow_medium); - hbitmap_test_add("/hbitmap/truncate/shrink/medium", - test_hbitmap_truncate_shrink_medium); - hbitmap_test_add("/hbitmap/truncate/grow/large", - test_hbitmap_truncate_grow_large); - hbitmap_test_add("/hbitmap/truncate/shrink/large", - test_hbitmap_truncate_shrink_large); - - hbitmap_test_add("/hbitmap/serialize/align", - test_hbitmap_serialize_align); - hbitmap_test_add("/hbitmap/serialize/basic", - test_hbitmap_serialize_basic); - hbitmap_test_add("/hbitmap/serialize/part", - test_hbitmap_serialize_part); - hbitmap_test_add("/hbitmap/serialize/zeroes", - test_hbitmap_serialize_zeroes); - - hbitmap_test_add("/hbitmap/iter/iter_and_reset", - test_hbitmap_iter_and_reset); - - hbitmap_test_add("/hbitmap/next_zero/next_x_0", - test_hbitmap_next_x_0); - hbitmap_test_add("/hbitmap/next_zero/next_x_4", - test_hbitmap_next_x_4); - hbitmap_test_add("/hbitmap/next_zero/next_x_after_truncate", - test_hbitmap_next_x_after_truncate); - - hbitmap_test_add("/hbitmap/next_dirty_area/next_dirty_area_0", - test_hbitmap_next_dirty_area_0); - hbitmap_test_add("/hbitmap/next_dirty_area/next_dirty_area_1", - test_hbitmap_next_dirty_area_1); - hbitmap_test_add("/hbitmap/next_dirty_area/next_dirty_area_4", - test_hbitmap_next_dirty_area_4); - hbitmap_test_add("/hbitmap/next_dirty_area/next_dirty_area_after_truncate", - test_hbitmap_next_dirty_area_after_truncate); - - g_test_run(); - - return 0; -} diff --git a/tests/test-image-locking.c b/tests/test-image-locking.c deleted file mode 100644 index ba057bd66c..0000000000 --- a/tests/test-image-locking.c +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Image locking tests - * - * Copyright (c) 2018 Red Hat Inc. - * - * Author: Fam Zheng - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "block/block.h" -#include "sysemu/block-backend.h" -#include "qapi/error.h" -#include "qapi/qmp/qdict.h" -#include "qemu/main-loop.h" - -static BlockBackend *open_image(const char *path, - uint64_t perm, uint64_t shared_perm, - Error **errp) -{ - Error *local_err = NULL; - BlockBackend *blk; - QDict *options = qdict_new(); - - qdict_put_str(options, "driver", "raw"); - blk = blk_new_open(path, NULL, options, BDRV_O_RDWR, &local_err); - if (blk) { - g_assert_null(local_err); - if (blk_set_perm(blk, perm, shared_perm, errp)) { - blk_unref(blk); - blk = NULL; - } - } else { - error_propagate(errp, local_err); - } - return blk; -} - -static void check_locked_bytes(int fd, uint64_t perm_locks, - uint64_t shared_perm_locks) -{ - int i; - - if (!perm_locks && !shared_perm_locks) { - g_assert(!qemu_lock_fd_test(fd, 0, 0, true)); - return; - } - for (i = 0; (1ULL << i) <= BLK_PERM_ALL; i++) { - uint64_t bit = (1ULL << i); - bool perm_expected = !!(bit & perm_locks); - bool shared_perm_expected = !!(bit & shared_perm_locks); - g_assert_cmpint(perm_expected, ==, - !!qemu_lock_fd_test(fd, 100 + i, 1, true)); - g_assert_cmpint(shared_perm_expected, ==, - !!qemu_lock_fd_test(fd, 200 + i, 1, true)); - } -} - -static void test_image_locking_basic(void) -{ - BlockBackend *blk1, *blk2, *blk3; - char img_path[] = "/tmp/qtest.XXXXXX"; - uint64_t perm, shared_perm; - - int fd = mkstemp(img_path); - assert(fd >= 0); - - perm = BLK_PERM_WRITE | BLK_PERM_CONSISTENT_READ; - shared_perm = BLK_PERM_ALL; - blk1 = open_image(img_path, perm, shared_perm, &error_abort); - g_assert(blk1); - - check_locked_bytes(fd, perm, ~shared_perm); - - /* compatible perm between blk1 and blk2 */ - blk2 = open_image(img_path, perm | BLK_PERM_RESIZE, shared_perm, NULL); - g_assert(blk2); - check_locked_bytes(fd, perm | BLK_PERM_RESIZE, ~shared_perm); - - /* incompatible perm with already open blk1 and blk2 */ - blk3 = open_image(img_path, perm, BLK_PERM_WRITE_UNCHANGED, NULL); - g_assert_null(blk3); - - blk_unref(blk2); - - /* Check that extra bytes in blk2 are correctly unlocked */ - check_locked_bytes(fd, perm, ~shared_perm); - - blk_unref(blk1); - - /* Image is unused, no lock there */ - check_locked_bytes(fd, 0, 0); - blk3 = open_image(img_path, perm, BLK_PERM_WRITE_UNCHANGED, &error_abort); - g_assert(blk3); - blk_unref(blk3); - close(fd); - unlink(img_path); -} - -static void test_set_perm_abort(void) -{ - BlockBackend *blk1, *blk2; - char img_path[] = "/tmp/qtest.XXXXXX"; - uint64_t perm, shared_perm; - int r; - int fd = mkstemp(img_path); - assert(fd >= 0); - - perm = BLK_PERM_WRITE | BLK_PERM_CONSISTENT_READ; - shared_perm = BLK_PERM_ALL; - blk1 = open_image(img_path, perm, shared_perm, &error_abort); - g_assert(blk1); - - blk2 = open_image(img_path, perm, shared_perm, &error_abort); - g_assert(blk2); - - check_locked_bytes(fd, perm, ~shared_perm); - - /* A failed blk_set_perm mustn't change perm status (locked bytes) */ - r = blk_set_perm(blk2, perm | BLK_PERM_RESIZE, BLK_PERM_WRITE_UNCHANGED, - NULL); - g_assert_cmpint(r, !=, 0); - check_locked_bytes(fd, perm, ~shared_perm); - blk_unref(blk1); - blk_unref(blk2); -} - -int main(int argc, char **argv) -{ - bdrv_init(); - qemu_init_main_loop(&error_abort); - - g_test_init(&argc, &argv, NULL); - - if (qemu_has_ofd_lock()) { - g_test_add_func("/image-locking/basic", test_image_locking_basic); - g_test_add_func("/image-locking/set-perm-abort", test_set_perm_abort); - } - - return g_test_run(); -} diff --git a/tests/test-int128.c b/tests/test-int128.c deleted file mode 100644 index b86a3c76e6..0000000000 --- a/tests/test-int128.c +++ /dev/null @@ -1,223 +0,0 @@ -/* - * Test Int128 arithmetic - * - * This work is licensed under the terms of the GNU LGPL, version 2 or later. - * See the COPYING.LIB file in the top-level directory. - * - */ - -#include "qemu/osdep.h" -#include "qemu/int128.h" - -/* clang doesn't support __noclone__ but it does have a mechanism for - * telling us this. We assume that if we don't have __has_attribute() - * then this is GCC and that GCC always supports __noclone__. - */ -#if defined(__has_attribute) -#if !__has_attribute(__noclone__) -#define ATTRIBUTE_NOCLONE -#endif -#endif -#ifndef ATTRIBUTE_NOCLONE -#define ATTRIBUTE_NOCLONE __attribute__((__noclone__)) -#endif - -static uint32_t tests[8] = { - 0x00000000, 0x00000001, 0x7FFFFFFE, 0x7FFFFFFF, - 0x80000000, 0x80000001, 0xFFFFFFFE, 0xFFFFFFFF, -}; - -#define LOW 3ULL -#define HIGH (1ULL << 63) -#define MIDDLE (-1ULL & ~LOW & ~HIGH) - -static uint64_t expand16(unsigned x) -{ - return (x & LOW) | ((x & 4) ? MIDDLE : 0) | (x & 0x8000 ? HIGH : 0); -} - -static Int128 expand(uint32_t x) -{ - uint64_t l, h; - l = expand16(x & 65535); - h = expand16(x >> 16); - return (Int128) int128_make128(l, h); -}; - -static void test_and(void) -{ - int i, j; - - for (i = 0; i < ARRAY_SIZE(tests); ++i) { - for (j = 0; j < ARRAY_SIZE(tests); ++j) { - Int128 a = expand(tests[i]); - Int128 b = expand(tests[j]); - Int128 r = expand(tests[i] & tests[j]); - Int128 s = int128_and(a, b); - g_assert_cmpuint(int128_getlo(r), ==, int128_getlo(s)); - g_assert_cmpuint(int128_gethi(r), ==, int128_gethi(s)); - } - } -} - -static void test_add(void) -{ - int i, j; - - for (i = 0; i < ARRAY_SIZE(tests); ++i) { - for (j = 0; j < ARRAY_SIZE(tests); ++j) { - Int128 a = expand(tests[i]); - Int128 b = expand(tests[j]); - Int128 r = expand(tests[i] + tests[j]); - Int128 s = int128_add(a, b); - g_assert_cmpuint(int128_getlo(r), ==, int128_getlo(s)); - g_assert_cmpuint(int128_gethi(r), ==, int128_gethi(s)); - } - } -} - -static void test_sub(void) -{ - int i, j; - - for (i = 0; i < ARRAY_SIZE(tests); ++i) { - for (j = 0; j < ARRAY_SIZE(tests); ++j) { - Int128 a = expand(tests[i]); - Int128 b = expand(tests[j]); - Int128 r = expand(tests[i] - tests[j]); - Int128 s = int128_sub(a, b); - g_assert_cmpuint(int128_getlo(r), ==, int128_getlo(s)); - g_assert_cmpuint(int128_gethi(r), ==, int128_gethi(s)); - } - } -} - -static void test_neg(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(tests); ++i) { - Int128 a = expand(tests[i]); - Int128 r = expand(-tests[i]); - Int128 s = int128_neg(a); - g_assert_cmpuint(int128_getlo(r), ==, int128_getlo(s)); - g_assert_cmpuint(int128_gethi(r), ==, int128_gethi(s)); - } -} - -static void test_nz(void) -{ - int i, j; - - for (i = 0; i < ARRAY_SIZE(tests); ++i) { - for (j = 0; j < ARRAY_SIZE(tests); ++j) { - Int128 a = expand(tests[i]); - g_assert_cmpuint(int128_nz(a), ==, tests[i] != 0); - } - } -} - -static void test_le(void) -{ - int i, j; - - for (i = 0; i < ARRAY_SIZE(tests); ++i) { - for (j = 0; j < ARRAY_SIZE(tests); ++j) { - /* Signed comparison */ - int32_t a = (int32_t) tests[i]; - int32_t b = (int32_t) tests[j]; - g_assert_cmpuint(int128_le(expand(a), expand(b)), ==, a <= b); - } - } -} - -static void test_lt(void) -{ - int i, j; - - for (i = 0; i < ARRAY_SIZE(tests); ++i) { - for (j = 0; j < ARRAY_SIZE(tests); ++j) { - /* Signed comparison */ - int32_t a = (int32_t) tests[i]; - int32_t b = (int32_t) tests[j]; - g_assert_cmpuint(int128_lt(expand(a), expand(b)), ==, a < b); - } - } -} - -static void test_ge(void) -{ - int i, j; - - for (i = 0; i < ARRAY_SIZE(tests); ++i) { - for (j = 0; j < ARRAY_SIZE(tests); ++j) { - /* Signed comparison */ - int32_t a = (int32_t) tests[i]; - int32_t b = (int32_t) tests[j]; - g_assert_cmpuint(int128_ge(expand(a), expand(b)), ==, a >= b); - } - } -} - -static void test_gt(void) -{ - int i, j; - - for (i = 0; i < ARRAY_SIZE(tests); ++i) { - for (j = 0; j < ARRAY_SIZE(tests); ++j) { - /* Signed comparison */ - int32_t a = (int32_t) tests[i]; - int32_t b = (int32_t) tests[j]; - g_assert_cmpuint(int128_gt(expand(a), expand(b)), ==, a > b); - } - } -} - -/* Make sure to test undefined behavior at runtime! */ - -static void __attribute__((__noinline__)) ATTRIBUTE_NOCLONE -test_rshift_one(uint32_t x, int n, uint64_t h, uint64_t l) -{ - Int128 a = expand(x); - Int128 r = int128_rshift(a, n); - g_assert_cmpuint(int128_getlo(r), ==, l); - g_assert_cmpuint(int128_gethi(r), ==, h); -} - -static void test_rshift(void) -{ - test_rshift_one(0x00010000U, 64, 0x0000000000000000ULL, 0x0000000000000001ULL); - test_rshift_one(0x80010000U, 64, 0xFFFFFFFFFFFFFFFFULL, 0x8000000000000001ULL); - test_rshift_one(0x7FFE0000U, 64, 0x0000000000000000ULL, 0x7FFFFFFFFFFFFFFEULL); - test_rshift_one(0xFFFE0000U, 64, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFEULL); - test_rshift_one(0x00010000U, 60, 0x0000000000000000ULL, 0x0000000000000010ULL); - test_rshift_one(0x80010000U, 60, 0xFFFFFFFFFFFFFFF8ULL, 0x0000000000000010ULL); - test_rshift_one(0x00018000U, 60, 0x0000000000000000ULL, 0x0000000000000018ULL); - test_rshift_one(0x80018000U, 60, 0xFFFFFFFFFFFFFFF8ULL, 0x0000000000000018ULL); - test_rshift_one(0x7FFE0000U, 60, 0x0000000000000007ULL, 0xFFFFFFFFFFFFFFE0ULL); - test_rshift_one(0xFFFE0000U, 60, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFE0ULL); - test_rshift_one(0x7FFE8000U, 60, 0x0000000000000007ULL, 0xFFFFFFFFFFFFFFE8ULL); - test_rshift_one(0xFFFE8000U, 60, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFE8ULL); - test_rshift_one(0x00018000U, 0, 0x0000000000000001ULL, 0x8000000000000000ULL); - test_rshift_one(0x80018000U, 0, 0x8000000000000001ULL, 0x8000000000000000ULL); - test_rshift_one(0x7FFE0000U, 0, 0x7FFFFFFFFFFFFFFEULL, 0x0000000000000000ULL); - test_rshift_one(0xFFFE0000U, 0, 0xFFFFFFFFFFFFFFFEULL, 0x0000000000000000ULL); - test_rshift_one(0x7FFE8000U, 0, 0x7FFFFFFFFFFFFFFEULL, 0x8000000000000000ULL); - test_rshift_one(0xFFFE8000U, 0, 0xFFFFFFFFFFFFFFFEULL, 0x8000000000000000ULL); -} - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - g_test_add_func("/int128/int128_and", test_and); - g_test_add_func("/int128/int128_add", test_add); - g_test_add_func("/int128/int128_sub", test_sub); - g_test_add_func("/int128/int128_neg", test_neg); - g_test_add_func("/int128/int128_nz", test_nz); - g_test_add_func("/int128/int128_le", test_le); - g_test_add_func("/int128/int128_lt", test_lt); - g_test_add_func("/int128/int128_ge", test_ge); - g_test_add_func("/int128/int128_gt", test_gt); - g_test_add_func("/int128/int128_rshift", test_rshift); - return g_test_run(); -} diff --git a/tests/test-io-channel-buffer.c b/tests/test-io-channel-buffer.c deleted file mode 100644 index 9c6724dea4..0000000000 --- a/tests/test-io-channel-buffer.c +++ /dev/null @@ -1,52 +0,0 @@ -/* - * QEMU I/O channel buffer test - * - * Copyright (c) 2015 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - * - */ - -#include "qemu/osdep.h" -#include "io/channel-buffer.h" -#include "qemu/module.h" -#include "io-channel-helpers.h" - - -static void test_io_channel_buf(void) -{ - QIOChannelBuffer *buf; - QIOChannelTest *test; - - buf = qio_channel_buffer_new(0); - - test = qio_channel_test_new(); - qio_channel_test_run_writer(test, QIO_CHANNEL(buf)); - buf->offset = 0; - qio_channel_test_run_reader(test, QIO_CHANNEL(buf)); - qio_channel_test_validate(test); - - object_unref(OBJECT(buf)); -} - - -int main(int argc, char **argv) -{ - module_call_init(MODULE_INIT_QOM); - - g_test_init(&argc, &argv, NULL); - - g_test_add_func("/io/channel/buf", test_io_channel_buf); - return g_test_run(); -} diff --git a/tests/test-io-channel-command.c b/tests/test-io-channel-command.c deleted file mode 100644 index 99056e07c0..0000000000 --- a/tests/test-io-channel-command.c +++ /dev/null @@ -1,130 +0,0 @@ -/* - * QEMU I/O channel command test - * - * Copyright (c) 2015 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - * - */ - -#include "qemu/osdep.h" -#include "io/channel-command.h" -#include "io-channel-helpers.h" -#include "qapi/error.h" -#include "qemu/module.h" - -#ifndef WIN32 -static void test_io_channel_command_fifo(bool async) -{ -#define TEST_FIFO "tests/test-io-channel-command.fifo" - QIOChannel *src, *dst; - QIOChannelTest *test; - const char *srcfifo = "PIPE:" TEST_FIFO ",wronly"; - const char *dstfifo = "PIPE:" TEST_FIFO ",rdonly"; - const char *srcargv[] = { - "/bin/socat", "-", srcfifo, NULL, - }; - const char *dstargv[] = { - "/bin/socat", dstfifo, "-", NULL, - }; - - unlink(TEST_FIFO); - if (access("/bin/socat", X_OK) < 0) { - return; /* Pretend success if socat is not present */ - } - if (mkfifo(TEST_FIFO, 0600) < 0) { - abort(); - } - src = QIO_CHANNEL(qio_channel_command_new_spawn(srcargv, - O_WRONLY, - &error_abort)); - dst = QIO_CHANNEL(qio_channel_command_new_spawn(dstargv, - O_RDONLY, - &error_abort)); - - test = qio_channel_test_new(); - qio_channel_test_run_threads(test, async, src, dst); - qio_channel_test_validate(test); - - object_unref(OBJECT(src)); - object_unref(OBJECT(dst)); - - unlink(TEST_FIFO); -} - - -static void test_io_channel_command_fifo_async(void) -{ - test_io_channel_command_fifo(true); -} - -static void test_io_channel_command_fifo_sync(void) -{ - test_io_channel_command_fifo(false); -} - - -static void test_io_channel_command_echo(bool async) -{ - QIOChannel *ioc; - QIOChannelTest *test; - const char *socatargv[] = { - "/bin/socat", "-", "-", NULL, - }; - - if (access("/bin/socat", X_OK) < 0) { - return; /* Pretend success if socat is not present */ - } - - ioc = QIO_CHANNEL(qio_channel_command_new_spawn(socatargv, - O_RDWR, - &error_abort)); - test = qio_channel_test_new(); - qio_channel_test_run_threads(test, async, ioc, ioc); - qio_channel_test_validate(test); - - object_unref(OBJECT(ioc)); -} - - -static void test_io_channel_command_echo_async(void) -{ - test_io_channel_command_echo(true); -} - -static void test_io_channel_command_echo_sync(void) -{ - test_io_channel_command_echo(false); -} -#endif - -int main(int argc, char **argv) -{ - module_call_init(MODULE_INIT_QOM); - - g_test_init(&argc, &argv, NULL); - -#ifndef WIN32 - g_test_add_func("/io/channel/command/fifo/sync", - test_io_channel_command_fifo_sync); - g_test_add_func("/io/channel/command/fifo/async", - test_io_channel_command_fifo_async); - g_test_add_func("/io/channel/command/echo/sync", - test_io_channel_command_echo_sync); - g_test_add_func("/io/channel/command/echo/async", - test_io_channel_command_echo_async); -#endif - - return g_test_run(); -} diff --git a/tests/test-io-channel-file.c b/tests/test-io-channel-file.c deleted file mode 100644 index 29038e67b6..0000000000 --- a/tests/test-io-channel-file.c +++ /dev/null @@ -1,155 +0,0 @@ -/* - * QEMU I/O channel file test - * - * Copyright (c) 2015 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - * - */ - -#include "qemu/osdep.h" -#include "io/channel-file.h" -#include "io/channel-util.h" -#include "io-channel-helpers.h" -#include "qapi/error.h" -#include "qemu/module.h" - -#define TEST_FILE "tests/test-io-channel-file.txt" -#define TEST_MASK 0600 - -/* - * On Windows the stat() function in the C library checks only - * the FAT-style READONLY attribute and does not look at the ACL at all. - */ -#ifdef _WIN32 -#define TEST_MASK_EXPECT 0700 -#else -#define TEST_MASK_EXPECT 0777 -#endif - -static void test_io_channel_file_helper(int flags) -{ - QIOChannel *src, *dst; - QIOChannelTest *test; - struct stat st; - mode_t mask; - int ret; - - unlink(TEST_FILE); - src = QIO_CHANNEL(qio_channel_file_new_path( - TEST_FILE, - flags, TEST_MASK, - &error_abort)); - dst = QIO_CHANNEL(qio_channel_file_new_path( - TEST_FILE, - O_RDONLY | O_BINARY, 0, - &error_abort)); - - test = qio_channel_test_new(); - qio_channel_test_run_writer(test, src); - qio_channel_test_run_reader(test, dst); - qio_channel_test_validate(test); - - /* Check that the requested mode took effect. */ - mask = umask(0); - umask(mask); - ret = stat(TEST_FILE, &st); - g_assert_cmpint(ret, >, -1); - g_assert_cmpuint(TEST_MASK & ~mask, ==, st.st_mode & TEST_MASK_EXPECT); - - unlink(TEST_FILE); - object_unref(OBJECT(src)); - object_unref(OBJECT(dst)); -} - -static void test_io_channel_file(void) -{ - test_io_channel_file_helper(O_WRONLY | O_CREAT | O_TRUNC | O_BINARY); -} - -static void test_io_channel_file_rdwr(void) -{ - test_io_channel_file_helper(O_RDWR | O_CREAT | O_TRUNC | O_BINARY); -} - -static void test_io_channel_fd(void) -{ - QIOChannel *ioc; - int fd = -1; - - fd = open(TEST_FILE, O_CREAT | O_TRUNC | O_WRONLY, 0600); - g_assert_cmpint(fd, >, -1); - - ioc = qio_channel_new_fd(fd, &error_abort); - - g_assert_cmpstr(object_get_typename(OBJECT(ioc)), - ==, - TYPE_QIO_CHANNEL_FILE); - - unlink(TEST_FILE); - object_unref(OBJECT(ioc)); -} - - -#ifndef _WIN32 -static void test_io_channel_pipe(bool async) -{ - QIOChannel *src, *dst; - QIOChannelTest *test; - int fd[2]; - - if (pipe(fd) < 0) { - perror("pipe"); - abort(); - } - - src = QIO_CHANNEL(qio_channel_file_new_fd(fd[1])); - dst = QIO_CHANNEL(qio_channel_file_new_fd(fd[0])); - - test = qio_channel_test_new(); - qio_channel_test_run_threads(test, async, src, dst); - qio_channel_test_validate(test); - - object_unref(OBJECT(src)); - object_unref(OBJECT(dst)); -} - - -static void test_io_channel_pipe_async(void) -{ - test_io_channel_pipe(true); -} - -static void test_io_channel_pipe_sync(void) -{ - test_io_channel_pipe(false); -} -#endif /* ! _WIN32 */ - - -int main(int argc, char **argv) -{ - module_call_init(MODULE_INIT_QOM); - - g_test_init(&argc, &argv, NULL); - - g_test_add_func("/io/channel/file", test_io_channel_file); - g_test_add_func("/io/channel/file/rdwr", test_io_channel_file_rdwr); - g_test_add_func("/io/channel/file/fd", test_io_channel_fd); -#ifndef _WIN32 - g_test_add_func("/io/channel/pipe/sync", test_io_channel_pipe_sync); - g_test_add_func("/io/channel/pipe/async", test_io_channel_pipe_async); -#endif - return g_test_run(); -} diff --git a/tests/test-io-channel-socket.c b/tests/test-io-channel-socket.c deleted file mode 100644 index c49eec1f03..0000000000 --- a/tests/test-io-channel-socket.c +++ /dev/null @@ -1,603 +0,0 @@ -/* - * QEMU I/O channel sockets test - * - * Copyright (c) 2015-2016 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - * - */ - -#include "qemu/osdep.h" -#include "io/channel-socket.h" -#include "io/channel-util.h" -#include "io-channel-helpers.h" -#include "socket-helpers.h" -#include "qapi/error.h" -#include "qemu/module.h" -#include "qemu/main-loop.h" - - -static void test_io_channel_set_socket_bufs(QIOChannel *src, - QIOChannel *dst) -{ - int buflen = 64 * 1024; - - /* - * Make the socket buffers small so that we see - * the effects of partial reads/writes - */ - setsockopt(((QIOChannelSocket *)src)->fd, - SOL_SOCKET, SO_SNDBUF, - (char *)&buflen, - sizeof(buflen)); - - setsockopt(((QIOChannelSocket *)dst)->fd, - SOL_SOCKET, SO_SNDBUF, - (char *)&buflen, - sizeof(buflen)); -} - - -static void test_io_channel_setup_sync(SocketAddress *listen_addr, - SocketAddress *connect_addr, - QIOChannel **srv, - QIOChannel **src, - QIOChannel **dst) -{ - QIOChannelSocket *lioc; - - lioc = qio_channel_socket_new(); - qio_channel_socket_listen_sync(lioc, listen_addr, 1, &error_abort); - - if (listen_addr->type == SOCKET_ADDRESS_TYPE_INET) { - SocketAddress *laddr = qio_channel_socket_get_local_address( - lioc, &error_abort); - - g_free(connect_addr->u.inet.port); - connect_addr->u.inet.port = g_strdup(laddr->u.inet.port); - - qapi_free_SocketAddress(laddr); - } - - *src = QIO_CHANNEL(qio_channel_socket_new()); - qio_channel_socket_connect_sync( - QIO_CHANNEL_SOCKET(*src), connect_addr, &error_abort); - qio_channel_set_delay(*src, false); - - qio_channel_wait(QIO_CHANNEL(lioc), G_IO_IN); - *dst = QIO_CHANNEL(qio_channel_socket_accept(lioc, &error_abort)); - g_assert(*dst); - - test_io_channel_set_socket_bufs(*src, *dst); - - *srv = QIO_CHANNEL(lioc); -} - - -struct TestIOChannelData { - bool err; - GMainLoop *loop; -}; - - -static void test_io_channel_complete(QIOTask *task, - gpointer opaque) -{ - struct TestIOChannelData *data = opaque; - data->err = qio_task_propagate_error(task, NULL); - g_main_loop_quit(data->loop); -} - - -static void test_io_channel_setup_async(SocketAddress *listen_addr, - SocketAddress *connect_addr, - QIOChannel **srv, - QIOChannel **src, - QIOChannel **dst) -{ - QIOChannelSocket *lioc; - struct TestIOChannelData data; - - data.loop = g_main_loop_new(g_main_context_default(), - TRUE); - - lioc = qio_channel_socket_new(); - qio_channel_socket_listen_async( - lioc, listen_addr, 1, - test_io_channel_complete, &data, NULL, NULL); - - g_main_loop_run(data.loop); - g_main_context_iteration(g_main_context_default(), FALSE); - - g_assert(!data.err); - - if (listen_addr->type == SOCKET_ADDRESS_TYPE_INET) { - SocketAddress *laddr = qio_channel_socket_get_local_address( - lioc, &error_abort); - - g_free(connect_addr->u.inet.port); - connect_addr->u.inet.port = g_strdup(laddr->u.inet.port); - - qapi_free_SocketAddress(laddr); - } - - *src = QIO_CHANNEL(qio_channel_socket_new()); - - qio_channel_socket_connect_async( - QIO_CHANNEL_SOCKET(*src), connect_addr, - test_io_channel_complete, &data, NULL, NULL); - - g_main_loop_run(data.loop); - g_main_context_iteration(g_main_context_default(), FALSE); - - g_assert(!data.err); - - qio_channel_wait(QIO_CHANNEL(lioc), G_IO_IN); - *dst = QIO_CHANNEL(qio_channel_socket_accept(lioc, &error_abort)); - g_assert(*dst); - - qio_channel_set_delay(*src, false); - test_io_channel_set_socket_bufs(*src, *dst); - - *srv = QIO_CHANNEL(lioc); - - g_main_loop_unref(data.loop); -} - - -static void test_io_channel_socket_path_exists(SocketAddress *addr, - bool expectExists) -{ - if (addr->type != SOCKET_ADDRESS_TYPE_UNIX) { - return; - } - - g_assert(g_file_test(addr->u.q_unix.path, - G_FILE_TEST_EXISTS) == expectExists); -} - - -static void test_io_channel(bool async, - SocketAddress *listen_addr, - SocketAddress *connect_addr, - bool passFD) -{ - QIOChannel *src, *dst, *srv; - QIOChannelTest *test; - if (async) { - test_io_channel_setup_async(listen_addr, connect_addr, - &srv, &src, &dst); - - g_assert(!passFD || - qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_FD_PASS)); - g_assert(!passFD || - qio_channel_has_feature(dst, QIO_CHANNEL_FEATURE_FD_PASS)); - g_assert(qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_SHUTDOWN)); - g_assert(qio_channel_has_feature(dst, QIO_CHANNEL_FEATURE_SHUTDOWN)); - - test_io_channel_socket_path_exists(listen_addr, true); - - test = qio_channel_test_new(); - qio_channel_test_run_threads(test, true, src, dst); - qio_channel_test_validate(test); - - test_io_channel_socket_path_exists(listen_addr, true); - - /* unref without close, to ensure finalize() cleans up */ - - object_unref(OBJECT(src)); - object_unref(OBJECT(dst)); - test_io_channel_socket_path_exists(listen_addr, true); - - object_unref(OBJECT(srv)); - test_io_channel_socket_path_exists(listen_addr, false); - - test_io_channel_setup_async(listen_addr, connect_addr, - &srv, &src, &dst); - - g_assert(!passFD || - qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_FD_PASS)); - g_assert(!passFD || - qio_channel_has_feature(dst, QIO_CHANNEL_FEATURE_FD_PASS)); - g_assert(qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_SHUTDOWN)); - g_assert(qio_channel_has_feature(dst, QIO_CHANNEL_FEATURE_SHUTDOWN)); - - test = qio_channel_test_new(); - qio_channel_test_run_threads(test, false, src, dst); - qio_channel_test_validate(test); - - /* close before unref, to ensure finalize copes with already closed */ - - qio_channel_close(src, &error_abort); - qio_channel_close(dst, &error_abort); - test_io_channel_socket_path_exists(listen_addr, true); - - object_unref(OBJECT(src)); - object_unref(OBJECT(dst)); - test_io_channel_socket_path_exists(listen_addr, true); - - qio_channel_close(srv, &error_abort); - test_io_channel_socket_path_exists(listen_addr, false); - - object_unref(OBJECT(srv)); - test_io_channel_socket_path_exists(listen_addr, false); - } else { - test_io_channel_setup_sync(listen_addr, connect_addr, - &srv, &src, &dst); - - g_assert(!passFD || - qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_FD_PASS)); - g_assert(!passFD || - qio_channel_has_feature(dst, QIO_CHANNEL_FEATURE_FD_PASS)); - g_assert(qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_SHUTDOWN)); - g_assert(qio_channel_has_feature(dst, QIO_CHANNEL_FEATURE_SHUTDOWN)); - - test_io_channel_socket_path_exists(listen_addr, true); - - test = qio_channel_test_new(); - qio_channel_test_run_threads(test, true, src, dst); - qio_channel_test_validate(test); - - test_io_channel_socket_path_exists(listen_addr, true); - - /* unref without close, to ensure finalize() cleans up */ - - object_unref(OBJECT(src)); - object_unref(OBJECT(dst)); - test_io_channel_socket_path_exists(listen_addr, true); - - object_unref(OBJECT(srv)); - test_io_channel_socket_path_exists(listen_addr, false); - - test_io_channel_setup_sync(listen_addr, connect_addr, - &srv, &src, &dst); - - g_assert(!passFD || - qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_FD_PASS)); - g_assert(!passFD || - qio_channel_has_feature(dst, QIO_CHANNEL_FEATURE_FD_PASS)); - g_assert(qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_SHUTDOWN)); - g_assert(qio_channel_has_feature(dst, QIO_CHANNEL_FEATURE_SHUTDOWN)); - - test = qio_channel_test_new(); - qio_channel_test_run_threads(test, false, src, dst); - qio_channel_test_validate(test); - - test_io_channel_socket_path_exists(listen_addr, true); - - /* close before unref, to ensure finalize copes with already closed */ - - qio_channel_close(src, &error_abort); - qio_channel_close(dst, &error_abort); - test_io_channel_socket_path_exists(listen_addr, true); - - object_unref(OBJECT(src)); - object_unref(OBJECT(dst)); - test_io_channel_socket_path_exists(listen_addr, true); - - qio_channel_close(srv, &error_abort); - test_io_channel_socket_path_exists(listen_addr, false); - - object_unref(OBJECT(srv)); - test_io_channel_socket_path_exists(listen_addr, false); - } -} - - -static void test_io_channel_ipv4(bool async) -{ - SocketAddress *listen_addr = g_new0(SocketAddress, 1); - SocketAddress *connect_addr = g_new0(SocketAddress, 1); - - listen_addr->type = SOCKET_ADDRESS_TYPE_INET; - listen_addr->u.inet = (InetSocketAddress) { - .host = g_strdup("127.0.0.1"), - .port = NULL, /* Auto-select */ - }; - - connect_addr->type = SOCKET_ADDRESS_TYPE_INET; - connect_addr->u.inet = (InetSocketAddress) { - .host = g_strdup("127.0.0.1"), - .port = NULL, /* Filled in later */ - }; - - test_io_channel(async, listen_addr, connect_addr, false); - - qapi_free_SocketAddress(listen_addr); - qapi_free_SocketAddress(connect_addr); -} - - -static void test_io_channel_ipv4_sync(void) -{ - return test_io_channel_ipv4(false); -} - - -static void test_io_channel_ipv4_async(void) -{ - return test_io_channel_ipv4(true); -} - - -static void test_io_channel_ipv6(bool async) -{ - SocketAddress *listen_addr = g_new0(SocketAddress, 1); - SocketAddress *connect_addr = g_new0(SocketAddress, 1); - - listen_addr->type = SOCKET_ADDRESS_TYPE_INET; - listen_addr->u.inet = (InetSocketAddress) { - .host = g_strdup("::1"), - .port = NULL, /* Auto-select */ - }; - - connect_addr->type = SOCKET_ADDRESS_TYPE_INET; - connect_addr->u.inet = (InetSocketAddress) { - .host = g_strdup("::1"), - .port = NULL, /* Filled in later */ - }; - - test_io_channel(async, listen_addr, connect_addr, false); - - qapi_free_SocketAddress(listen_addr); - qapi_free_SocketAddress(connect_addr); -} - - -static void test_io_channel_ipv6_sync(void) -{ - return test_io_channel_ipv6(false); -} - - -static void test_io_channel_ipv6_async(void) -{ - return test_io_channel_ipv6(true); -} - - -#ifndef _WIN32 -static void test_io_channel_unix(bool async) -{ - SocketAddress *listen_addr = g_new0(SocketAddress, 1); - SocketAddress *connect_addr = g_new0(SocketAddress, 1); - -#define TEST_SOCKET "test-io-channel-socket.sock" - listen_addr->type = SOCKET_ADDRESS_TYPE_UNIX; - listen_addr->u.q_unix.path = g_strdup(TEST_SOCKET); - - connect_addr->type = SOCKET_ADDRESS_TYPE_UNIX; - connect_addr->u.q_unix.path = g_strdup(TEST_SOCKET); - - test_io_channel(async, listen_addr, connect_addr, true); - - qapi_free_SocketAddress(listen_addr); - qapi_free_SocketAddress(connect_addr); -} - - -static void test_io_channel_unix_sync(void) -{ - return test_io_channel_unix(false); -} - - -static void test_io_channel_unix_async(void) -{ - return test_io_channel_unix(true); -} - -static void test_io_channel_unix_fd_pass(void) -{ - SocketAddress *listen_addr = g_new0(SocketAddress, 1); - SocketAddress *connect_addr = g_new0(SocketAddress, 1); - QIOChannel *src, *dst, *srv; - int testfd; - int fdsend[3]; - int *fdrecv = NULL; - size_t nfdrecv = 0; - size_t i; - char bufsend[12], bufrecv[12]; - struct iovec iosend[1], iorecv[1]; - -#define TEST_SOCKET "test-io-channel-socket.sock" -#define TEST_FILE "test-io-channel-socket.txt" - - testfd = open(TEST_FILE, O_RDWR|O_TRUNC|O_CREAT, 0700); - g_assert(testfd != -1); - fdsend[0] = testfd; - fdsend[1] = testfd; - fdsend[2] = testfd; - - listen_addr->type = SOCKET_ADDRESS_TYPE_UNIX; - listen_addr->u.q_unix.path = g_strdup(TEST_SOCKET); - - connect_addr->type = SOCKET_ADDRESS_TYPE_UNIX; - connect_addr->u.q_unix.path = g_strdup(TEST_SOCKET); - - test_io_channel_setup_sync(listen_addr, connect_addr, &srv, &src, &dst); - - memcpy(bufsend, "Hello World", G_N_ELEMENTS(bufsend)); - - iosend[0].iov_base = bufsend; - iosend[0].iov_len = G_N_ELEMENTS(bufsend); - - iorecv[0].iov_base = bufrecv; - iorecv[0].iov_len = G_N_ELEMENTS(bufrecv); - - g_assert(qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_FD_PASS)); - g_assert(qio_channel_has_feature(dst, QIO_CHANNEL_FEATURE_FD_PASS)); - - qio_channel_writev_full(src, - iosend, - G_N_ELEMENTS(iosend), - fdsend, - G_N_ELEMENTS(fdsend), - &error_abort); - - qio_channel_readv_full(dst, - iorecv, - G_N_ELEMENTS(iorecv), - &fdrecv, - &nfdrecv, - &error_abort); - - g_assert(nfdrecv == G_N_ELEMENTS(fdsend)); - /* Each recvd FD should be different from sent FD */ - for (i = 0; i < nfdrecv; i++) { - g_assert_cmpint(fdrecv[i], !=, testfd); - } - /* Each recvd FD should be different from each other */ - g_assert_cmpint(fdrecv[0], !=, fdrecv[1]); - g_assert_cmpint(fdrecv[0], !=, fdrecv[2]); - g_assert_cmpint(fdrecv[1], !=, fdrecv[2]); - - /* Check the I/O buf we sent at the same time matches */ - g_assert(memcmp(bufsend, bufrecv, G_N_ELEMENTS(bufsend)) == 0); - - /* Write some data into the FD we received */ - g_assert(write(fdrecv[0], bufsend, G_N_ELEMENTS(bufsend)) == - G_N_ELEMENTS(bufsend)); - - /* Read data from the original FD and make sure it matches */ - memset(bufrecv, 0, G_N_ELEMENTS(bufrecv)); - g_assert(lseek(testfd, 0, SEEK_SET) == 0); - g_assert(read(testfd, bufrecv, G_N_ELEMENTS(bufrecv)) == - G_N_ELEMENTS(bufrecv)); - g_assert(memcmp(bufsend, bufrecv, G_N_ELEMENTS(bufsend)) == 0); - - object_unref(OBJECT(src)); - object_unref(OBJECT(dst)); - object_unref(OBJECT(srv)); - qapi_free_SocketAddress(listen_addr); - qapi_free_SocketAddress(connect_addr); - unlink(TEST_SOCKET); - unlink(TEST_FILE); - close(testfd); - for (i = 0; i < nfdrecv; i++) { - close(fdrecv[i]); - } - g_free(fdrecv); -} - -static void test_io_channel_unix_listen_cleanup(void) -{ - QIOChannelSocket *ioc; - struct sockaddr_un un; - int sock; - -#define TEST_SOCKET "test-io-channel-socket.sock" - - ioc = qio_channel_socket_new(); - - /* Manually bind ioc without calling the qio api to avoid setting - * the LISTEN feature */ - sock = qemu_socket(PF_UNIX, SOCK_STREAM, 0); - memset(&un, 0, sizeof(un)); - un.sun_family = AF_UNIX; - snprintf(un.sun_path, sizeof(un.sun_path), "%s", TEST_SOCKET); - unlink(TEST_SOCKET); - bind(sock, (struct sockaddr *)&un, sizeof(un)); - ioc->fd = sock; - ioc->localAddrLen = sizeof(ioc->localAddr); - getsockname(sock, (struct sockaddr *)&ioc->localAddr, - &ioc->localAddrLen); - - g_assert(g_file_test(TEST_SOCKET, G_FILE_TEST_EXISTS)); - object_unref(OBJECT(ioc)); - g_assert(g_file_test(TEST_SOCKET, G_FILE_TEST_EXISTS)); - - unlink(TEST_SOCKET); -} - -#endif /* _WIN32 */ - - -static void test_io_channel_ipv4_fd(void) -{ - QIOChannel *ioc; - int fd = -1; - struct sockaddr_in sa = { - .sin_family = AF_INET, - .sin_addr = { - .s_addr = htonl(INADDR_LOOPBACK), - } - /* Leave port unset for auto-assign */ - }; - socklen_t salen = sizeof(sa); - - fd = socket(AF_INET, SOCK_STREAM, 0); - g_assert_cmpint(fd, >, -1); - - g_assert_cmpint(bind(fd, (struct sockaddr *)&sa, salen), ==, 0); - - ioc = qio_channel_new_fd(fd, &error_abort); - - g_assert_cmpstr(object_get_typename(OBJECT(ioc)), - ==, - TYPE_QIO_CHANNEL_SOCKET); - - object_unref(OBJECT(ioc)); -} - - -int main(int argc, char **argv) -{ - bool has_ipv4, has_ipv6; - - module_call_init(MODULE_INIT_QOM); - qemu_init_main_loop(&error_abort); - socket_init(); - - g_test_init(&argc, &argv, NULL); - - /* We're creating actual IPv4/6 sockets, so we should - * check if the host running tests actually supports - * each protocol to avoid breaking tests on machines - * with either IPv4 or IPv6 disabled. - */ - if (socket_check_protocol_support(&has_ipv4, &has_ipv6) < 0) { - g_printerr("socket_check_protocol_support() failed\n"); - goto end; - } - - if (has_ipv4) { - g_test_add_func("/io/channel/socket/ipv4-sync", - test_io_channel_ipv4_sync); - g_test_add_func("/io/channel/socket/ipv4-async", - test_io_channel_ipv4_async); - g_test_add_func("/io/channel/socket/ipv4-fd", - test_io_channel_ipv4_fd); - } - if (has_ipv6) { - g_test_add_func("/io/channel/socket/ipv6-sync", - test_io_channel_ipv6_sync); - g_test_add_func("/io/channel/socket/ipv6-async", - test_io_channel_ipv6_async); - } - -#ifndef _WIN32 - g_test_add_func("/io/channel/socket/unix-sync", - test_io_channel_unix_sync); - g_test_add_func("/io/channel/socket/unix-async", - test_io_channel_unix_async); - g_test_add_func("/io/channel/socket/unix-fd-pass", - test_io_channel_unix_fd_pass); - g_test_add_func("/io/channel/socket/unix-listen-cleanup", - test_io_channel_unix_listen_cleanup); -#endif /* _WIN32 */ - -end: - return g_test_run(); -} diff --git a/tests/test-io-channel-tls.c b/tests/test-io-channel-tls.c deleted file mode 100644 index ad7554c534..0000000000 --- a/tests/test-io-channel-tls.c +++ /dev/null @@ -1,346 +0,0 @@ -/* - * QEMU I/O channel TLS test - * - * Copyright (C) 2015 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see - * . - * - * Author: Daniel P. Berrange - */ - - -#include "qemu/osdep.h" - -#include "crypto-tls-x509-helpers.h" -#include "io/channel-tls.h" -#include "io/channel-socket.h" -#include "io-channel-helpers.h" -#include "crypto/init.h" -#include "crypto/tlscredsx509.h" -#include "qapi/error.h" -#include "qemu/module.h" -#include "authz/list.h" -#include "qom/object_interfaces.h" - -#ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT - -#define WORKDIR "tests/test-io-channel-tls-work/" -#define KEYFILE WORKDIR "key-ctx.pem" - -struct QIOChannelTLSTestData { - const char *servercacrt; - const char *clientcacrt; - const char *servercrt; - const char *clientcrt; - bool expectServerFail; - bool expectClientFail; - const char *hostname; - const char *const *wildcards; -}; - -struct QIOChannelTLSHandshakeData { - bool finished; - bool failed; -}; - -static void test_tls_handshake_done(QIOTask *task, - gpointer opaque) -{ - struct QIOChannelTLSHandshakeData *data = opaque; - - data->finished = true; - data->failed = qio_task_propagate_error(task, NULL); -} - - -static QCryptoTLSCreds *test_tls_creds_create(QCryptoTLSCredsEndpoint endpoint, - const char *certdir) -{ - Object *parent = object_get_objects_root(); - Object *creds = object_new_with_props( - TYPE_QCRYPTO_TLS_CREDS_X509, - parent, - (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ? - "testtlscredsserver" : "testtlscredsclient"), - &error_abort, - "endpoint", (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ? - "server" : "client"), - "dir", certdir, - "verify-peer", "yes", - "priority", "NORMAL", - /* We skip initial sanity checks here because we - * want to make sure that problems are being - * detected at the TLS session validation stage, - * and the test-crypto-tlscreds test already - * validate the sanity check code. - */ - "sanity-check", "no", - NULL - ); - - return QCRYPTO_TLS_CREDS(creds); -} - - -/* - * This tests validation checking of peer certificates - * - * This is replicating the checks that are done for an - * active TLS session after handshake completes. To - * simulate that we create our TLS contexts, skipping - * sanity checks. When then get a socketpair, and - * initiate a TLS session across them. Finally do - * do actual cert validation tests - */ -static void test_io_channel_tls(const void *opaque) -{ - struct QIOChannelTLSTestData *data = - (struct QIOChannelTLSTestData *)opaque; - QCryptoTLSCreds *clientCreds; - QCryptoTLSCreds *serverCreds; - QIOChannelTLS *clientChanTLS; - QIOChannelTLS *serverChanTLS; - QIOChannelSocket *clientChanSock; - QIOChannelSocket *serverChanSock; - QAuthZList *auth; - const char * const *wildcards; - int channel[2]; - struct QIOChannelTLSHandshakeData clientHandshake = { false, false }; - struct QIOChannelTLSHandshakeData serverHandshake = { false, false }; - QIOChannelTest *test; - GMainContext *mainloop; - - /* We'll use this for our fake client-server connection */ - g_assert(socketpair(AF_UNIX, SOCK_STREAM, 0, channel) == 0); - -#define CLIENT_CERT_DIR "tests/test-io-channel-tls-client/" -#define SERVER_CERT_DIR "tests/test-io-channel-tls-server/" - mkdir(CLIENT_CERT_DIR, 0700); - mkdir(SERVER_CERT_DIR, 0700); - - unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT); - unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT); - unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY); - - unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT); - unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT); - unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY); - - g_assert(link(data->servercacrt, - SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT) == 0); - g_assert(link(data->servercrt, - SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT) == 0); - g_assert(link(KEYFILE, - SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY) == 0); - - g_assert(link(data->clientcacrt, - CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT) == 0); - g_assert(link(data->clientcrt, - CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT) == 0); - g_assert(link(KEYFILE, - CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY) == 0); - - clientCreds = test_tls_creds_create( - QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, - CLIENT_CERT_DIR); - g_assert(clientCreds != NULL); - - serverCreds = test_tls_creds_create( - QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, - SERVER_CERT_DIR); - g_assert(serverCreds != NULL); - - auth = qauthz_list_new("channeltlsacl", - QAUTHZ_LIST_POLICY_DENY, - &error_abort); - wildcards = data->wildcards; - while (wildcards && *wildcards) { - qauthz_list_append_rule(auth, *wildcards, - QAUTHZ_LIST_POLICY_ALLOW, - QAUTHZ_LIST_FORMAT_GLOB, - &error_abort); - wildcards++; - } - - clientChanSock = qio_channel_socket_new_fd( - channel[0], &error_abort); - g_assert(clientChanSock != NULL); - serverChanSock = qio_channel_socket_new_fd( - channel[1], &error_abort); - g_assert(serverChanSock != NULL); - - /* - * We have an evil loop to do the handshake in a single - * thread, so we need these non-blocking to avoid deadlock - * of ourselves - */ - qio_channel_set_blocking(QIO_CHANNEL(clientChanSock), false, NULL); - qio_channel_set_blocking(QIO_CHANNEL(serverChanSock), false, NULL); - - /* Now the real part of the test, setup the sessions */ - clientChanTLS = qio_channel_tls_new_client( - QIO_CHANNEL(clientChanSock), clientCreds, - data->hostname, &error_abort); - g_assert(clientChanTLS != NULL); - - serverChanTLS = qio_channel_tls_new_server( - QIO_CHANNEL(serverChanSock), serverCreds, - "channeltlsacl", &error_abort); - g_assert(serverChanTLS != NULL); - - qio_channel_tls_handshake(clientChanTLS, - test_tls_handshake_done, - &clientHandshake, - NULL, - NULL); - qio_channel_tls_handshake(serverChanTLS, - test_tls_handshake_done, - &serverHandshake, - NULL, - NULL); - - /* - * Finally we loop around & around doing handshake on each - * session until we get an error, or the handshake completes. - * This relies on the socketpair being nonblocking to avoid - * deadlocking ourselves upon handshake - */ - mainloop = g_main_context_default(); - do { - g_main_context_iteration(mainloop, TRUE); - } while (!clientHandshake.finished || - !serverHandshake.finished); - - g_assert(clientHandshake.failed == data->expectClientFail); - g_assert(serverHandshake.failed == data->expectServerFail); - - test = qio_channel_test_new(); - qio_channel_test_run_threads(test, false, - QIO_CHANNEL(clientChanTLS), - QIO_CHANNEL(serverChanTLS)); - qio_channel_test_validate(test); - - test = qio_channel_test_new(); - qio_channel_test_run_threads(test, true, - QIO_CHANNEL(clientChanTLS), - QIO_CHANNEL(serverChanTLS)); - qio_channel_test_validate(test); - - unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT); - unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT); - unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY); - - unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT); - unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT); - unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY); - - rmdir(CLIENT_CERT_DIR); - rmdir(SERVER_CERT_DIR); - - object_unparent(OBJECT(serverCreds)); - object_unparent(OBJECT(clientCreds)); - - object_unref(OBJECT(serverChanTLS)); - object_unref(OBJECT(clientChanTLS)); - - object_unref(OBJECT(serverChanSock)); - object_unref(OBJECT(clientChanSock)); - - object_unparent(OBJECT(auth)); - - close(channel[0]); - close(channel[1]); -} - - -int main(int argc, char **argv) -{ - int ret; - - g_assert(qcrypto_init(NULL) == 0); - - module_call_init(MODULE_INIT_QOM); - g_test_init(&argc, &argv, NULL); - g_setenv("GNUTLS_FORCE_FIPS_MODE", "2", 1); - - mkdir(WORKDIR, 0700); - - test_tls_init(KEYFILE); - -# define TEST_CHANNEL(name, caCrt, \ - serverCrt, clientCrt, \ - expectServerFail, expectClientFail, \ - hostname, wildcards) \ - struct QIOChannelTLSTestData name = { \ - caCrt, caCrt, serverCrt, clientCrt, \ - expectServerFail, expectClientFail, \ - hostname, wildcards \ - }; \ - g_test_add_data_func("/qio/channel/tls/" # name, \ - &name, test_io_channel_tls); - - /* A perfect CA, perfect client & perfect server */ - - /* Basic:CA:critical */ - TLS_ROOT_REQ(cacertreq, - "UK", "qemu CA", NULL, NULL, NULL, NULL, - true, true, true, - true, true, GNUTLS_KEY_KEY_CERT_SIGN, - false, false, NULL, NULL, - 0, 0); - TLS_CERT_REQ(servercertreq, cacertreq, - "UK", "qemu.org", NULL, NULL, NULL, NULL, - true, true, false, - true, true, - GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, - true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, - 0, 0); - TLS_CERT_REQ(clientcertreq, cacertreq, - "UK", "qemu", NULL, NULL, NULL, NULL, - true, true, false, - true, true, - GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, - true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL, - 0, 0); - - const char *const wildcards[] = { - "C=UK,CN=qemu*", - NULL, - }; - TEST_CHANNEL(basic, cacertreq.filename, servercertreq.filename, - clientcertreq.filename, false, false, - "qemu.org", wildcards); - - ret = g_test_run(); - - test_tls_discard_cert(&clientcertreq); - test_tls_discard_cert(&servercertreq); - test_tls_discard_cert(&cacertreq); - - test_tls_cleanup(KEYFILE); - rmdir(WORKDIR); - - return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; -} - -#else /* ! QCRYPTO_HAVE_TLS_TEST_SUPPORT */ - -int -main(void) -{ - return EXIT_SUCCESS; -} - -#endif /* ! QCRYPTO_HAVE_TLS_TEST_SUPPORT */ diff --git a/tests/test-io-task.c b/tests/test-io-task.c deleted file mode 100644 index 953a50ae66..0000000000 --- a/tests/test-io-task.c +++ /dev/null @@ -1,268 +0,0 @@ -/* - * QEMU I/O task tests - * - * Copyright (c) 2015 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - * - */ - -#include "qemu/osdep.h" - -#include "qom/object.h" -#include "io/task.h" -#include "qapi/error.h" -#include "qemu/module.h" - -#define TYPE_DUMMY "qemu:dummy" - -typedef struct DummyObject DummyObject; -typedef struct DummyObjectClass DummyObjectClass; - -struct DummyObject { - Object parent; -}; - -struct DummyObjectClass { - ObjectClass parent; -}; - -static const TypeInfo dummy_info = { - .parent = TYPE_OBJECT, - .name = TYPE_DUMMY, - .instance_size = sizeof(DummyObject), - .class_size = sizeof(DummyObjectClass), -}; - -struct TestTaskData { - Object *source; - Error *err; - bool freed; -}; - - -static void task_callback(QIOTask *task, - gpointer opaque) -{ - struct TestTaskData *data = opaque; - - data->source = qio_task_get_source(task); - qio_task_propagate_error(task, &data->err); -} - - -static void test_task_complete(void) -{ - QIOTask *task; - Object *obj = object_new(TYPE_DUMMY); - Object *src; - struct TestTaskData data = { NULL, NULL, false }; - - task = qio_task_new(obj, task_callback, &data, NULL); - src = qio_task_get_source(task); - - qio_task_complete(task); - - g_assert(obj == src); - - object_unref(obj); - - g_assert(data.source == obj); - g_assert(data.err == NULL); - g_assert(data.freed == false); -} - - -static void task_data_free(gpointer opaque) -{ - struct TestTaskData *data = opaque; - - data->freed = true; -} - - -static void test_task_data_free(void) -{ - QIOTask *task; - Object *obj = object_new(TYPE_DUMMY); - struct TestTaskData data = { NULL, NULL, false }; - - task = qio_task_new(obj, task_callback, &data, task_data_free); - - qio_task_complete(task); - - object_unref(obj); - - g_assert(data.source == obj); - g_assert(data.err == NULL); - g_assert(data.freed == true); -} - - -static void test_task_failure(void) -{ - QIOTask *task; - Object *obj = object_new(TYPE_DUMMY); - struct TestTaskData data = { NULL, NULL, false }; - Error *err = NULL; - - task = qio_task_new(obj, task_callback, &data, NULL); - - error_setg(&err, "Some error"); - - qio_task_set_error(task, err); - qio_task_complete(task); - - object_unref(obj); - - g_assert(data.source == obj); - g_assert(data.err == err); - g_assert(data.freed == false); - error_free(data.err); -} - - -struct TestThreadWorkerData { - Object *source; - Error *err; - bool fail; - GThread *worker; - GThread *complete; - GMainLoop *loop; -}; - -static void test_task_thread_worker(QIOTask *task, - gpointer opaque) -{ - struct TestThreadWorkerData *data = opaque; - - data->worker = g_thread_self(); - - if (data->fail) { - Error *err = NULL; - error_setg(&err, "Testing fail"); - qio_task_set_error(task, err); - } -} - - -static void test_task_thread_callback(QIOTask *task, - gpointer opaque) -{ - struct TestThreadWorkerData *data = opaque; - - data->source = qio_task_get_source(task); - qio_task_propagate_error(task, &data->err); - - data->complete = g_thread_self(); - - g_main_loop_quit(data->loop); -} - - -static void test_task_thread_complete(void) -{ - QIOTask *task; - Object *obj = object_new(TYPE_DUMMY); - struct TestThreadWorkerData data = { 0 }; - GThread *self; - - data.loop = g_main_loop_new(g_main_context_default(), - TRUE); - - task = qio_task_new(obj, - test_task_thread_callback, - &data, - NULL); - - qio_task_run_in_thread(task, - test_task_thread_worker, - &data, - NULL, - NULL); - - g_main_loop_run(data.loop); - - g_main_loop_unref(data.loop); - object_unref(obj); - - g_assert(data.source == obj); - g_assert(data.err == NULL); - - self = g_thread_self(); - - /* Make sure the test_task_thread_worker actually got - * run in a different thread */ - g_assert(data.worker != self); - - /* And that the test_task_thread_callback got rnu in - * the main loop thread (ie this one) */ - g_assert(data.complete == self); -} - - -static void test_task_thread_failure(void) -{ - QIOTask *task; - Object *obj = object_new(TYPE_DUMMY); - struct TestThreadWorkerData data = { 0 }; - GThread *self; - - data.loop = g_main_loop_new(g_main_context_default(), - TRUE); - data.fail = true; - - task = qio_task_new(obj, - test_task_thread_callback, - &data, - NULL); - - qio_task_run_in_thread(task, - test_task_thread_worker, - &data, - NULL, - NULL); - - g_main_loop_run(data.loop); - - g_main_loop_unref(data.loop); - object_unref(obj); - - g_assert(data.source == obj); - error_free_or_abort(&data.err); - - self = g_thread_self(); - - /* Make sure the test_task_thread_worker actually got - * run in a different thread */ - g_assert(data.worker != self); - - /* And that the test_task_thread_callback got rnu in - * the main loop thread (ie this one) */ - g_assert(data.complete == self); -} - - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - module_call_init(MODULE_INIT_QOM); - type_register_static(&dummy_info); - g_test_add_func("/crypto/task/complete", test_task_complete); - g_test_add_func("/crypto/task/datafree", test_task_data_free); - g_test_add_func("/crypto/task/failure", test_task_failure); - g_test_add_func("/crypto/task/thread_complete", test_task_thread_complete); - g_test_add_func("/crypto/task/thread_failure", test_task_thread_failure); - return g_test_run(); -} diff --git a/tests/test-iov.c b/tests/test-iov.c deleted file mode 100644 index 9c415e2f1f..0000000000 --- a/tests/test-iov.c +++ /dev/null @@ -1,581 +0,0 @@ -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "qemu/iov.h" -#include "qemu/sockets.h" - -/* create a randomly-sized iovec with random vectors */ -static void iov_random(struct iovec **iovp, unsigned *iov_cntp) -{ - unsigned niov = g_test_rand_int_range(3,8); - struct iovec *iov = g_malloc(niov * sizeof(*iov)); - unsigned i; - for (i = 0; i < niov; ++i) { - iov[i].iov_len = g_test_rand_int_range(5,20); - iov[i].iov_base = g_malloc(iov[i].iov_len); - } - *iovp = iov; - *iov_cntp = niov; -} - -static void iov_free(struct iovec *iov, unsigned niov) -{ - unsigned i; - for (i = 0; i < niov; ++i) { - g_free(iov[i].iov_base); - } - g_free(iov); -} - -static bool iov_equals(const struct iovec *a, const struct iovec *b, - unsigned niov) -{ - return memcmp(a, b, sizeof(a[0]) * niov) == 0; -} - -static void test_iov_bytes(struct iovec *iov, unsigned niov, - size_t offset, size_t bytes) -{ - unsigned i; - size_t j, o; - unsigned char *b; - o = 0; - - /* we walk over all elements, */ - for (i = 0; i < niov; ++i) { - b = iov[i].iov_base; - /* over each char of each element, */ - for (j = 0; j < iov[i].iov_len; ++j) { - /* counting each of them and - * verifying that the ones within [offset,offset+bytes) - * range are equal to the position number (o) */ - if (o >= offset && o < offset + bytes) { - g_assert(b[j] == (o & 255)); - } else { - g_assert(b[j] == 0xff); - } - ++o; - } - } -} - -static void test_to_from_buf_1(void) -{ - unsigned niov; - struct iovec *iov; - size_t sz; - unsigned char *ibuf, *obuf; - unsigned i, j, n; - - iov_random(&iov, &niov); - - sz = iov_size(iov, niov); - - ibuf = g_malloc(sz + 8) + 4; - memcpy(ibuf-4, "aaaa", 4); memcpy(ibuf + sz, "bbbb", 4); - obuf = g_malloc(sz + 8) + 4; - memcpy(obuf-4, "xxxx", 4); memcpy(obuf + sz, "yyyy", 4); - - /* fill in ibuf with 0123456... */ - for (i = 0; i < sz; ++i) { - ibuf[i] = i & 255; - } - - for (i = 0; i <= sz; ++i) { - - /* Test from/to buf for offset(i) in [0..sz] up to the end of buffer. - * For last iteration with offset == sz, the procedure should - * skip whole vector and process exactly 0 bytes */ - - /* first set bytes [i..sz) to some "random" value */ - n = iov_memset(iov, niov, 0, 0xff, sz); - g_assert(n == sz); - - /* next copy bytes [i..sz) from ibuf to iovec */ - n = iov_from_buf(iov, niov, i, ibuf + i, sz - i); - g_assert(n == sz - i); - - /* clear part of obuf */ - memset(obuf + i, 0, sz - i); - /* and set this part of obuf to values from iovec */ - n = iov_to_buf(iov, niov, i, obuf + i, sz - i); - g_assert(n == sz - i); - - /* now compare resulting buffers */ - g_assert(memcmp(ibuf, obuf, sz) == 0); - - /* test just one char */ - n = iov_to_buf(iov, niov, i, obuf + i, 1); - g_assert(n == (i < sz)); - if (n) { - g_assert(obuf[i] == (i & 255)); - } - - for (j = i; j <= sz; ++j) { - /* now test num of bytes cap up to byte no. j, - * with j in [i..sz]. */ - - /* clear iovec */ - n = iov_memset(iov, niov, 0, 0xff, sz); - g_assert(n == sz); - - /* copy bytes [i..j) from ibuf to iovec */ - n = iov_from_buf(iov, niov, i, ibuf + i, j - i); - g_assert(n == j - i); - - /* clear part of obuf */ - memset(obuf + i, 0, j - i); - - /* copy bytes [i..j) from iovec to obuf */ - n = iov_to_buf(iov, niov, i, obuf + i, j - i); - g_assert(n == j - i); - - /* verify result */ - g_assert(memcmp(ibuf, obuf, sz) == 0); - - /* now actually check if the iovec contains the right data */ - test_iov_bytes(iov, niov, i, j - i); - } - } - g_assert(!memcmp(ibuf-4, "aaaa", 4) && !memcmp(ibuf+sz, "bbbb", 4)); - g_free(ibuf-4); - g_assert(!memcmp(obuf-4, "xxxx", 4) && !memcmp(obuf+sz, "yyyy", 4)); - g_free(obuf-4); - iov_free(iov, niov); -} - -static void test_to_from_buf(void) -{ - int x; - for (x = 0; x < 4; ++x) { - test_to_from_buf_1(); - } -} - -static void test_io(void) -{ -#ifndef _WIN32 -/* socketpair(PF_UNIX) which does not exist on windows */ - - int sv[2]; - int r; - unsigned i, j, k, s, t; - fd_set fds; - unsigned niov; - struct iovec *iov, *siov; - unsigned char *buf; - size_t sz; - - iov_random(&iov, &niov); - sz = iov_size(iov, niov); - buf = g_malloc(sz); - for (i = 0; i < sz; ++i) { - buf[i] = i & 255; - } - iov_from_buf(iov, niov, 0, buf, sz); - - siov = g_memdup(iov, sizeof(*iov) * niov); - - if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) < 0) { - perror("socketpair"); - exit(1); - } - - FD_ZERO(&fds); - - t = 0; - if (fork() == 0) { - /* writer */ - - close(sv[0]); - FD_SET(sv[1], &fds); - fcntl(sv[1], F_SETFL, O_RDWR|O_NONBLOCK); - r = g_test_rand_int_range(sz / 2, sz); - setsockopt(sv[1], SOL_SOCKET, SO_SNDBUF, &r, sizeof(r)); - - for (i = 0; i <= sz; ++i) { - for (j = i; j <= sz; ++j) { - k = i; - do { - s = g_test_rand_int_range(0, j - k + 1); - r = iov_send(sv[1], iov, niov, k, s); - g_assert(memcmp(iov, siov, sizeof(*iov)*niov) == 0); - if (r >= 0) { - k += r; - t += r; - usleep(g_test_rand_int_range(0, 30)); - } else if (errno == EAGAIN) { - select(sv[1]+1, NULL, &fds, NULL, NULL); - continue; - } else { - perror("send"); - exit(1); - } - } while(k < j); - } - } - iov_free(iov, niov); - g_free(buf); - g_free(siov); - exit(0); - - } else { - /* reader & verifier */ - - close(sv[1]); - FD_SET(sv[0], &fds); - fcntl(sv[0], F_SETFL, O_RDWR|O_NONBLOCK); - r = g_test_rand_int_range(sz / 2, sz); - setsockopt(sv[0], SOL_SOCKET, SO_RCVBUF, &r, sizeof(r)); - usleep(500000); - - for (i = 0; i <= sz; ++i) { - for (j = i; j <= sz; ++j) { - k = i; - iov_memset(iov, niov, 0, 0xff, sz); - do { - s = g_test_rand_int_range(0, j - k + 1); - r = iov_recv(sv[0], iov, niov, k, s); - g_assert(memcmp(iov, siov, sizeof(*iov)*niov) == 0); - if (r > 0) { - k += r; - t += r; - } else if (!r) { - if (s) { - break; - } - } else if (errno == EAGAIN) { - select(sv[0]+1, &fds, NULL, NULL, NULL); - continue; - } else { - perror("recv"); - exit(1); - } - } while(k < j); - test_iov_bytes(iov, niov, i, j - i); - } - } - - iov_free(iov, niov); - g_free(buf); - g_free(siov); - } -#endif -} - -static void test_discard_front(void) -{ - struct iovec *iov; - struct iovec *iov_tmp; - unsigned int iov_cnt; - unsigned int iov_cnt_tmp; - void *old_base; - size_t size; - size_t ret; - - /* Discard zero bytes */ - iov_random(&iov, &iov_cnt); - iov_tmp = iov; - iov_cnt_tmp = iov_cnt; - ret = iov_discard_front(&iov_tmp, &iov_cnt_tmp, 0); - g_assert(ret == 0); - g_assert(iov_tmp == iov); - g_assert(iov_cnt_tmp == iov_cnt); - iov_free(iov, iov_cnt); - - /* Discard more bytes than vector size */ - iov_random(&iov, &iov_cnt); - iov_tmp = iov; - iov_cnt_tmp = iov_cnt; - size = iov_size(iov, iov_cnt); - ret = iov_discard_front(&iov_tmp, &iov_cnt_tmp, size + 1); - g_assert(ret == size); - g_assert(iov_cnt_tmp == 0); - iov_free(iov, iov_cnt); - - /* Discard entire vector */ - iov_random(&iov, &iov_cnt); - iov_tmp = iov; - iov_cnt_tmp = iov_cnt; - size = iov_size(iov, iov_cnt); - ret = iov_discard_front(&iov_tmp, &iov_cnt_tmp, size); - g_assert(ret == size); - g_assert(iov_cnt_tmp == 0); - iov_free(iov, iov_cnt); - - /* Discard within first element */ - iov_random(&iov, &iov_cnt); - iov_tmp = iov; - iov_cnt_tmp = iov_cnt; - old_base = iov->iov_base; - size = g_test_rand_int_range(1, iov->iov_len); - ret = iov_discard_front(&iov_tmp, &iov_cnt_tmp, size); - g_assert(ret == size); - g_assert(iov_tmp == iov); - g_assert(iov_cnt_tmp == iov_cnt); - g_assert(iov_tmp->iov_base == old_base + size); - iov_tmp->iov_base = old_base; /* undo before g_free() */ - iov_free(iov, iov_cnt); - - /* Discard entire first element */ - iov_random(&iov, &iov_cnt); - iov_tmp = iov; - iov_cnt_tmp = iov_cnt; - ret = iov_discard_front(&iov_tmp, &iov_cnt_tmp, iov->iov_len); - g_assert(ret == iov->iov_len); - g_assert(iov_tmp == iov + 1); - g_assert(iov_cnt_tmp == iov_cnt - 1); - iov_free(iov, iov_cnt); - - /* Discard within second element */ - iov_random(&iov, &iov_cnt); - iov_tmp = iov; - iov_cnt_tmp = iov_cnt; - old_base = iov[1].iov_base; - size = iov->iov_len + g_test_rand_int_range(1, iov[1].iov_len); - ret = iov_discard_front(&iov_tmp, &iov_cnt_tmp, size); - g_assert(ret == size); - g_assert(iov_tmp == iov + 1); - g_assert(iov_cnt_tmp == iov_cnt - 1); - g_assert(iov_tmp->iov_base == old_base + (size - iov->iov_len)); - iov_tmp->iov_base = old_base; /* undo before g_free() */ - iov_free(iov, iov_cnt); -} - -static void test_discard_front_undo(void) -{ - IOVDiscardUndo undo; - struct iovec *iov; - struct iovec *iov_tmp; - struct iovec *iov_orig; - unsigned int iov_cnt; - unsigned int iov_cnt_tmp; - size_t size; - - /* Discard zero bytes */ - iov_random(&iov, &iov_cnt); - iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt); - iov_tmp = iov; - iov_cnt_tmp = iov_cnt; - iov_discard_front_undoable(&iov_tmp, &iov_cnt_tmp, 0, &undo); - iov_discard_undo(&undo); - assert(iov_equals(iov, iov_orig, iov_cnt)); - g_free(iov_orig); - iov_free(iov, iov_cnt); - - /* Discard more bytes than vector size */ - iov_random(&iov, &iov_cnt); - iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt); - iov_tmp = iov; - iov_cnt_tmp = iov_cnt; - size = iov_size(iov, iov_cnt); - iov_discard_front_undoable(&iov_tmp, &iov_cnt_tmp, size + 1, &undo); - iov_discard_undo(&undo); - assert(iov_equals(iov, iov_orig, iov_cnt)); - g_free(iov_orig); - iov_free(iov, iov_cnt); - - /* Discard entire vector */ - iov_random(&iov, &iov_cnt); - iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt); - iov_tmp = iov; - iov_cnt_tmp = iov_cnt; - size = iov_size(iov, iov_cnt); - iov_discard_front_undoable(&iov_tmp, &iov_cnt_tmp, size, &undo); - iov_discard_undo(&undo); - assert(iov_equals(iov, iov_orig, iov_cnt)); - g_free(iov_orig); - iov_free(iov, iov_cnt); - - /* Discard within first element */ - iov_random(&iov, &iov_cnt); - iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt); - iov_tmp = iov; - iov_cnt_tmp = iov_cnt; - size = g_test_rand_int_range(1, iov->iov_len); - iov_discard_front_undoable(&iov_tmp, &iov_cnt_tmp, size, &undo); - iov_discard_undo(&undo); - assert(iov_equals(iov, iov_orig, iov_cnt)); - g_free(iov_orig); - iov_free(iov, iov_cnt); - - /* Discard entire first element */ - iov_random(&iov, &iov_cnt); - iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt); - iov_tmp = iov; - iov_cnt_tmp = iov_cnt; - iov_discard_front_undoable(&iov_tmp, &iov_cnt_tmp, iov->iov_len, &undo); - iov_discard_undo(&undo); - assert(iov_equals(iov, iov_orig, iov_cnt)); - g_free(iov_orig); - iov_free(iov, iov_cnt); - - /* Discard within second element */ - iov_random(&iov, &iov_cnt); - iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt); - iov_tmp = iov; - iov_cnt_tmp = iov_cnt; - size = iov->iov_len + g_test_rand_int_range(1, iov[1].iov_len); - iov_discard_front_undoable(&iov_tmp, &iov_cnt_tmp, size, &undo); - iov_discard_undo(&undo); - assert(iov_equals(iov, iov_orig, iov_cnt)); - g_free(iov_orig); - iov_free(iov, iov_cnt); -} - -static void test_discard_back(void) -{ - struct iovec *iov; - unsigned int iov_cnt; - unsigned int iov_cnt_tmp; - void *old_base; - size_t size; - size_t ret; - - /* Discard zero bytes */ - iov_random(&iov, &iov_cnt); - iov_cnt_tmp = iov_cnt; - ret = iov_discard_back(iov, &iov_cnt_tmp, 0); - g_assert(ret == 0); - g_assert(iov_cnt_tmp == iov_cnt); - iov_free(iov, iov_cnt); - - /* Discard more bytes than vector size */ - iov_random(&iov, &iov_cnt); - iov_cnt_tmp = iov_cnt; - size = iov_size(iov, iov_cnt); - ret = iov_discard_back(iov, &iov_cnt_tmp, size + 1); - g_assert(ret == size); - g_assert(iov_cnt_tmp == 0); - iov_free(iov, iov_cnt); - - /* Discard entire vector */ - iov_random(&iov, &iov_cnt); - iov_cnt_tmp = iov_cnt; - size = iov_size(iov, iov_cnt); - ret = iov_discard_back(iov, &iov_cnt_tmp, size); - g_assert(ret == size); - g_assert(iov_cnt_tmp == 0); - iov_free(iov, iov_cnt); - - /* Discard within last element */ - iov_random(&iov, &iov_cnt); - iov_cnt_tmp = iov_cnt; - old_base = iov[iov_cnt - 1].iov_base; - size = g_test_rand_int_range(1, iov[iov_cnt - 1].iov_len); - ret = iov_discard_back(iov, &iov_cnt_tmp, size); - g_assert(ret == size); - g_assert(iov_cnt_tmp == iov_cnt); - g_assert(iov[iov_cnt - 1].iov_base == old_base); - iov_free(iov, iov_cnt); - - /* Discard entire last element */ - iov_random(&iov, &iov_cnt); - iov_cnt_tmp = iov_cnt; - old_base = iov[iov_cnt - 1].iov_base; - size = iov[iov_cnt - 1].iov_len; - ret = iov_discard_back(iov, &iov_cnt_tmp, size); - g_assert(ret == size); - g_assert(iov_cnt_tmp == iov_cnt - 1); - iov_free(iov, iov_cnt); - - /* Discard within second-to-last element */ - iov_random(&iov, &iov_cnt); - iov_cnt_tmp = iov_cnt; - old_base = iov[iov_cnt - 2].iov_base; - size = iov[iov_cnt - 1].iov_len + - g_test_rand_int_range(1, iov[iov_cnt - 2].iov_len); - ret = iov_discard_back(iov, &iov_cnt_tmp, size); - g_assert(ret == size); - g_assert(iov_cnt_tmp == iov_cnt - 1); - g_assert(iov[iov_cnt - 2].iov_base == old_base); - iov_free(iov, iov_cnt); -} - -static void test_discard_back_undo(void) -{ - IOVDiscardUndo undo; - struct iovec *iov; - struct iovec *iov_orig; - unsigned int iov_cnt; - unsigned int iov_cnt_tmp; - size_t size; - - /* Discard zero bytes */ - iov_random(&iov, &iov_cnt); - iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt); - iov_cnt_tmp = iov_cnt; - iov_discard_back_undoable(iov, &iov_cnt_tmp, 0, &undo); - iov_discard_undo(&undo); - assert(iov_equals(iov, iov_orig, iov_cnt)); - g_free(iov_orig); - iov_free(iov, iov_cnt); - - /* Discard more bytes than vector size */ - iov_random(&iov, &iov_cnt); - iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt); - iov_cnt_tmp = iov_cnt; - size = iov_size(iov, iov_cnt); - iov_discard_back_undoable(iov, &iov_cnt_tmp, size + 1, &undo); - iov_discard_undo(&undo); - assert(iov_equals(iov, iov_orig, iov_cnt)); - g_free(iov_orig); - iov_free(iov, iov_cnt); - - /* Discard entire vector */ - iov_random(&iov, &iov_cnt); - iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt); - iov_cnt_tmp = iov_cnt; - size = iov_size(iov, iov_cnt); - iov_discard_back_undoable(iov, &iov_cnt_tmp, size, &undo); - iov_discard_undo(&undo); - assert(iov_equals(iov, iov_orig, iov_cnt)); - g_free(iov_orig); - iov_free(iov, iov_cnt); - - /* Discard within last element */ - iov_random(&iov, &iov_cnt); - iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt); - iov_cnt_tmp = iov_cnt; - size = g_test_rand_int_range(1, iov[iov_cnt - 1].iov_len); - iov_discard_back_undoable(iov, &iov_cnt_tmp, size, &undo); - iov_discard_undo(&undo); - assert(iov_equals(iov, iov_orig, iov_cnt)); - g_free(iov_orig); - iov_free(iov, iov_cnt); - - /* Discard entire last element */ - iov_random(&iov, &iov_cnt); - iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt); - iov_cnt_tmp = iov_cnt; - size = iov[iov_cnt - 1].iov_len; - iov_discard_back_undoable(iov, &iov_cnt_tmp, size, &undo); - iov_discard_undo(&undo); - assert(iov_equals(iov, iov_orig, iov_cnt)); - g_free(iov_orig); - iov_free(iov, iov_cnt); - - /* Discard within second-to-last element */ - iov_random(&iov, &iov_cnt); - iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt); - iov_cnt_tmp = iov_cnt; - size = iov[iov_cnt - 1].iov_len + - g_test_rand_int_range(1, iov[iov_cnt - 2].iov_len); - iov_discard_back_undoable(iov, &iov_cnt_tmp, size, &undo); - iov_discard_undo(&undo); - assert(iov_equals(iov, iov_orig, iov_cnt)); - g_free(iov_orig); - iov_free(iov, iov_cnt); -} - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - g_test_rand_int(); - g_test_add_func("/basic/iov/from-to-buf", test_to_from_buf); - g_test_add_func("/basic/iov/io", test_io); - g_test_add_func("/basic/iov/discard-front", test_discard_front); - g_test_add_func("/basic/iov/discard-back", test_discard_back); - g_test_add_func("/basic/iov/discard-front-undo", test_discard_front_undo); - g_test_add_func("/basic/iov/discard-back-undo", test_discard_back_undo); - return g_test_run(); -} diff --git a/tests/test-keyval.c b/tests/test-keyval.c deleted file mode 100644 index ee927fe4e4..0000000000 --- a/tests/test-keyval.c +++ /dev/null @@ -1,750 +0,0 @@ -/* - * Unit tests for parsing of KEY=VALUE,... strings - * - * Copyright (C) 2017 Red Hat Inc. - * - * Authors: - * Markus Armbruster , - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "qemu/units.h" -#include "qapi/error.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qstring.h" -#include "qapi/qobject-input-visitor.h" -#include "test-qapi-visit.h" -#include "qemu/cutils.h" -#include "qemu/option.h" - -static void test_keyval_parse(void) -{ - Error *err = NULL; - QDict *qdict, *sub_qdict; - char long_key[129]; - char *params; - bool help; - - /* Nothing */ - qdict = keyval_parse("", NULL, NULL, &error_abort); - g_assert_cmpuint(qdict_size(qdict), ==, 0); - qobject_unref(qdict); - - /* Empty key (qemu_opts_parse() accepts this) */ - qdict = keyval_parse("=val", NULL, NULL, &err); - error_free_or_abort(&err); - g_assert(!qdict); - - /* Empty key fragment */ - qdict = keyval_parse(".", NULL, NULL, &err); - error_free_or_abort(&err); - g_assert(!qdict); - qdict = keyval_parse("key.", NULL, NULL, &err); - error_free_or_abort(&err); - g_assert(!qdict); - - /* Invalid non-empty key (qemu_opts_parse() doesn't care) */ - qdict = keyval_parse("7up=val", NULL, NULL, &err); - error_free_or_abort(&err); - g_assert(!qdict); - - /* Overlong key */ - memset(long_key, 'a', 127); - long_key[127] = 'z'; - long_key[128] = 0; - params = g_strdup_printf("k.%s=v", long_key); - qdict = keyval_parse(params + 2, NULL, NULL, &err); - error_free_or_abort(&err); - g_assert(!qdict); - - /* Overlong key fragment */ - qdict = keyval_parse(params, NULL, NULL, &err); - error_free_or_abort(&err); - g_assert(!qdict); - g_free(params); - - /* Long key (qemu_opts_parse() accepts and truncates silently) */ - params = g_strdup_printf("k.%s=v", long_key + 1); - qdict = keyval_parse(params + 2, NULL, NULL, &error_abort); - g_assert_cmpuint(qdict_size(qdict), ==, 1); - g_assert_cmpstr(qdict_get_try_str(qdict, long_key + 1), ==, "v"); - qobject_unref(qdict); - - /* Long key fragment */ - qdict = keyval_parse(params, NULL, NULL, &error_abort); - g_assert_cmpuint(qdict_size(qdict), ==, 1); - sub_qdict = qdict_get_qdict(qdict, "k"); - g_assert(sub_qdict); - g_assert_cmpuint(qdict_size(sub_qdict), ==, 1); - g_assert_cmpstr(qdict_get_try_str(sub_qdict, long_key + 1), ==, "v"); - qobject_unref(qdict); - g_free(params); - - /* Crap after valid key */ - qdict = keyval_parse("key[0]=val", NULL, NULL, &err); - error_free_or_abort(&err); - g_assert(!qdict); - - /* Multiple keys, last one wins */ - qdict = keyval_parse("a=1,b=2,,x,a=3", NULL, NULL, &error_abort); - g_assert_cmpuint(qdict_size(qdict), ==, 2); - g_assert_cmpstr(qdict_get_try_str(qdict, "a"), ==, "3"); - g_assert_cmpstr(qdict_get_try_str(qdict, "b"), ==, "2,x"); - qobject_unref(qdict); - - /* Even when it doesn't in qemu_opts_parse() */ - qdict = keyval_parse("id=foo,id=bar", NULL, NULL, &error_abort); - g_assert_cmpuint(qdict_size(qdict), ==, 1); - g_assert_cmpstr(qdict_get_try_str(qdict, "id"), ==, "bar"); - qobject_unref(qdict); - - /* Dotted keys */ - qdict = keyval_parse("a.b.c=1,a.b.c=2,d=3", NULL, NULL, &error_abort); - g_assert_cmpuint(qdict_size(qdict), ==, 2); - sub_qdict = qdict_get_qdict(qdict, "a"); - g_assert(sub_qdict); - g_assert_cmpuint(qdict_size(sub_qdict), ==, 1); - sub_qdict = qdict_get_qdict(sub_qdict, "b"); - g_assert(sub_qdict); - g_assert_cmpuint(qdict_size(sub_qdict), ==, 1); - g_assert_cmpstr(qdict_get_try_str(sub_qdict, "c"), ==, "2"); - g_assert_cmpstr(qdict_get_try_str(qdict, "d"), ==, "3"); - qobject_unref(qdict); - - /* Inconsistent dotted keys */ - qdict = keyval_parse("a.b=1,a=2", NULL, NULL, &err); - error_free_or_abort(&err); - g_assert(!qdict); - qdict = keyval_parse("a.b=1,a.b.c=2", NULL, NULL, &err); - error_free_or_abort(&err); - g_assert(!qdict); - - /* Trailing comma is ignored */ - qdict = keyval_parse("x=y,", NULL, NULL, &error_abort); - g_assert_cmpuint(qdict_size(qdict), ==, 1); - g_assert_cmpstr(qdict_get_try_str(qdict, "x"), ==, "y"); - qobject_unref(qdict); - - /* Except when it isn't */ - qdict = keyval_parse(",", NULL, NULL, &err); - error_free_or_abort(&err); - g_assert(!qdict); - - /* Value containing ,id= not misinterpreted as qemu_opts_parse() does */ - qdict = keyval_parse("x=,,id=bar", NULL, NULL, &error_abort); - g_assert_cmpuint(qdict_size(qdict), ==, 1); - g_assert_cmpstr(qdict_get_try_str(qdict, "x"), ==, ",id=bar"); - qobject_unref(qdict); - - /* Anti-social ID is left to caller (qemu_opts_parse() rejects it) */ - qdict = keyval_parse("id=666", NULL, NULL, &error_abort); - g_assert_cmpuint(qdict_size(qdict), ==, 1); - g_assert_cmpstr(qdict_get_try_str(qdict, "id"), ==, "666"); - qobject_unref(qdict); - - /* Implied value not supported (unlike qemu_opts_parse()) */ - qdict = keyval_parse("an,noaus,noaus=", NULL, NULL, &err); - error_free_or_abort(&err); - g_assert(!qdict); - - /* Implied value, key "no" (qemu_opts_parse(): negated empty key) */ - qdict = keyval_parse("no", NULL, NULL, &err); - error_free_or_abort(&err); - g_assert(!qdict); - - /* Implied key */ - qdict = keyval_parse("an,aus=off,noaus=", "implied", NULL, &error_abort); - g_assert_cmpuint(qdict_size(qdict), ==, 3); - g_assert_cmpstr(qdict_get_try_str(qdict, "implied"), ==, "an"); - g_assert_cmpstr(qdict_get_try_str(qdict, "aus"), ==, "off"); - g_assert_cmpstr(qdict_get_try_str(qdict, "noaus"), ==, ""); - qobject_unref(qdict); - - /* Implied dotted key */ - qdict = keyval_parse("val", "eins.zwei", NULL, &error_abort); - g_assert_cmpuint(qdict_size(qdict), ==, 1); - sub_qdict = qdict_get_qdict(qdict, "eins"); - g_assert(sub_qdict); - g_assert_cmpuint(qdict_size(sub_qdict), ==, 1); - g_assert_cmpstr(qdict_get_try_str(sub_qdict, "zwei"), ==, "val"); - qobject_unref(qdict); - - /* Implied key with empty value (qemu_opts_parse() accepts this) */ - qdict = keyval_parse(",", "implied", NULL, &err); - error_free_or_abort(&err); - g_assert(!qdict); - - /* Likewise (qemu_opts_parse(): implied key with comma value) */ - qdict = keyval_parse(",,,a=1", "implied", NULL, &err); - error_free_or_abort(&err); - g_assert(!qdict); - - /* Implied key's value can't have comma (qemu_opts_parse(): it can) */ - qdict = keyval_parse("val,,ue", "implied", NULL, &err); - error_free_or_abort(&err); - g_assert(!qdict); - - /* Empty key is not an implied key */ - qdict = keyval_parse("=val", "implied", NULL, &err); - error_free_or_abort(&err); - g_assert(!qdict); - - /* "help" by itself, without implied key */ - qdict = keyval_parse("help", NULL, &help, &error_abort); - g_assert_cmpuint(qdict_size(qdict), ==, 0); - g_assert(help); - qobject_unref(qdict); - - /* "help" by itself, with implied key */ - qdict = keyval_parse("help", "implied", &help, &error_abort); - g_assert_cmpuint(qdict_size(qdict), ==, 0); - g_assert(help); - qobject_unref(qdict); - - /* "help" when no help is available, without implied key */ - qdict = keyval_parse("help", NULL, NULL, &err); - error_free_or_abort(&err); - g_assert(!qdict); - - /* "help" when no help is available, with implied key */ - qdict = keyval_parse("help", "implied", NULL, &err); - error_free_or_abort(&err); - g_assert(!qdict); - - /* Key "help" */ - qdict = keyval_parse("help=on", NULL, &help, &error_abort); - g_assert_cmpuint(qdict_size(qdict), ==, 1); - g_assert_cmpstr(qdict_get_try_str(qdict, "help"), ==, "on"); - g_assert(!help); - qobject_unref(qdict); - - /* "help" followed by crap, without implied key */ - qdict = keyval_parse("help.abc", NULL, &help, &err); - error_free_or_abort(&err); - g_assert(!qdict); - - /* "help" followed by crap, with implied key */ - qdict = keyval_parse("help.abc", "implied", &help, &err); - g_assert_cmpuint(qdict_size(qdict), ==, 1); - g_assert_cmpstr(qdict_get_try_str(qdict, "implied"), ==, "help.abc"); - g_assert(!help); - qobject_unref(qdict); - - /* "help" with other stuff, without implied key */ - qdict = keyval_parse("number=42,help,foo=bar", NULL, &help, &error_abort); - g_assert_cmpuint(qdict_size(qdict), ==, 2); - g_assert_cmpstr(qdict_get_try_str(qdict, "number"), ==, "42"); - g_assert_cmpstr(qdict_get_try_str(qdict, "foo"), ==, "bar"); - g_assert(help); - qobject_unref(qdict); - - /* "help" with other stuff, with implied key */ - qdict = keyval_parse("val,help,foo=bar", "implied", &help, &error_abort); - g_assert_cmpuint(qdict_size(qdict), ==, 2); - g_assert_cmpstr(qdict_get_try_str(qdict, "implied"), ==, "val"); - g_assert_cmpstr(qdict_get_try_str(qdict, "foo"), ==, "bar"); - g_assert(help); - qobject_unref(qdict); -} - -static void check_list012(QList *qlist) -{ - static const char *expected[] = { "null", "eins", "zwei" }; - int i; - QString *qstr; - - g_assert(qlist); - for (i = 0; i < ARRAY_SIZE(expected); i++) { - qstr = qobject_to(QString, qlist_pop(qlist)); - g_assert(qstr); - g_assert_cmpstr(qstring_get_str(qstr), ==, expected[i]); - qobject_unref(qstr); - } - g_assert(qlist_empty(qlist)); -} - -static void test_keyval_parse_list(void) -{ - Error *err = NULL; - QDict *qdict, *sub_qdict; - - /* Root can't be a list */ - qdict = keyval_parse("0=1", NULL, NULL, &err); - error_free_or_abort(&err); - g_assert(!qdict); - - /* List elements need not be in order */ - qdict = keyval_parse("list.0=null,list.2=zwei,list.1=eins", NULL, NULL, - &error_abort); - g_assert_cmpint(qdict_size(qdict), ==, 1); - check_list012(qdict_get_qlist(qdict, "list")); - qobject_unref(qdict); - - /* Multiple indexes, last one wins */ - qdict = keyval_parse("list.1=goner,list.0=null,list.01=eins,list.2=zwei", - NULL, NULL, &error_abort); - g_assert_cmpint(qdict_size(qdict), ==, 1); - check_list012(qdict_get_qlist(qdict, "list")); - qobject_unref(qdict); - - /* List at deeper nesting */ - qdict = keyval_parse("a.list.1=eins,a.list.00=null,a.list.2=zwei", NULL, - NULL, &error_abort); - g_assert_cmpint(qdict_size(qdict), ==, 1); - sub_qdict = qdict_get_qdict(qdict, "a"); - g_assert_cmpint(qdict_size(sub_qdict), ==, 1); - check_list012(qdict_get_qlist(sub_qdict, "list")); - qobject_unref(qdict); - - /* Inconsistent dotted keys: both list and dictionary */ - qdict = keyval_parse("a.b.c=1,a.b.0=2", NULL, NULL, &err); - error_free_or_abort(&err); - g_assert(!qdict); - qdict = keyval_parse("a.0.c=1,a.b.c=2", NULL, NULL, &err); - error_free_or_abort(&err); - g_assert(!qdict); - - /* Missing list indexes */ - qdict = keyval_parse("list.1=lonely", NULL, NULL, &err); - error_free_or_abort(&err); - g_assert(!qdict); - qdict = keyval_parse("list.0=null,list.2=eins,list.02=zwei", NULL, NULL, - &err); - error_free_or_abort(&err); - g_assert(!qdict); -} - -static void test_keyval_visit_bool(void) -{ - Error *err = NULL; - Visitor *v; - QDict *qdict; - bool b; - - qdict = keyval_parse("bool1=on,bool2=off", NULL, NULL, &error_abort); - v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - qobject_unref(qdict); - visit_start_struct(v, NULL, NULL, 0, &error_abort); - visit_type_bool(v, "bool1", &b, &error_abort); - g_assert(b); - visit_type_bool(v, "bool2", &b, &error_abort); - g_assert(!b); - visit_check_struct(v, &error_abort); - visit_end_struct(v, NULL); - visit_free(v); - - qdict = keyval_parse("bool1=offer", NULL, NULL, &error_abort); - v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - qobject_unref(qdict); - visit_start_struct(v, NULL, NULL, 0, &error_abort); - visit_type_bool(v, "bool1", &b, &err); - error_free_or_abort(&err); - visit_end_struct(v, NULL); - visit_free(v); -} - -static void test_keyval_visit_number(void) -{ - Error *err = NULL; - Visitor *v; - QDict *qdict; - uint64_t u; - - /* Lower limit zero */ - qdict = keyval_parse("number1=0", NULL, NULL, &error_abort); - v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - qobject_unref(qdict); - visit_start_struct(v, NULL, NULL, 0, &error_abort); - visit_type_uint64(v, "number1", &u, &error_abort); - g_assert_cmpuint(u, ==, 0); - visit_check_struct(v, &error_abort); - visit_end_struct(v, NULL); - visit_free(v); - - /* Upper limit 2^64-1 */ - qdict = keyval_parse("number1=18446744073709551615,number2=-1", NULL, - NULL, &error_abort); - v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - qobject_unref(qdict); - visit_start_struct(v, NULL, NULL, 0, &error_abort); - visit_type_uint64(v, "number1", &u, &error_abort); - g_assert_cmphex(u, ==, UINT64_MAX); - visit_type_uint64(v, "number2", &u, &error_abort); - g_assert_cmphex(u, ==, UINT64_MAX); - visit_check_struct(v, &error_abort); - visit_end_struct(v, NULL); - visit_free(v); - - /* Above upper limit */ - qdict = keyval_parse("number1=18446744073709551616", NULL, NULL, - &error_abort); - v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - qobject_unref(qdict); - visit_start_struct(v, NULL, NULL, 0, &error_abort); - visit_type_uint64(v, "number1", &u, &err); - error_free_or_abort(&err); - visit_end_struct(v, NULL); - visit_free(v); - - /* Below lower limit */ - qdict = keyval_parse("number1=-18446744073709551616", NULL, NULL, - &error_abort); - v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - qobject_unref(qdict); - visit_start_struct(v, NULL, NULL, 0, &error_abort); - visit_type_uint64(v, "number1", &u, &err); - error_free_or_abort(&err); - visit_end_struct(v, NULL); - visit_free(v); - - /* Hex and octal */ - qdict = keyval_parse("number1=0x2a,number2=052", NULL, NULL, &error_abort); - v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - qobject_unref(qdict); - visit_start_struct(v, NULL, NULL, 0, &error_abort); - visit_type_uint64(v, "number1", &u, &error_abort); - g_assert_cmpuint(u, ==, 42); - visit_type_uint64(v, "number2", &u, &error_abort); - g_assert_cmpuint(u, ==, 42); - visit_check_struct(v, &error_abort); - visit_end_struct(v, NULL); - visit_free(v); - - /* Trailing crap */ - qdict = keyval_parse("number1=3.14,number2=08", NULL, NULL, &error_abort); - v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - qobject_unref(qdict); - visit_start_struct(v, NULL, NULL, 0, &error_abort); - visit_type_uint64(v, "number1", &u, &err); - error_free_or_abort(&err); - visit_type_uint64(v, "number2", &u, &err); - error_free_or_abort(&err); - visit_end_struct(v, NULL); - visit_free(v); -} - -static void test_keyval_visit_size(void) -{ - Error *err = NULL; - Visitor *v; - QDict *qdict; - uint64_t sz; - - /* Lower limit zero */ - qdict = keyval_parse("sz1=0", NULL, NULL, &error_abort); - v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - qobject_unref(qdict); - visit_start_struct(v, NULL, NULL, 0, &error_abort); - visit_type_size(v, "sz1", &sz, &error_abort); - g_assert_cmpuint(sz, ==, 0); - visit_check_struct(v, &error_abort); - visit_end_struct(v, NULL); - visit_free(v); - - /* Note: precision is 53 bits since we're parsing with strtod() */ - - /* Around limit of precision: 2^53-1, 2^53, 2^53+1 */ - qdict = keyval_parse("sz1=9007199254740991," - "sz2=9007199254740992," - "sz3=9007199254740993", - NULL, NULL, &error_abort); - v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - qobject_unref(qdict); - visit_start_struct(v, NULL, NULL, 0, &error_abort); - visit_type_size(v, "sz1", &sz, &error_abort); - g_assert_cmphex(sz, ==, 0x1fffffffffffff); - visit_type_size(v, "sz2", &sz, &error_abort); - g_assert_cmphex(sz, ==, 0x20000000000000); - visit_type_size(v, "sz3", &sz, &error_abort); - g_assert_cmphex(sz, ==, 0x20000000000000); - visit_check_struct(v, &error_abort); - visit_end_struct(v, NULL); - visit_free(v); - - /* Close to signed upper limit 0x7ffffffffffffc00 (53 msbs set) */ - qdict = keyval_parse("sz1=9223372036854774784," /* 7ffffffffffffc00 */ - "sz2=9223372036854775295", /* 7ffffffffffffdff */ - NULL, NULL, &error_abort); - v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - qobject_unref(qdict); - visit_start_struct(v, NULL, NULL, 0, &error_abort); - visit_type_size(v, "sz1", &sz, &error_abort); - g_assert_cmphex(sz, ==, 0x7ffffffffffffc00); - visit_type_size(v, "sz2", &sz, &error_abort); - g_assert_cmphex(sz, ==, 0x7ffffffffffffc00); - visit_check_struct(v, &error_abort); - visit_end_struct(v, NULL); - visit_free(v); - - /* Close to actual upper limit 0xfffffffffffff800 (53 msbs set) */ - qdict = keyval_parse("sz1=18446744073709549568," /* fffffffffffff800 */ - "sz2=18446744073709550591", /* fffffffffffffbff */ - NULL, NULL, &error_abort); - v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - qobject_unref(qdict); - visit_start_struct(v, NULL, NULL, 0, &error_abort); - visit_type_size(v, "sz1", &sz, &error_abort); - g_assert_cmphex(sz, ==, 0xfffffffffffff800); - visit_type_size(v, "sz2", &sz, &error_abort); - g_assert_cmphex(sz, ==, 0xfffffffffffff800); - visit_check_struct(v, &error_abort); - visit_end_struct(v, NULL); - visit_free(v); - - /* Beyond limits */ - qdict = keyval_parse("sz1=-1," - "sz2=18446744073709550592", /* fffffffffffffc00 */ - NULL, NULL, &error_abort); - v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - qobject_unref(qdict); - visit_start_struct(v, NULL, NULL, 0, &error_abort); - visit_type_size(v, "sz1", &sz, &err); - error_free_or_abort(&err); - visit_type_size(v, "sz2", &sz, &err); - error_free_or_abort(&err); - visit_end_struct(v, NULL); - visit_free(v); - - /* Suffixes */ - qdict = keyval_parse("sz1=8b,sz2=1.5k,sz3=2M,sz4=0.1G,sz5=16777215T", - NULL, NULL, &error_abort); - v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - qobject_unref(qdict); - visit_start_struct(v, NULL, NULL, 0, &error_abort); - visit_type_size(v, "sz1", &sz, &error_abort); - g_assert_cmpuint(sz, ==, 8); - visit_type_size(v, "sz2", &sz, &error_abort); - g_assert_cmpuint(sz, ==, 1536); - visit_type_size(v, "sz3", &sz, &error_abort); - g_assert_cmphex(sz, ==, 2 * MiB); - visit_type_size(v, "sz4", &sz, &error_abort); - g_assert_cmphex(sz, ==, GiB / 10); - visit_type_size(v, "sz5", &sz, &error_abort); - g_assert_cmphex(sz, ==, 16777215ULL * TiB); - visit_check_struct(v, &error_abort); - visit_end_struct(v, NULL); - visit_free(v); - - /* Beyond limit with suffix */ - qdict = keyval_parse("sz1=16777216T", NULL, NULL, &error_abort); - v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - qobject_unref(qdict); - visit_start_struct(v, NULL, NULL, 0, &error_abort); - visit_type_size(v, "sz1", &sz, &err); - error_free_or_abort(&err); - visit_end_struct(v, NULL); - visit_free(v); - - /* Trailing crap */ - qdict = keyval_parse("sz1=0Z,sz2=16Gi", NULL, NULL, &error_abort); - v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - qobject_unref(qdict); - visit_start_struct(v, NULL, NULL, 0, &error_abort); - visit_type_size(v, "sz1", &sz, &err); - error_free_or_abort(&err); - visit_type_size(v, "sz2", &sz, &err); - error_free_or_abort(&err); - visit_end_struct(v, NULL); - visit_free(v); -} - -static void test_keyval_visit_dict(void) -{ - Error *err = NULL; - Visitor *v; - QDict *qdict; - int64_t i; - - qdict = keyval_parse("a.b.c=1,a.b.c=2,d=3", NULL, NULL, &error_abort); - v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - qobject_unref(qdict); - visit_start_struct(v, NULL, NULL, 0, &error_abort); - visit_start_struct(v, "a", NULL, 0, &error_abort); - visit_start_struct(v, "b", NULL, 0, &error_abort); - visit_type_int(v, "c", &i, &error_abort); - g_assert_cmpint(i, ==, 2); - visit_check_struct(v, &error_abort); - visit_end_struct(v, NULL); - visit_check_struct(v, &error_abort); - visit_end_struct(v, NULL); - visit_type_int(v, "d", &i, &error_abort); - g_assert_cmpint(i, ==, 3); - visit_check_struct(v, &error_abort); - visit_end_struct(v, NULL); - visit_free(v); - - qdict = keyval_parse("a.b=", NULL, NULL, &error_abort); - v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - qobject_unref(qdict); - visit_start_struct(v, NULL, NULL, 0, &error_abort); - visit_start_struct(v, "a", NULL, 0, &error_abort); - visit_type_int(v, "c", &i, &err); /* a.c missing */ - error_free_or_abort(&err); - visit_check_struct(v, &err); - error_free_or_abort(&err); /* a.b unexpected */ - visit_end_struct(v, NULL); - visit_check_struct(v, &error_abort); - visit_end_struct(v, NULL); - visit_free(v); -} - -static void test_keyval_visit_list(void) -{ - Error *err = NULL; - Visitor *v; - QDict *qdict; - char *s; - - qdict = keyval_parse("a.0=,a.1=I,a.2.0=II", NULL, NULL, &error_abort); - /* TODO empty list */ - v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - qobject_unref(qdict); - visit_start_struct(v, NULL, NULL, 0, &error_abort); - visit_start_list(v, "a", NULL, 0, &error_abort); - visit_type_str(v, NULL, &s, &error_abort); - g_assert_cmpstr(s, ==, ""); - g_free(s); - visit_type_str(v, NULL, &s, &error_abort); - g_assert_cmpstr(s, ==, "I"); - g_free(s); - visit_start_list(v, NULL, NULL, 0, &error_abort); - visit_type_str(v, NULL, &s, &error_abort); - g_assert_cmpstr(s, ==, "II"); - g_free(s); - visit_check_list(v, &error_abort); - visit_end_list(v, NULL); - visit_check_list(v, &error_abort); - visit_end_list(v, NULL); - visit_check_struct(v, &error_abort); - visit_end_struct(v, NULL); - visit_free(v); - - qdict = keyval_parse("a.0=,b.0.0=head", NULL, NULL, &error_abort); - v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - qobject_unref(qdict); - visit_start_struct(v, NULL, NULL, 0, &error_abort); - visit_start_list(v, "a", NULL, 0, &error_abort); - visit_check_list(v, &err); /* a[0] unexpected */ - error_free_or_abort(&err); - visit_end_list(v, NULL); - visit_start_list(v, "b", NULL, 0, &error_abort); - visit_start_list(v, NULL, NULL, 0, &error_abort); - visit_type_str(v, NULL, &s, &error_abort); - g_assert_cmpstr(s, ==, "head"); - g_free(s); - visit_type_str(v, NULL, &s, &err); /* b[0][1] missing */ - error_free_or_abort(&err); - visit_end_list(v, NULL); - visit_end_list(v, NULL); - visit_check_struct(v, &error_abort); - visit_end_struct(v, NULL); - visit_free(v); -} - -static void test_keyval_visit_optional(void) -{ - Visitor *v; - QDict *qdict; - bool present; - int64_t i; - - qdict = keyval_parse("a.b=1", NULL, NULL, &error_abort); - v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - qobject_unref(qdict); - visit_start_struct(v, NULL, NULL, 0, &error_abort); - visit_optional(v, "b", &present); - g_assert(!present); /* b missing */ - visit_optional(v, "a", &present); - g_assert(present); /* a present */ - visit_start_struct(v, "a", NULL, 0, &error_abort); - visit_optional(v, "b", &present); - g_assert(present); /* a.b present */ - visit_type_int(v, "b", &i, &error_abort); - g_assert_cmpint(i, ==, 1); - visit_optional(v, "a", &present); - g_assert(!present); /* a.a missing */ - visit_check_struct(v, &error_abort); - visit_end_struct(v, NULL); - visit_check_struct(v, &error_abort); - visit_end_struct(v, NULL); - visit_free(v); -} - -static void test_keyval_visit_alternate(void) -{ - Error *err = NULL; - Visitor *v; - QDict *qdict; - AltStrObj *aso; - AltNumEnum *ane; - AltEnumBool *aeb; - - /* - * Can't do scalar alternate variants other than string. You get - * the string variant if there is one, else an error. - * TODO make it work for unambiguous cases like AltEnumBool below - */ - qdict = keyval_parse("a=1,b=2,c=on", NULL, NULL, &error_abort); - v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - qobject_unref(qdict); - visit_start_struct(v, NULL, NULL, 0, &error_abort); - visit_type_AltStrObj(v, "a", &aso, &error_abort); - g_assert_cmpint(aso->type, ==, QTYPE_QSTRING); - g_assert_cmpstr(aso->u.s, ==, "1"); - qapi_free_AltStrObj(aso); - visit_type_AltNumEnum(v, "b", &ane, &err); - error_free_or_abort(&err); - visit_type_AltEnumBool(v, "c", &aeb, &err); - error_free_or_abort(&err); - visit_end_struct(v, NULL); - visit_free(v); -} - -static void test_keyval_visit_any(void) -{ - Visitor *v; - QDict *qdict; - QObject *any; - QList *qlist; - QString *qstr; - - qdict = keyval_parse("a.0=null,a.1=1", NULL, NULL, &error_abort); - v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - qobject_unref(qdict); - visit_start_struct(v, NULL, NULL, 0, &error_abort); - visit_type_any(v, "a", &any, &error_abort); - qlist = qobject_to(QList, any); - g_assert(qlist); - qstr = qobject_to(QString, qlist_pop(qlist)); - g_assert_cmpstr(qstring_get_str(qstr), ==, "null"); - qobject_unref(qstr); - qstr = qobject_to(QString, qlist_pop(qlist)); - g_assert_cmpstr(qstring_get_str(qstr), ==, "1"); - g_assert(qlist_empty(qlist)); - qobject_unref(qstr); - qobject_unref(any); - visit_check_struct(v, &error_abort); - visit_end_struct(v, NULL); - visit_free(v); -} - -int main(int argc, char *argv[]) -{ - g_test_init(&argc, &argv, NULL); - g_test_add_func("/keyval/keyval_parse", test_keyval_parse); - g_test_add_func("/keyval/keyval_parse/list", test_keyval_parse_list); - g_test_add_func("/keyval/visit/bool", test_keyval_visit_bool); - g_test_add_func("/keyval/visit/number", test_keyval_visit_number); - g_test_add_func("/keyval/visit/size", test_keyval_visit_size); - g_test_add_func("/keyval/visit/dict", test_keyval_visit_dict); - g_test_add_func("/keyval/visit/list", test_keyval_visit_list); - g_test_add_func("/keyval/visit/optional", test_keyval_visit_optional); - g_test_add_func("/keyval/visit/alternate", test_keyval_visit_alternate); - g_test_add_func("/keyval/visit/any", test_keyval_visit_any); - g_test_run(); - return 0; -} diff --git a/tests/test-logging.c b/tests/test-logging.c deleted file mode 100644 index ccb819f193..0000000000 --- a/tests/test-logging.c +++ /dev/null @@ -1,218 +0,0 @@ -/* - * logging unit-tests - * - * Copyright (C) 2016 Linaro Ltd. - * - * Author: Alex Bennée - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include - -#include "qemu-common.h" -#include "qapi/error.h" -#include "qemu/log.h" - -static void test_parse_range(void) -{ - Error *err = NULL; - - qemu_set_dfilter_ranges("0x1000+0x100", &error_abort); - - g_assert_false(qemu_log_in_addr_range(0xfff)); - g_assert(qemu_log_in_addr_range(0x1000)); - g_assert(qemu_log_in_addr_range(0x1001)); - g_assert(qemu_log_in_addr_range(0x10ff)); - g_assert_false(qemu_log_in_addr_range(0x1100)); - - qemu_set_dfilter_ranges("0x1000-0x100", &error_abort); - - g_assert_false(qemu_log_in_addr_range(0x1001)); - g_assert(qemu_log_in_addr_range(0x1000)); - g_assert(qemu_log_in_addr_range(0x0f01)); - g_assert_false(qemu_log_in_addr_range(0x0f00)); - - qemu_set_dfilter_ranges("0x1000..0x1100", &error_abort); - - g_assert_false(qemu_log_in_addr_range(0xfff)); - g_assert(qemu_log_in_addr_range(0x1000)); - g_assert(qemu_log_in_addr_range(0x1100)); - g_assert_false(qemu_log_in_addr_range(0x1101)); - - qemu_set_dfilter_ranges("0x1000..0x1000", &error_abort); - - g_assert_false(qemu_log_in_addr_range(0xfff)); - g_assert(qemu_log_in_addr_range(0x1000)); - g_assert_false(qemu_log_in_addr_range(0x1001)); - - qemu_set_dfilter_ranges("0x1000+0x100,0x2100-0x100,0x3000..0x3100", - &error_abort); - g_assert(qemu_log_in_addr_range(0x1050)); - g_assert(qemu_log_in_addr_range(0x2050)); - g_assert(qemu_log_in_addr_range(0x3050)); - - qemu_set_dfilter_ranges("0xffffffffffffffff-1", &error_abort); - g_assert(qemu_log_in_addr_range(UINT64_MAX)); - g_assert_false(qemu_log_in_addr_range(UINT64_MAX - 1)); - - qemu_set_dfilter_ranges("0..0xffffffffffffffff", &error_abort); - g_assert(qemu_log_in_addr_range(0)); - g_assert(qemu_log_in_addr_range(UINT64_MAX)); - - qemu_set_dfilter_ranges("2..1", &err); - error_free_or_abort(&err); - - qemu_set_dfilter_ranges("0x1000+onehundred", &err); - error_free_or_abort(&err); - - qemu_set_dfilter_ranges("0x1000+0", &err); - error_free_or_abort(&err); -} - -static void set_log_path_tmp(char const *dir, char const *tpl, Error **errp) -{ - gchar *file_path = g_build_filename(dir, tpl, NULL); - - qemu_set_log_filename(file_path, errp); - g_free(file_path); -} - -static void test_parse_path(gconstpointer data) -{ - gchar const *tmp_path = data; - Error *err = NULL; - - set_log_path_tmp(tmp_path, "qemu.log", &error_abort); - set_log_path_tmp(tmp_path, "qemu-%d.log", &error_abort); - set_log_path_tmp(tmp_path, "qemu.log.%d", &error_abort); - - set_log_path_tmp(tmp_path, "qemu-%d%d.log", &err); - error_free_or_abort(&err); -} - -static void test_logfile_write(gconstpointer data) -{ - QemuLogFile *logfile; - QemuLogFile *logfile2; - gchar const *dir = data; - g_autofree gchar *file_path = NULL; - g_autofree gchar *file_path1 = NULL; - FILE *orig_fd; - - /* - * Before starting test, set log flags, to ensure the file gets - * opened below with the call to qemu_set_log_filename(). - * In cases where a logging backend other than log is used, - * this is needed. - */ - qemu_set_log(CPU_LOG_TB_OUT_ASM); - file_path = g_build_filename(dir, "qemu_test_log_write0.log", NULL); - file_path1 = g_build_filename(dir, "qemu_test_log_write1.log", NULL); - - /* - * Test that even if an open file handle is changed, - * our handle remains valid due to RCU. - */ - qemu_set_log_filename(file_path, &error_abort); - rcu_read_lock(); - logfile = qatomic_rcu_read(&qemu_logfile); - orig_fd = logfile->fd; - g_assert(logfile && logfile->fd); - fprintf(logfile->fd, "%s 1st write to file\n", __func__); - fflush(logfile->fd); - - /* Change the logfile and ensure that the handle is still valid. */ - qemu_set_log_filename(file_path1, &error_abort); - logfile2 = qatomic_rcu_read(&qemu_logfile); - g_assert(logfile->fd == orig_fd); - g_assert(logfile2->fd != logfile->fd); - fprintf(logfile->fd, "%s 2nd write to file\n", __func__); - fflush(logfile->fd); - rcu_read_unlock(); -} - -static void test_logfile_lock(gconstpointer data) -{ - FILE *logfile; - gchar const *dir = data; - g_autofree gchar *file_path = NULL; - - file_path = g_build_filename(dir, "qemu_test_logfile_lock0.log", NULL); - - /* - * Test the use of the logfile lock, such - * that even if an open file handle is closed, - * our handle remains valid for use due to RCU. - */ - qemu_set_log_filename(file_path, &error_abort); - logfile = qemu_log_lock(); - g_assert(logfile); - fprintf(logfile, "%s 1st write to file\n", __func__); - fflush(logfile); - - /* - * Initiate a close file and make sure our handle remains - * valid since we still have the logfile lock. - */ - qemu_log_close(); - fprintf(logfile, "%s 2nd write to file\n", __func__); - fflush(logfile); - qemu_log_unlock(logfile); -} - -/* Remove a directory and all its entries (non-recursive). */ -static void rmdir_full(gchar const *root) -{ - GDir *root_gdir = g_dir_open(root, 0, NULL); - gchar const *entry_name; - - g_assert_nonnull(root_gdir); - while ((entry_name = g_dir_read_name(root_gdir)) != NULL) { - gchar *entry_path = g_build_filename(root, entry_name, NULL); - g_assert(g_remove(entry_path) == 0); - g_free(entry_path); - } - g_dir_close(root_gdir); - g_assert(g_rmdir(root) == 0); -} - -int main(int argc, char **argv) -{ - g_autofree gchar *tmp_path = g_dir_make_tmp("qemu-test-logging.XXXXXX", NULL); - int rc; - - g_test_init(&argc, &argv, NULL); - g_assert_nonnull(tmp_path); - - g_test_add_func("/logging/parse_range", test_parse_range); - g_test_add_data_func("/logging/parse_path", tmp_path, test_parse_path); - g_test_add_data_func("/logging/logfile_write_path", - tmp_path, test_logfile_write); - g_test_add_data_func("/logging/logfile_lock_path", - tmp_path, test_logfile_lock); - - rc = g_test_run(); - qemu_log_close(); - drain_call_rcu(); - - rmdir_full(tmp_path); - return rc; -} diff --git a/tests/test-mul64.c b/tests/test-mul64.c deleted file mode 100644 index 9be775d084..0000000000 --- a/tests/test-mul64.c +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Test 64x64 -> 128 multiply subroutines - * - * This work is licensed under the terms of the GNU LGPL, version 2 or later. - * See the COPYING.LIB file in the top-level directory. - * - */ - -#include "qemu/osdep.h" -#include "qemu/host-utils.h" - - -typedef struct { - uint64_t a, b; - uint64_t rh, rl; -} Test; - -static const Test test_u_data[] = { - { 1, 1, 0, 1 }, - { 10000, 10000, 0, 100000000 }, - { 0xffffffffffffffffULL, 2, 1, 0xfffffffffffffffeULL }, - { 0xffffffffffffffffULL, 0xffffffffffffffffULL, - 0xfffffffffffffffeULL, 0x0000000000000001ULL }, - { 0x1122334455667788ull, 0x8877665544332211ull, - 0x092228fb777ae38full, 0x0a3e963337c60008ull }, -}; - -static const Test test_s_data[] = { - { 1, 1, 0, 1 }, - { 1, -1, -1, -1 }, - { -10, -10, 0, 100 }, - { 10000, 10000, 0, 100000000 }, - { -1, 2, -1, -2 }, - { 0x1122334455667788ULL, 0x1122334455667788ULL, - 0x01258f60bbc2975cULL, 0x1eace4a3c82fb840ULL }, -}; - -static void test_u(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(test_u_data); ++i) { - uint64_t rl, rh; - mulu64(&rl, &rh, test_u_data[i].a, test_u_data[i].b); - g_assert_cmpuint(rl, ==, test_u_data[i].rl); - g_assert_cmpuint(rh, ==, test_u_data[i].rh); - } -} - -static void test_s(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(test_s_data); ++i) { - uint64_t rl, rh; - muls64(&rl, &rh, test_s_data[i].a, test_s_data[i].b); - g_assert_cmpuint(rl, ==, test_s_data[i].rl); - g_assert_cmpint(rh, ==, test_s_data[i].rh); - } -} - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - g_test_add_func("/host-utils/mulu64", test_u); - g_test_add_func("/host-utils/muls64", test_s); - return g_test_run(); -} diff --git a/tests/test-opts-visitor.c b/tests/test-opts-visitor.c deleted file mode 100644 index 23e897061c..0000000000 --- a/tests/test-opts-visitor.c +++ /dev/null @@ -1,371 +0,0 @@ -/* - * Options Visitor unit-tests. - * - * Copyright (C) 2013 Red Hat, Inc. - * - * Authors: - * Laszlo Ersek (based on test-string-output-visitor) - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" - -#include "qemu/config-file.h" /* qemu_add_opts() */ -#include "qemu/option.h" /* qemu_opts_parse() */ -#include "qapi/error.h" -#include "qapi/opts-visitor.h" /* opts_visitor_new() */ -#include "test-qapi-visit.h" /* visit_type_UserDefOptions() */ - -static QemuOptsList userdef_opts = { - .name = "userdef", - .head = QTAILQ_HEAD_INITIALIZER(userdef_opts.head), - .desc = { { 0 } } /* validated with OptsVisitor */ -}; - -/* fixture (= glib test case context) and test case manipulation */ - -typedef struct OptsVisitorFixture { - UserDefOptions *userdef; - Error *err; -} OptsVisitorFixture; - - -static void -setup_fixture(OptsVisitorFixture *f, gconstpointer test_data) -{ - const char *opts_string = test_data; - QemuOpts *opts; - Visitor *v; - - opts = qemu_opts_parse(qemu_find_opts("userdef"), opts_string, false, - NULL); - g_assert(opts != NULL); - - v = opts_visitor_new(opts); - visit_type_UserDefOptions(v, NULL, &f->userdef, &f->err); - visit_free(v); - qemu_opts_del(opts); -} - - -static void -teardown_fixture(OptsVisitorFixture *f, gconstpointer test_data) -{ - qapi_free_UserDefOptions(f->userdef); - error_free(f->err); -} - - -static void -add_test(const char *testpath, - void (*test_func)(OptsVisitorFixture *f, gconstpointer test_data), - gconstpointer test_data) -{ - g_test_add(testpath, OptsVisitorFixture, test_data, setup_fixture, - test_func, teardown_fixture); -} - -/* test output evaluation */ - -static void -expect_ok(OptsVisitorFixture *f, gconstpointer test_data) -{ - g_assert(f->err == NULL); - g_assert(f->userdef != NULL); -} - - -static void -expect_fail(OptsVisitorFixture *f, gconstpointer test_data) -{ - g_assert(f->err != NULL); - - /* The error message is printed when this test utility is invoked directly - * (ie. without gtester) and the --verbose flag is passed: - * - * tests/test-opts-visitor --verbose - */ - g_test_message("'%s': %s", (const char *)test_data, - error_get_pretty(f->err)); -} - - -static void -test_value(OptsVisitorFixture *f, gconstpointer test_data) -{ - uint64_t magic, bitval; - intList *i64; - uint64List *u64; - uint16List *u16; - - expect_ok(f, test_data); - - magic = 0; - for (i64 = f->userdef->i64; i64 != NULL; i64 = i64->next) { - g_assert(-16 <= i64->value && i64->value < 64-16); - bitval = 1ull << (i64->value + 16); - g_assert((magic & bitval) == 0); - magic |= bitval; - } - g_assert(magic == 0xDEADBEEF); - - magic = 0; - for (u64 = f->userdef->u64; u64 != NULL; u64 = u64->next) { - g_assert(u64->value < 64); - bitval = 1ull << u64->value; - g_assert((magic & bitval) == 0); - magic |= bitval; - } - g_assert(magic == 0xBADC0FFEE0DDF00DULL); - - magic = 0; - for (u16 = f->userdef->u16; u16 != NULL; u16 = u16->next) { - g_assert(u16->value < 64); - bitval = 1ull << u16->value; - g_assert((magic & bitval) == 0); - magic |= bitval; - } - g_assert(magic == 0xD15EA5E); -} - - -static void -expect_i64_min(OptsVisitorFixture *f, gconstpointer test_data) -{ - expect_ok(f, test_data); - g_assert(f->userdef->has_i64); - g_assert(f->userdef->i64->next == NULL); - g_assert(f->userdef->i64->value == INT64_MIN); -} - - -static void -expect_i64_max(OptsVisitorFixture *f, gconstpointer test_data) -{ - expect_ok(f, test_data); - g_assert(f->userdef->has_i64); - g_assert(f->userdef->i64->next == NULL); - g_assert(f->userdef->i64->value == INT64_MAX); -} - - -static void -expect_zero(OptsVisitorFixture *f, gconstpointer test_data) -{ - expect_ok(f, test_data); - g_assert(f->userdef->has_u64); - g_assert(f->userdef->u64->next == NULL); - g_assert(f->userdef->u64->value == 0); -} - - -static void -expect_u64_max(OptsVisitorFixture *f, gconstpointer test_data) -{ - expect_ok(f, test_data); - g_assert(f->userdef->has_u64); - g_assert(f->userdef->u64->next == NULL); - g_assert(f->userdef->u64->value == UINT64_MAX); -} - -/* test cases */ - -static void -test_opts_range_unvisited(void) -{ - Error *err = NULL; - intList *list = NULL; - intList *tail; - QemuOpts *opts; - Visitor *v; - - opts = qemu_opts_parse(qemu_find_opts("userdef"), "ilist=0-2", false, - &error_abort); - - v = opts_visitor_new(opts); - - visit_start_struct(v, NULL, NULL, 0, &error_abort); - - /* Would be simpler if the visitor genuinely supported virtual walks */ - visit_start_list(v, "ilist", (GenericList **)&list, sizeof(*list), - &error_abort); - tail = list; - visit_type_int(v, NULL, &tail->value, &error_abort); - g_assert_cmpint(tail->value, ==, 0); - tail = (intList *)visit_next_list(v, (GenericList *)tail, sizeof(*list)); - g_assert(tail); - visit_type_int(v, NULL, &tail->value, &error_abort); - g_assert_cmpint(tail->value, ==, 1); - tail = (intList *)visit_next_list(v, (GenericList *)tail, sizeof(*list)); - g_assert(tail); - visit_check_list(v, &error_abort); /* unvisited tail ignored until... */ - visit_end_list(v, (void **)&list); - - visit_check_struct(v, &err); /* ...here */ - error_free_or_abort(&err); - visit_end_struct(v, NULL); - - qapi_free_intList(list); - visit_free(v); - qemu_opts_del(opts); -} - -static void -test_opts_range_beyond(void) -{ - Error *err = NULL; - intList *list = NULL; - intList *tail; - QemuOpts *opts; - Visitor *v; - int64_t val; - - opts = qemu_opts_parse(qemu_find_opts("userdef"), "ilist=0", false, - &error_abort); - - v = opts_visitor_new(opts); - - visit_start_struct(v, NULL, NULL, 0, &error_abort); - - /* Would be simpler if the visitor genuinely supported virtual walks */ - visit_start_list(v, "ilist", (GenericList **)&list, sizeof(*list), - &error_abort); - tail = list; - visit_type_int(v, NULL, &tail->value, &error_abort); - g_assert_cmpint(tail->value, ==, 0); - tail = (intList *)visit_next_list(v, (GenericList *)tail, sizeof(*tail)); - g_assert(!tail); - visit_type_int(v, NULL, &val, &err); - error_free_or_abort(&err); - visit_end_list(v, (void **)&list); - - visit_check_struct(v, &error_abort); - visit_end_struct(v, NULL); - - qapi_free_intList(list); - visit_free(v); - qemu_opts_del(opts); -} - -static void -test_opts_dict_unvisited(void) -{ - Error *err = NULL; - QemuOpts *opts; - Visitor *v; - UserDefOptions *userdef; - - opts = qemu_opts_parse(qemu_find_opts("userdef"), "i64x=0,bogus=1", false, - &error_abort); - - v = opts_visitor_new(opts); - visit_type_UserDefOptions(v, NULL, &userdef, &err); - error_free_or_abort(&err); - visit_free(v); - qemu_opts_del(opts); - g_assert(!userdef); -} - -int -main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - - qemu_add_opts(&userdef_opts); - - /* Three hexadecimal magic numbers, "dead beef", "bad coffee, odd food" and - * "disease", from - * , were - * converted to binary and dissected into bit ranges. Each magic number is - * going to be recomposed using the lists called "i64", "u64" and "u16", - * respectively. - * - * (Note that these types pertain to the individual bit shift counts, not - * the magic numbers themselves; the intent is to exercise opts_type_int() - * and opts_type_uint64().) - * - * The "i64" shift counts have been decreased by 16 (decimal) in order to - * test negative values as well. Finally, the full list of QemuOpt elements - * has been permuted with "shuf". - * - * Both "i64" and "u64" have some (distinct) single-element ranges - * represented as both "a" and "a-a". "u16" is a special case of "i64" (see - * visit_type_uint16()), so it wouldn't add a separate test in this regard. - */ - - add_test("/visitor/opts/flatten/value", &test_value, - "i64=-1-0,u64=12-16,u64=2-3,i64=-11--9,u64=57,u16=9,i64=5-5," - "u16=1-4,u16=20,u64=63-63,i64=-16--13,u64=50-52,i64=14-15,u16=11," - "i64=7,u16=18,i64=2-3,u16=6,u64=54-55,u64=0,u64=18-20,u64=33-43," - "i64=9-12,u16=26-27,u64=59-61,u16=13-16,u64=29-31,u64=22-23," - "u16=24,i64=-7--3"); - - add_test("/visitor/opts/i64/val1/errno", &expect_fail, - "i64=0x8000000000000000"); - add_test("/visitor/opts/i64/val1/empty", &expect_fail, "i64="); - add_test("/visitor/opts/i64/val1/trailing", &expect_fail, "i64=5z"); - add_test("/visitor/opts/i64/nonlist", &expect_fail, "i64x=5-6"); - add_test("/visitor/opts/i64/val2/errno", &expect_fail, - "i64=0x7fffffffffffffff-0x8000000000000000"); - add_test("/visitor/opts/i64/val2/empty", &expect_fail, "i64=5-"); - add_test("/visitor/opts/i64/val2/trailing", &expect_fail, "i64=5-6z"); - add_test("/visitor/opts/i64/range/empty", &expect_fail, "i64=6-5"); - add_test("/visitor/opts/i64/range/minval", &expect_i64_min, - "i64=-0x8000000000000000--0x8000000000000000"); - add_test("/visitor/opts/i64/range/maxval", &expect_i64_max, - "i64=0x7fffffffffffffff-0x7fffffffffffffff"); - - add_test("/visitor/opts/u64/val1/errno", &expect_fail, "u64=-1"); - add_test("/visitor/opts/u64/val1/empty", &expect_fail, "u64="); - add_test("/visitor/opts/u64/val1/trailing", &expect_fail, "u64=5z"); - add_test("/visitor/opts/u64/nonlist", &expect_fail, "u64x=5-6"); - add_test("/visitor/opts/u64/val2/errno", &expect_fail, - "u64=0xffffffffffffffff-0x10000000000000000"); - add_test("/visitor/opts/u64/val2/empty", &expect_fail, "u64=5-"); - add_test("/visitor/opts/u64/val2/trailing", &expect_fail, "u64=5-6z"); - add_test("/visitor/opts/u64/range/empty", &expect_fail, "u64=6-5"); - add_test("/visitor/opts/u64/range/minval", &expect_zero, "u64=0-0"); - add_test("/visitor/opts/u64/range/maxval", &expect_u64_max, - "u64=0xffffffffffffffff-0xffffffffffffffff"); - - /* Test maximum range sizes. The macro value is open-coded here - * *intentionally*; the test case must use concrete values by design. If - * OPTS_VISITOR_RANGE_MAX is changed, the following values need to be - * recalculated as well. The assert and this comment should help with it. - */ - g_assert(OPTS_VISITOR_RANGE_MAX == 65536); - - /* The unsigned case is simple, a u64-u64 difference can always be - * represented as a u64. - */ - add_test("/visitor/opts/u64/range/max", &expect_ok, "u64=0-65535"); - add_test("/visitor/opts/u64/range/2big", &expect_fail, "u64=0-65536"); - - /* The same cannot be said about an i64-i64 difference. */ - add_test("/visitor/opts/i64/range/max/pos/a", &expect_ok, - "i64=0x7fffffffffff0000-0x7fffffffffffffff"); - add_test("/visitor/opts/i64/range/max/pos/b", &expect_ok, - "i64=0x7ffffffffffeffff-0x7ffffffffffffffe"); - add_test("/visitor/opts/i64/range/2big/pos", &expect_fail, - "i64=0x7ffffffffffeffff-0x7fffffffffffffff"); - add_test("/visitor/opts/i64/range/max/neg/a", &expect_ok, - "i64=-0x8000000000000000--0x7fffffffffff0001"); - add_test("/visitor/opts/i64/range/max/neg/b", &expect_ok, - "i64=-0x7fffffffffffffff--0x7fffffffffff0000"); - add_test("/visitor/opts/i64/range/2big/neg", &expect_fail, - "i64=-0x8000000000000000--0x7fffffffffff0000"); - add_test("/visitor/opts/i64/range/2big/full", &expect_fail, - "i64=-0x8000000000000000-0x7fffffffffffffff"); - - g_test_add_func("/visitor/opts/range/unvisited", - test_opts_range_unvisited); - g_test_add_func("/visitor/opts/range/beyond", - test_opts_range_beyond); - - g_test_add_func("/visitor/opts/dict/unvisited", test_opts_dict_unvisited); - - g_test_run(); - return 0; -} diff --git a/tests/test-qapi-util.c b/tests/test-qapi-util.c deleted file mode 100644 index 847f305cff..0000000000 --- a/tests/test-qapi-util.c +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Unit tests for QAPI utility functions - * - * Copyright (C) 2017 Red Hat Inc. - * - * Authors: - * Markus Armbruster , - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" - -static void test_qapi_enum_parse(void) -{ - Error *err = NULL; - int ret; - - ret = qapi_enum_parse(&QType_lookup, NULL, QTYPE_NONE, &error_abort); - g_assert_cmpint(ret, ==, QTYPE_NONE); - - ret = qapi_enum_parse(&QType_lookup, "junk", -1, NULL); - g_assert_cmpint(ret, ==, -1); - - ret = qapi_enum_parse(&QType_lookup, "junk", -1, &err); - error_free_or_abort(&err); - - ret = qapi_enum_parse(&QType_lookup, "none", -1, &error_abort); - g_assert_cmpint(ret, ==, QTYPE_NONE); - - ret = qapi_enum_parse(&QType_lookup, QType_str(QTYPE__MAX - 1), - QTYPE__MAX - 1, &error_abort); - g_assert_cmpint(ret, ==, QTYPE__MAX - 1); -} - -static void test_parse_qapi_name(void) -{ - int ret; - - /* Must start with a letter */ - ret = parse_qapi_name("a", true); - g_assert(ret == 1); - ret = parse_qapi_name("a$", false); - g_assert(ret == 1); - ret = parse_qapi_name("", false); - g_assert(ret == -1); - ret = parse_qapi_name("1", false); - g_assert(ret == -1); - - /* Only letters, digits, hyphen, underscore */ - ret = parse_qapi_name("A-Za-z0-9_", true); - g_assert(ret == 10); - ret = parse_qapi_name("A-Za-z0-9_$", false); - g_assert(ret == 10); - ret = parse_qapi_name("A-Za-z0-9_$", true); - g_assert(ret == -1); - - /* __RFQDN_ */ - ret = parse_qapi_name("__com.redhat_supports", true); - g_assert(ret == 21); - ret = parse_qapi_name("_com.example_", false); - g_assert(ret == -1); - ret = parse_qapi_name("__com.example", false); - g_assert(ret == -1); - ret = parse_qapi_name("__com.example_", false); - g_assert(ret == -1); -} - -int main(int argc, char *argv[]) -{ - g_test_init(&argc, &argv, NULL); - g_test_add_func("/qapi/util/qapi_enum_parse", test_qapi_enum_parse); - g_test_add_func("/qapi/util/parse_qapi_name", test_parse_qapi_name); - g_test_run(); - return 0; -} diff --git a/tests/test-qdev-global-props.c b/tests/test-qdev-global-props.c deleted file mode 100644 index c8862cac5f..0000000000 --- a/tests/test-qdev-global-props.c +++ /dev/null @@ -1,319 +0,0 @@ -/* - * Test code for qdev global-properties handling - * - * Copyright (c) 2012 Red Hat Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" - -#include "hw/qdev-properties.h" -#include "qom/object.h" -#include "qapi/error.h" -#include "qapi/visitor.h" - - -#define TYPE_STATIC_PROPS "static_prop_type" -typedef struct MyType MyType; -DECLARE_INSTANCE_CHECKER(MyType, STATIC_TYPE, - TYPE_STATIC_PROPS) - -#define TYPE_SUBCLASS "static_prop_subtype" - -#define PROP_DEFAULT 100 - -struct MyType { - DeviceState parent_obj; - - uint32_t prop1; - uint32_t prop2; -}; - -static Property static_props[] = { - DEFINE_PROP_UINT32("prop1", MyType, prop1, PROP_DEFAULT), - DEFINE_PROP_UINT32("prop2", MyType, prop2, PROP_DEFAULT), - DEFINE_PROP_END_OF_LIST() -}; - -static void static_prop_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - - dc->realize = NULL; - device_class_set_props(dc, static_props); -} - -static const TypeInfo static_prop_type = { - .name = TYPE_STATIC_PROPS, - .parent = TYPE_DEVICE, - .instance_size = sizeof(MyType), - .class_init = static_prop_class_init, -}; - -static const TypeInfo subclass_type = { - .name = TYPE_SUBCLASS, - .parent = TYPE_STATIC_PROPS, -}; - -/* Test simple static property setting to default value */ -static void test_static_prop_subprocess(void) -{ - MyType *mt; - - mt = STATIC_TYPE(object_new(TYPE_STATIC_PROPS)); - qdev_realize(DEVICE(mt), NULL, &error_fatal); - - g_assert_cmpuint(mt->prop1, ==, PROP_DEFAULT); -} - -static void test_static_prop(void) -{ - g_test_trap_subprocess("/qdev/properties/static/default/subprocess", 0, 0); - g_test_trap_assert_passed(); - g_test_trap_assert_stderr(""); - g_test_trap_assert_stdout(""); -} - -static void register_global_properties(GlobalProperty *props) -{ - int i; - - for (i = 0; props[i].driver != NULL; i++) { - qdev_prop_register_global(props + i); - } -} - - -/* Test setting of static property using global properties */ -static void test_static_globalprop_subprocess(void) -{ - MyType *mt; - static GlobalProperty props[] = { - { TYPE_STATIC_PROPS, "prop1", "200" }, - {} - }; - - register_global_properties(props); - - mt = STATIC_TYPE(object_new(TYPE_STATIC_PROPS)); - qdev_realize(DEVICE(mt), NULL, &error_fatal); - - g_assert_cmpuint(mt->prop1, ==, 200); - g_assert_cmpuint(mt->prop2, ==, PROP_DEFAULT); -} - -static void test_static_globalprop(void) -{ - g_test_trap_subprocess("/qdev/properties/static/global/subprocess", 0, 0); - g_test_trap_assert_passed(); - g_test_trap_assert_stderr(""); - g_test_trap_assert_stdout(""); -} - -#define TYPE_DYNAMIC_PROPS "dynamic-prop-type" -DECLARE_INSTANCE_CHECKER(MyType, DYNAMIC_TYPE, - TYPE_DYNAMIC_PROPS) - -#define TYPE_UNUSED_HOTPLUG "hotplug-type" -#define TYPE_UNUSED_NOHOTPLUG "nohotplug-type" - -static void prop1_accessor(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - MyType *mt = DYNAMIC_TYPE(obj); - - visit_type_uint32(v, name, &mt->prop1, errp); -} - -static void prop2_accessor(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - MyType *mt = DYNAMIC_TYPE(obj); - - visit_type_uint32(v, name, &mt->prop2, errp); -} - -static void dynamic_instance_init(Object *obj) -{ - object_property_add(obj, "prop1", "uint32", prop1_accessor, prop1_accessor, - NULL, NULL); - object_property_add(obj, "prop2", "uint32", prop2_accessor, prop2_accessor, - NULL, NULL); -} - -static void dynamic_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - - dc->realize = NULL; -} - - -static const TypeInfo dynamic_prop_type = { - .name = TYPE_DYNAMIC_PROPS, - .parent = TYPE_DEVICE, - .instance_size = sizeof(MyType), - .instance_init = dynamic_instance_init, - .class_init = dynamic_class_init, -}; - -static void hotplug_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - - dc->realize = NULL; - dc->hotpluggable = true; -} - -static const TypeInfo hotplug_type = { - .name = TYPE_UNUSED_HOTPLUG, - .parent = TYPE_DEVICE, - .instance_size = sizeof(MyType), - .instance_init = dynamic_instance_init, - .class_init = hotplug_class_init, -}; - -static void nohotplug_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - - dc->realize = NULL; - dc->hotpluggable = false; -} - -static const TypeInfo nohotplug_type = { - .name = TYPE_UNUSED_NOHOTPLUG, - .parent = TYPE_DEVICE, - .instance_size = sizeof(MyType), - .instance_init = dynamic_instance_init, - .class_init = nohotplug_class_init, -}; - -#define TYPE_NONDEVICE "nondevice-type" - -static const TypeInfo nondevice_type = { - .name = TYPE_NONDEVICE, - .parent = TYPE_OBJECT, -}; - -/* Test setting of dynamic properties using global properties */ -static void test_dynamic_globalprop_subprocess(void) -{ - MyType *mt; - static GlobalProperty props[] = { - { TYPE_DYNAMIC_PROPS, "prop1", "101", }, - { TYPE_DYNAMIC_PROPS, "prop2", "102", }, - { TYPE_DYNAMIC_PROPS"-bad", "prop3", "103", }, - { TYPE_UNUSED_HOTPLUG, "prop4", "104", }, - { TYPE_UNUSED_NOHOTPLUG, "prop5", "105", }, - { TYPE_NONDEVICE, "prop6", "106", }, - {} - }; - int global_error; - - register_global_properties(props); - - mt = DYNAMIC_TYPE(object_new(TYPE_DYNAMIC_PROPS)); - qdev_realize(DEVICE(mt), NULL, &error_fatal); - - g_assert_cmpuint(mt->prop1, ==, 101); - g_assert_cmpuint(mt->prop2, ==, 102); - global_error = qdev_prop_check_globals(); - g_assert_cmpuint(global_error, ==, 1); - g_assert(props[0].used); - g_assert(props[1].used); - g_assert(!props[2].used); - g_assert(!props[3].used); - g_assert(!props[4].used); - g_assert(!props[5].used); -} - -static void test_dynamic_globalprop(void) -{ - g_test_trap_subprocess("/qdev/properties/dynamic/global/subprocess", 0, 0); - g_test_trap_assert_passed(); - g_test_trap_assert_stderr_unmatched("*prop1*"); - g_test_trap_assert_stderr_unmatched("*prop2*"); - g_test_trap_assert_stderr( - "*warning: global dynamic-prop-type-bad.prop3 has invalid class name*"); - g_test_trap_assert_stderr_unmatched("*prop4*"); - g_test_trap_assert_stderr( - "*warning: global nohotplug-type.prop5=105 not used*"); - g_test_trap_assert_stderr( - "*warning: global nondevice-type.prop6 has invalid class name*"); - g_test_trap_assert_stdout(""); -} - -/* Test if global props affecting subclasses are applied in the right order */ -static void test_subclass_global_props(void) -{ - MyType *mt; - /* Global properties must be applied in the order they were registered */ - static GlobalProperty props[] = { - { TYPE_STATIC_PROPS, "prop1", "101" }, - { TYPE_SUBCLASS, "prop1", "102" }, - { TYPE_SUBCLASS, "prop2", "103" }, - { TYPE_STATIC_PROPS, "prop2", "104" }, - {} - }; - - register_global_properties(props); - - mt = STATIC_TYPE(object_new(TYPE_SUBCLASS)); - qdev_realize(DEVICE(mt), NULL, &error_fatal); - - g_assert_cmpuint(mt->prop1, ==, 102); - g_assert_cmpuint(mt->prop2, ==, 104); -} - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - - module_call_init(MODULE_INIT_QOM); - type_register_static(&static_prop_type); - type_register_static(&subclass_type); - type_register_static(&dynamic_prop_type); - type_register_static(&hotplug_type); - type_register_static(&nohotplug_type); - type_register_static(&nondevice_type); - - g_test_add_func("/qdev/properties/static/default/subprocess", - test_static_prop_subprocess); - g_test_add_func("/qdev/properties/static/default", - test_static_prop); - - g_test_add_func("/qdev/properties/static/global/subprocess", - test_static_globalprop_subprocess); - g_test_add_func("/qdev/properties/static/global", - test_static_globalprop); - - g_test_add_func("/qdev/properties/dynamic/global/subprocess", - test_dynamic_globalprop_subprocess); - g_test_add_func("/qdev/properties/dynamic/global", - test_dynamic_globalprop); - - g_test_add_func("/qdev/properties/global/subclass", - test_subclass_global_props); - - g_test_run(); - - return 0; -} diff --git a/tests/test-qdist.c b/tests/test-qdist.c deleted file mode 100644 index 9541ce31eb..0000000000 --- a/tests/test-qdist.c +++ /dev/null @@ -1,389 +0,0 @@ -/* - * Copyright (C) 2016, Emilio G. Cota - * - * License: GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ -#include "qemu/osdep.h" -#include "qemu/qdist.h" - -#include - -struct entry_desc { - double x; - unsigned long count; - - /* 0 prints a space, 1-8 prints from qdist_blocks[] */ - int fill_code; -}; - -/* See: https://en.wikipedia.org/wiki/Block_Elements */ -static const gunichar qdist_blocks[] = { - 0x2581, - 0x2582, - 0x2583, - 0x2584, - 0x2585, - 0x2586, - 0x2587, - 0x2588 -}; - -#define QDIST_NR_BLOCK_CODES ARRAY_SIZE(qdist_blocks) - -static char *pr_hist(const struct entry_desc *darr, size_t n) -{ - GString *s = g_string_new(""); - size_t i; - - for (i = 0; i < n; i++) { - int fill = darr[i].fill_code; - - if (fill) { - assert(fill <= QDIST_NR_BLOCK_CODES); - g_string_append_unichar(s, qdist_blocks[fill - 1]); - } else { - g_string_append_c(s, ' '); - } - } - return g_string_free(s, FALSE); -} - -static void -histogram_check(const struct qdist *dist, const struct entry_desc *darr, - size_t n, size_t n_bins) -{ - char *pr = qdist_pr_plain(dist, n_bins); - char *str = pr_hist(darr, n); - - g_assert_cmpstr(pr, ==, str); - g_free(pr); - g_free(str); -} - -static void histogram_check_single_full(const struct qdist *dist, size_t n_bins) -{ - struct entry_desc desc = { .fill_code = 8 }; - - histogram_check(dist, &desc, 1, n_bins); -} - -static void -entries_check(const struct qdist *dist, const struct entry_desc *darr, size_t n) -{ - size_t i; - - for (i = 0; i < n; i++) { - struct qdist_entry *e = &dist->entries[i]; - - g_assert_cmpuint(e->count, ==, darr[i].count); - } -} - -static void -entries_insert(struct qdist *dist, const struct entry_desc *darr, size_t n) -{ - size_t i; - - for (i = 0; i < n; i++) { - qdist_add(dist, darr[i].x, darr[i].count); - } -} - -static void do_test_bin(const struct entry_desc *a, size_t n_a, - const struct entry_desc *b, size_t n_b) -{ - struct qdist qda; - struct qdist qdb; - - qdist_init(&qda); - - entries_insert(&qda, a, n_a); - qdist_inc(&qda, a[0].x); - qdist_add(&qda, a[0].x, -1); - - g_assert_cmpuint(qdist_unique_entries(&qda), ==, n_a); - g_assert_cmpfloat(qdist_xmin(&qda), ==, a[0].x); - g_assert_cmpfloat(qdist_xmax(&qda), ==, a[n_a - 1].x); - histogram_check(&qda, a, n_a, 0); - histogram_check(&qda, a, n_a, n_a); - - qdist_bin__internal(&qdb, &qda, n_b); - g_assert_cmpuint(qdb.n, ==, n_b); - entries_check(&qdb, b, n_b); - g_assert_cmpuint(qdist_sample_count(&qda), ==, qdist_sample_count(&qdb)); - /* - * No histogram_check() for $qdb, since we'd rebin it and that is a bug. - * Instead, regenerate it from $qda. - */ - histogram_check(&qda, b, n_b, n_b); - - qdist_destroy(&qdb); - qdist_destroy(&qda); -} - -static void do_test_pr(uint32_t opt) -{ - static const struct entry_desc desc[] = { - [0] = { 1, 900, 8 }, - [1] = { 2, 1, 1 }, - [2] = { 3, 2, 1 } - }; - static const char border[] = "|"; - const char *llabel = NULL; - const char *rlabel = NULL; - struct qdist dist; - GString *s; - char *str; - char *pr; - size_t n; - - n = ARRAY_SIZE(desc); - qdist_init(&dist); - - entries_insert(&dist, desc, n); - histogram_check(&dist, desc, n, 0); - - s = g_string_new(""); - - if (opt & QDIST_PR_LABELS) { - unsigned int lopts = opt & (QDIST_PR_NODECIMAL | - QDIST_PR_PERCENT | - QDIST_PR_100X | - QDIST_PR_NOBINRANGE); - - if (lopts == 0) { - llabel = "[1.0,1.7)"; - rlabel = "[2.3,3.0]"; - } else if (lopts == QDIST_PR_NODECIMAL) { - llabel = "[1,2)"; - rlabel = "[2,3]"; - } else if (lopts == (QDIST_PR_PERCENT | QDIST_PR_NODECIMAL)) { - llabel = "[1,2)%"; - rlabel = "[2,3]%"; - } else if (lopts == QDIST_PR_100X) { - llabel = "[100.0,166.7)"; - rlabel = "[233.3,300.0]"; - } else if (lopts == (QDIST_PR_NOBINRANGE | QDIST_PR_NODECIMAL)) { - llabel = "1"; - rlabel = "3"; - } else { - g_assert_cmpstr("BUG", ==, "This is not meant to be exhaustive"); - } - } - - if (llabel) { - g_string_append(s, llabel); - } - if (opt & QDIST_PR_BORDER) { - g_string_append(s, border); - } - - str = pr_hist(desc, n); - g_string_append(s, str); - g_free(str); - - if (opt & QDIST_PR_BORDER) { - g_string_append(s, border); - } - if (rlabel) { - g_string_append(s, rlabel); - } - - str = g_string_free(s, FALSE); - pr = qdist_pr(&dist, n, opt); - g_assert_cmpstr(pr, ==, str); - g_free(pr); - g_free(str); - - qdist_destroy(&dist); -} - -static inline void do_test_pr_label(uint32_t opt) -{ - opt |= QDIST_PR_LABELS; - do_test_pr(opt); -} - -static void test_pr(void) -{ - do_test_pr(0); - - do_test_pr(QDIST_PR_BORDER); - - /* 100X should be ignored because we're not setting LABELS */ - do_test_pr(QDIST_PR_100X); - - do_test_pr_label(0); - do_test_pr_label(QDIST_PR_NODECIMAL); - do_test_pr_label(QDIST_PR_PERCENT | QDIST_PR_NODECIMAL); - do_test_pr_label(QDIST_PR_100X); - do_test_pr_label(QDIST_PR_NOBINRANGE | QDIST_PR_NODECIMAL); -} - -static void test_bin_shrink(void) -{ - static const struct entry_desc a[] = { - [0] = { 0.0, 42922, 7 }, - [1] = { 0.25, 47834, 8 }, - [2] = { 0.50, 26628, 0 }, - [3] = { 0.625, 597, 4 }, - [4] = { 0.75, 10298, 1 }, - [5] = { 0.875, 22, 2 }, - [6] = { 1.0, 2771, 1 } - }; - static const struct entry_desc b[] = { - [0] = { 0.0, 42922, 7 }, - [1] = { 0.25, 47834, 8 }, - [2] = { 0.50, 27225, 3 }, - [3] = { 0.75, 13091, 1 } - }; - - return do_test_bin(a, ARRAY_SIZE(a), b, ARRAY_SIZE(b)); -} - -static void test_bin_expand(void) -{ - static const struct entry_desc a[] = { - [0] = { 0.0, 11713, 5 }, - [1] = { 0.25, 20294, 0 }, - [2] = { 0.50, 17266, 8 }, - [3] = { 0.625, 1506, 0 }, - [4] = { 0.75, 10355, 6 }, - [5] = { 0.833, 2, 1 }, - [6] = { 0.875, 99, 4 }, - [7] = { 1.0, 4301, 2 } - }; - static const struct entry_desc b[] = { - [0] = { 0.0, 11713, 5 }, - [1] = { 0.0, 0, 0 }, - [2] = { 0.0, 20294, 8 }, - [3] = { 0.0, 0, 0 }, - [4] = { 0.0, 0, 0 }, - [5] = { 0.0, 17266, 6 }, - [6] = { 0.0, 1506, 1 }, - [7] = { 0.0, 10355, 4 }, - [8] = { 0.0, 101, 1 }, - [9] = { 0.0, 4301, 2 } - }; - - return do_test_bin(a, ARRAY_SIZE(a), b, ARRAY_SIZE(b)); -} - -static void test_bin_precision(void) -{ - static const struct entry_desc a[] = { - [0] = { 0, 213549, 8 }, - [1] = { 1, 70, 1 }, - }; - static const struct entry_desc b[] = { - [0] = { 0, 213549, 8 }, - [1] = { 0, 70, 1 }, - }; - - return do_test_bin(a, ARRAY_SIZE(a), b, ARRAY_SIZE(b)); -} - -static void test_bin_simple(void) -{ - static const struct entry_desc a[] = { - [0] = { 10, 101, 8 }, - [1] = { 11, 0, 0 }, - [2] = { 12, 2, 1 } - }; - static const struct entry_desc b[] = { - [0] = { 0, 101, 8 }, - [1] = { 0, 0, 0 }, - [2] = { 0, 0, 0 }, - [3] = { 0, 0, 0 }, - [4] = { 0, 2, 1 } - }; - - return do_test_bin(a, ARRAY_SIZE(a), b, ARRAY_SIZE(b)); -} - -static void test_single_full(void) -{ - struct qdist dist; - - qdist_init(&dist); - - qdist_add(&dist, 3, 102); - g_assert_cmpfloat(qdist_avg(&dist), ==, 3); - g_assert_cmpfloat(qdist_xmin(&dist), ==, 3); - g_assert_cmpfloat(qdist_xmax(&dist), ==, 3); - - histogram_check_single_full(&dist, 0); - histogram_check_single_full(&dist, 1); - histogram_check_single_full(&dist, 10); - - qdist_destroy(&dist); -} - -static void test_single_empty(void) -{ - struct qdist dist; - char *pr; - - qdist_init(&dist); - - qdist_add(&dist, 3, 0); - g_assert_cmpuint(qdist_sample_count(&dist), ==, 0); - g_assert(isnan(qdist_avg(&dist))); - g_assert_cmpfloat(qdist_xmin(&dist), ==, 3); - g_assert_cmpfloat(qdist_xmax(&dist), ==, 3); - - pr = qdist_pr_plain(&dist, 0); - g_assert_cmpstr(pr, ==, " "); - g_free(pr); - - pr = qdist_pr_plain(&dist, 1); - g_assert_cmpstr(pr, ==, " "); - g_free(pr); - - pr = qdist_pr_plain(&dist, 2); - g_assert_cmpstr(pr, ==, " "); - g_free(pr); - - qdist_destroy(&dist); -} - -static void test_none(void) -{ - struct qdist dist; - char *pr; - - qdist_init(&dist); - - g_assert(isnan(qdist_avg(&dist))); - g_assert(isnan(qdist_xmin(&dist))); - g_assert(isnan(qdist_xmax(&dist))); - - pr = qdist_pr_plain(&dist, 0); - g_assert_cmpstr(pr, ==, "(empty)"); - g_free(pr); - - pr = qdist_pr_plain(&dist, 2); - g_assert_cmpstr(pr, ==, "(empty)"); - g_free(pr); - - pr = qdist_pr(&dist, 0, QDIST_PR_BORDER); - g_assert_cmpstr(pr, ==, "(empty)"); - g_free(pr); - - qdist_destroy(&dist); -} - -int main(int argc, char *argv[]) -{ - g_test_init(&argc, &argv, NULL); - g_test_add_func("/qdist/none", test_none); - g_test_add_func("/qdist/single/empty", test_single_empty); - g_test_add_func("/qdist/single/full", test_single_full); - g_test_add_func("/qdist/binning/simple", test_bin_simple); - g_test_add_func("/qdist/binning/precision", test_bin_precision); - g_test_add_func("/qdist/binning/expand", test_bin_expand); - g_test_add_func("/qdist/binning/shrink", test_bin_shrink); - g_test_add_func("/qdist/pr", test_pr); - return g_test_run(); -} diff --git a/tests/test-qemu-opts.c b/tests/test-qemu-opts.c deleted file mode 100644 index 8bbb17b1c7..0000000000 --- a/tests/test-qemu-opts.c +++ /dev/null @@ -1,1046 +0,0 @@ -/* - * QemuOpts unit-tests. - * - * Copyright (C) 2014 Leandro Dorileo - * - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "qemu/units.h" -#include "qemu/option.h" -#include "qemu/option_int.h" -#include "qapi/error.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qstring.h" -#include "qemu/config-file.h" - - -static QemuOptsList opts_list_01 = { - .name = "opts_list_01", - .head = QTAILQ_HEAD_INITIALIZER(opts_list_01.head), - .desc = { - { - .name = "str1", - .type = QEMU_OPT_STRING, - .help = "Help texts are preserved in qemu_opts_append", - .def_value_str = "default", - },{ - .name = "str2", - .type = QEMU_OPT_STRING, - },{ - .name = "str3", - .type = QEMU_OPT_STRING, - },{ - .name = "number1", - .type = QEMU_OPT_NUMBER, - .help = "Having help texts only for some options is okay", - },{ - .name = "number2", - .type = QEMU_OPT_NUMBER, - }, - { /* end of list */ } - }, -}; - -static QemuOptsList opts_list_02 = { - .name = "opts_list_02", - .head = QTAILQ_HEAD_INITIALIZER(opts_list_02.head), - .desc = { - { - .name = "str1", - .type = QEMU_OPT_STRING, - },{ - .name = "str2", - .type = QEMU_OPT_STRING, - },{ - .name = "bool1", - .type = QEMU_OPT_BOOL, - },{ - .name = "bool2", - .type = QEMU_OPT_BOOL, - },{ - .name = "size1", - .type = QEMU_OPT_SIZE, - },{ - .name = "size2", - .type = QEMU_OPT_SIZE, - },{ - .name = "size3", - .type = QEMU_OPT_SIZE, - }, - { /* end of list */ } - }, -}; - -static QemuOptsList opts_list_03 = { - .name = "opts_list_03", - .implied_opt_name = "implied", - .head = QTAILQ_HEAD_INITIALIZER(opts_list_03.head), - .desc = { - /* no elements => accept any params */ - { /* end of list */ } - }, -}; - -static QemuOptsList opts_list_04 = { - .name = "opts_list_04", - .head = QTAILQ_HEAD_INITIALIZER(opts_list_04.head), - .merge_lists = true, - .desc = { - { - .name = "str3", - .type = QEMU_OPT_STRING, - }, - { /* end of list */ } - }, -}; - -static void register_opts(void) -{ - qemu_add_opts(&opts_list_01); - qemu_add_opts(&opts_list_02); - qemu_add_opts(&opts_list_03); - qemu_add_opts(&opts_list_04); -} - -static void test_find_unknown_opts(void) -{ - QemuOptsList *list; - Error *err = NULL; - - /* should not return anything, we don't have an "unknown" option */ - list = qemu_find_opts_err("unknown", &err); - g_assert(list == NULL); - error_free_or_abort(&err); -} - -static void test_qemu_find_opts(void) -{ - QemuOptsList *list; - - /* we have an "opts_list_01" option, should return it */ - list = qemu_find_opts("opts_list_01"); - g_assert(list != NULL); - g_assert_cmpstr(list->name, ==, "opts_list_01"); -} - -static void test_qemu_opts_create(void) -{ - QemuOptsList *list; - QemuOpts *opts; - - list = qemu_find_opts("opts_list_01"); - g_assert(list != NULL); - g_assert(QTAILQ_EMPTY(&list->head)); - g_assert_cmpstr(list->name, ==, "opts_list_01"); - - /* should not find anything at this point */ - opts = qemu_opts_find(list, NULL); - g_assert(opts == NULL); - - /* create the opts */ - opts = qemu_opts_create(list, NULL, 0, &error_abort); - g_assert(opts != NULL); - g_assert(!QTAILQ_EMPTY(&list->head)); - - /* now we've create the opts, must find it */ - opts = qemu_opts_find(list, NULL); - g_assert(opts != NULL); - - qemu_opts_del(opts); - - /* should not find anything at this point */ - opts = qemu_opts_find(list, NULL); - g_assert(opts == NULL); -} - -static void test_qemu_opt_get(void) -{ - QemuOptsList *list; - QemuOpts *opts; - const char *opt = NULL; - - list = qemu_find_opts("opts_list_01"); - g_assert(list != NULL); - g_assert(QTAILQ_EMPTY(&list->head)); - g_assert_cmpstr(list->name, ==, "opts_list_01"); - - /* should not find anything at this point */ - opts = qemu_opts_find(list, NULL); - g_assert(opts == NULL); - - /* create the opts */ - opts = qemu_opts_create(list, NULL, 0, &error_abort); - g_assert(opts != NULL); - g_assert(!QTAILQ_EMPTY(&list->head)); - - /* haven't set anything to str2 yet */ - opt = qemu_opt_get(opts, "str2"); - g_assert(opt == NULL); - - qemu_opt_set(opts, "str2", "value", &error_abort); - - /* now we have set str2, should know about it */ - opt = qemu_opt_get(opts, "str2"); - g_assert_cmpstr(opt, ==, "value"); - - qemu_opt_set(opts, "str2", "value2", &error_abort); - - /* having reset the value, the returned should be the reset one */ - opt = qemu_opt_get(opts, "str2"); - g_assert_cmpstr(opt, ==, "value2"); - - qemu_opts_del(opts); - - /* should not find anything at this point */ - opts = qemu_opts_find(list, NULL); - g_assert(opts == NULL); -} - -static void test_qemu_opt_get_bool(void) -{ - QemuOptsList *list; - QemuOpts *opts; - bool opt; - - list = qemu_find_opts("opts_list_02"); - g_assert(list != NULL); - g_assert(QTAILQ_EMPTY(&list->head)); - g_assert_cmpstr(list->name, ==, "opts_list_02"); - - /* should not find anything at this point */ - opts = qemu_opts_find(list, NULL); - g_assert(opts == NULL); - - /* create the opts */ - opts = qemu_opts_create(list, NULL, 0, &error_abort); - g_assert(opts != NULL); - g_assert(!QTAILQ_EMPTY(&list->head)); - - /* haven't set anything to bool1 yet, so defval should be returned */ - opt = qemu_opt_get_bool(opts, "bool1", false); - g_assert(opt == false); - - qemu_opt_set_bool(opts, "bool1", true, &error_abort); - - /* now we have set bool1, should know about it */ - opt = qemu_opt_get_bool(opts, "bool1", false); - g_assert(opt == true); - - /* having reset the value, opt should be the reset one not defval */ - qemu_opt_set_bool(opts, "bool1", false, &error_abort); - - opt = qemu_opt_get_bool(opts, "bool1", true); - g_assert(opt == false); - - qemu_opts_del(opts); - - /* should not find anything at this point */ - opts = qemu_opts_find(list, NULL); - g_assert(opts == NULL); -} - -static void test_qemu_opt_get_number(void) -{ - QemuOptsList *list; - QemuOpts *opts; - uint64_t opt; - - list = qemu_find_opts("opts_list_01"); - g_assert(list != NULL); - g_assert(QTAILQ_EMPTY(&list->head)); - g_assert_cmpstr(list->name, ==, "opts_list_01"); - - /* should not find anything at this point */ - opts = qemu_opts_find(list, NULL); - g_assert(opts == NULL); - - /* create the opts */ - opts = qemu_opts_create(list, NULL, 0, &error_abort); - g_assert(opts != NULL); - g_assert(!QTAILQ_EMPTY(&list->head)); - - /* haven't set anything to number1 yet, so defval should be returned */ - opt = qemu_opt_get_number(opts, "number1", 5); - g_assert(opt == 5); - - qemu_opt_set_number(opts, "number1", 10, &error_abort); - - /* now we have set number1, should know about it */ - opt = qemu_opt_get_number(opts, "number1", 5); - g_assert(opt == 10); - - /* having reset it, the returned should be the reset one not defval */ - qemu_opt_set_number(opts, "number1", 15, &error_abort); - - opt = qemu_opt_get_number(opts, "number1", 5); - g_assert(opt == 15); - - qemu_opts_del(opts); - - /* should not find anything at this point */ - opts = qemu_opts_find(list, NULL); - g_assert(opts == NULL); -} - -static void test_qemu_opt_get_size(void) -{ - QemuOptsList *list; - QemuOpts *opts; - uint64_t opt; - QDict *dict; - - list = qemu_find_opts("opts_list_02"); - g_assert(list != NULL); - g_assert(QTAILQ_EMPTY(&list->head)); - g_assert_cmpstr(list->name, ==, "opts_list_02"); - - /* should not find anything at this point */ - opts = qemu_opts_find(list, NULL); - g_assert(opts == NULL); - - /* create the opts */ - opts = qemu_opts_create(list, NULL, 0, &error_abort); - g_assert(opts != NULL); - g_assert(!QTAILQ_EMPTY(&list->head)); - - /* haven't set anything to size1 yet, so defval should be returned */ - opt = qemu_opt_get_size(opts, "size1", 5); - g_assert(opt == 5); - - dict = qdict_new(); - g_assert(dict != NULL); - - qdict_put_str(dict, "size1", "10"); - - qemu_opts_absorb_qdict(opts, dict, &error_abort); - g_assert(error_abort == NULL); - - /* now we have set size1, should know about it */ - opt = qemu_opt_get_size(opts, "size1", 5); - g_assert(opt == 10); - - /* reset value */ - qdict_put_str(dict, "size1", "15"); - - qemu_opts_absorb_qdict(opts, dict, &error_abort); - g_assert(error_abort == NULL); - - /* test the reset value */ - opt = qemu_opt_get_size(opts, "size1", 5); - g_assert(opt == 15); - - qdict_del(dict, "size1"); - g_free(dict); - - qemu_opts_del(opts); - - /* should not find anything at this point */ - opts = qemu_opts_find(list, NULL); - g_assert(opts == NULL); -} - -static void test_qemu_opt_unset(void) -{ - QemuOpts *opts; - const char *value; - int ret; - - /* dynamically initialized (parsed) opts */ - opts = qemu_opts_parse(&opts_list_03, "key=value", false, NULL); - g_assert(opts != NULL); - - /* check default/parsed value */ - value = qemu_opt_get(opts, "key"); - g_assert_cmpstr(value, ==, "value"); - - /* reset it to value2 */ - qemu_opt_set(opts, "key", "value2", &error_abort); - - value = qemu_opt_get(opts, "key"); - g_assert_cmpstr(value, ==, "value2"); - - /* unset, valid only for "accept any" */ - ret = qemu_opt_unset(opts, "key"); - g_assert(ret == 0); - - /* after reset the value should be the parsed/default one */ - value = qemu_opt_get(opts, "key"); - g_assert_cmpstr(value, ==, "value"); - - qemu_opts_del(opts); -} - -static void test_qemu_opts_reset(void) -{ - QemuOptsList *list; - QemuOpts *opts; - uint64_t opt; - - list = qemu_find_opts("opts_list_01"); - g_assert(list != NULL); - g_assert(QTAILQ_EMPTY(&list->head)); - g_assert_cmpstr(list->name, ==, "opts_list_01"); - - /* should not find anything at this point */ - opts = qemu_opts_find(list, NULL); - g_assert(opts == NULL); - - /* create the opts */ - opts = qemu_opts_create(list, NULL, 0, &error_abort); - g_assert(opts != NULL); - g_assert(!QTAILQ_EMPTY(&list->head)); - - /* haven't set anything to number1 yet, so defval should be returned */ - opt = qemu_opt_get_number(opts, "number1", 5); - g_assert(opt == 5); - - qemu_opt_set_number(opts, "number1", 10, &error_abort); - - /* now we have set number1, should know about it */ - opt = qemu_opt_get_number(opts, "number1", 5); - g_assert(opt == 10); - - qemu_opts_reset(list); - - /* should not find anything at this point */ - opts = qemu_opts_find(list, NULL); - g_assert(opts == NULL); -} - -static void test_qemu_opts_set(void) -{ - QemuOptsList *list; - QemuOpts *opts; - const char *opt; - - list = qemu_find_opts("opts_list_04"); - g_assert(list != NULL); - g_assert(QTAILQ_EMPTY(&list->head)); - g_assert_cmpstr(list->name, ==, "opts_list_04"); - - /* should not find anything at this point */ - opts = qemu_opts_find(list, NULL); - g_assert(opts == NULL); - - /* implicitly create opts and set str3 value */ - qemu_opts_set(list, "str3", "value", &error_abort); - g_assert(!QTAILQ_EMPTY(&list->head)); - - /* get the just created opts */ - opts = qemu_opts_find(list, NULL); - g_assert(opts != NULL); - - /* check the str3 value */ - opt = qemu_opt_get(opts, "str3"); - g_assert_cmpstr(opt, ==, "value"); - - qemu_opts_del(opts); - - /* should not find anything at this point */ - opts = qemu_opts_find(list, NULL); - g_assert(opts == NULL); -} - -static int opts_count_iter(void *opaque, const char *name, const char *value, - Error **errp) -{ - (*(size_t *)opaque)++; - return 0; -} - -static size_t opts_count(QemuOpts *opts) -{ - size_t n = 0; - - qemu_opt_foreach(opts, opts_count_iter, &n, NULL); - return n; -} - -static void test_opts_parse(void) -{ - Error *err = NULL; - QemuOpts *opts; - - /* Nothing */ - opts = qemu_opts_parse(&opts_list_03, "", false, &error_abort); - g_assert_cmpuint(opts_count(opts), ==, 0); - - /* Empty key */ - opts = qemu_opts_parse(&opts_list_03, "=val", false, &error_abort); - g_assert_cmpuint(opts_count(opts), ==, 1); - g_assert_cmpstr(qemu_opt_get(opts, ""), ==, "val"); - - /* Multiple keys, last one wins */ - opts = qemu_opts_parse(&opts_list_03, "a=1,b=2,,x,a=3", - false, &error_abort); - g_assert_cmpuint(opts_count(opts), ==, 3); - g_assert_cmpstr(qemu_opt_get(opts, "a"), ==, "3"); - g_assert_cmpstr(qemu_opt_get(opts, "b"), ==, "2,x"); - - /* Except when it doesn't */ - opts = qemu_opts_parse(&opts_list_03, "id=foo,id=bar", - false, &error_abort); - g_assert_cmpuint(opts_count(opts), ==, 0); - g_assert_cmpstr(qemu_opts_id(opts), ==, "foo"); - - /* TODO Cover low-level access to repeated keys */ - - /* Trailing comma is ignored */ - opts = qemu_opts_parse(&opts_list_03, "x=y,", false, &error_abort); - g_assert_cmpuint(opts_count(opts), ==, 1); - g_assert_cmpstr(qemu_opt_get(opts, "x"), ==, "y"); - - /* Except when it isn't */ - opts = qemu_opts_parse(&opts_list_03, ",", false, &error_abort); - g_assert_cmpuint(opts_count(opts), ==, 1); - g_assert_cmpstr(qemu_opt_get(opts, ""), ==, "on"); - - /* Duplicate ID */ - opts = qemu_opts_parse(&opts_list_03, "x=y,id=foo", false, &err); - error_free_or_abort(&err); - g_assert(!opts); - /* TODO Cover .merge_lists = true */ - - /* Buggy ID recognition (fixed) */ - opts = qemu_opts_parse(&opts_list_03, "x=,,id=bar", false, &error_abort); - g_assert_cmpuint(opts_count(opts), ==, 1); - g_assert(!qemu_opts_id(opts)); - g_assert_cmpstr(qemu_opt_get(opts, "x"), ==, ",id=bar"); - - /* Anti-social ID */ - opts = qemu_opts_parse(&opts_list_01, "id=666", false, &err); - error_free_or_abort(&err); - g_assert(!opts); - - /* Implied value (qemu_opts_parse warns but accepts it) */ - opts = qemu_opts_parse(&opts_list_03, "an,noaus,noaus=", - false, &error_abort); - g_assert_cmpuint(opts_count(opts), ==, 3); - g_assert_cmpstr(qemu_opt_get(opts, "an"), ==, "on"); - g_assert_cmpstr(qemu_opt_get(opts, "aus"), ==, "off"); - g_assert_cmpstr(qemu_opt_get(opts, "noaus"), ==, ""); - - /* Implied value, negated empty key */ - opts = qemu_opts_parse(&opts_list_03, "no", false, &error_abort); - g_assert_cmpuint(opts_count(opts), ==, 1); - g_assert_cmpstr(qemu_opt_get(opts, ""), ==, "off"); - - /* Implied key */ - opts = qemu_opts_parse(&opts_list_03, "an,noaus,noaus=", true, - &error_abort); - g_assert_cmpuint(opts_count(opts), ==, 3); - g_assert_cmpstr(qemu_opt_get(opts, "implied"), ==, "an"); - g_assert_cmpstr(qemu_opt_get(opts, "aus"), ==, "off"); - g_assert_cmpstr(qemu_opt_get(opts, "noaus"), ==, ""); - - /* Implied key with empty value */ - opts = qemu_opts_parse(&opts_list_03, ",", true, &error_abort); - g_assert_cmpuint(opts_count(opts), ==, 1); - g_assert_cmpstr(qemu_opt_get(opts, "implied"), ==, ""); - - /* Implied key with comma value */ - opts = qemu_opts_parse(&opts_list_03, ",,,a=1", true, &error_abort); - g_assert_cmpuint(opts_count(opts), ==, 2); - g_assert_cmpstr(qemu_opt_get(opts, "implied"), ==, ","); - g_assert_cmpstr(qemu_opt_get(opts, "a"), ==, "1"); - - /* Empty key is not an implied key */ - opts = qemu_opts_parse(&opts_list_03, "=val", true, &error_abort); - g_assert_cmpuint(opts_count(opts), ==, 1); - g_assert_cmpstr(qemu_opt_get(opts, ""), ==, "val"); - - /* Unknown key */ - opts = qemu_opts_parse(&opts_list_01, "nonexistent=", false, &err); - error_free_or_abort(&err); - g_assert(!opts); - - qemu_opts_reset(&opts_list_01); - qemu_opts_reset(&opts_list_03); -} - -static void test_opts_parse_bool(void) -{ - Error *err = NULL; - QemuOpts *opts; - - opts = qemu_opts_parse(&opts_list_02, "bool1=on,bool2=off", - false, &error_abort); - g_assert_cmpuint(opts_count(opts), ==, 2); - g_assert(qemu_opt_get_bool(opts, "bool1", false)); - g_assert(!qemu_opt_get_bool(opts, "bool2", true)); - - opts = qemu_opts_parse(&opts_list_02, "bool1=offer", false, &err); - error_free_or_abort(&err); - g_assert(!opts); - - qemu_opts_reset(&opts_list_02); -} - -static void test_opts_parse_number(void) -{ - Error *err = NULL; - QemuOpts *opts; - - /* Lower limit zero */ - opts = qemu_opts_parse(&opts_list_01, "number1=0", false, &error_abort); - g_assert_cmpuint(opts_count(opts), ==, 1); - g_assert_cmpuint(qemu_opt_get_number(opts, "number1", 1), ==, 0); - - /* Upper limit 2^64-1 */ - opts = qemu_opts_parse(&opts_list_01, - "number1=18446744073709551615,number2=-1", - false, &error_abort); - g_assert_cmpuint(opts_count(opts), ==, 2); - g_assert_cmphex(qemu_opt_get_number(opts, "number1", 1), ==, UINT64_MAX); - g_assert_cmphex(qemu_opt_get_number(opts, "number2", 0), ==, UINT64_MAX); - - /* Above upper limit */ - opts = qemu_opts_parse(&opts_list_01, "number1=18446744073709551616", - false, &err); - error_free_or_abort(&err); - g_assert(!opts); - - /* Below lower limit */ - opts = qemu_opts_parse(&opts_list_01, "number1=-18446744073709551616", - false, &err); - error_free_or_abort(&err); - g_assert(!opts); - - /* Hex and octal */ - opts = qemu_opts_parse(&opts_list_01, "number1=0x2a,number2=052", - false, &error_abort); - g_assert_cmpuint(opts_count(opts), ==, 2); - g_assert_cmpuint(qemu_opt_get_number(opts, "number1", 1), ==, 42); - g_assert_cmpuint(qemu_opt_get_number(opts, "number2", 0), ==, 42); - - /* Invalid */ - opts = qemu_opts_parse(&opts_list_01, "number1=", false, &err); - error_free_or_abort(&err); - g_assert(!opts); - opts = qemu_opts_parse(&opts_list_01, "number1=eins", false, &err); - error_free_or_abort(&err); - g_assert(!opts); - - /* Leading whitespace */ - opts = qemu_opts_parse(&opts_list_01, "number1= \t42", - false, &error_abort); - g_assert_cmpuint(opts_count(opts), ==, 1); - g_assert_cmpuint(qemu_opt_get_number(opts, "number1", 1), ==, 42); - - /* Trailing crap */ - opts = qemu_opts_parse(&opts_list_01, "number1=3.14", false, &err); - error_free_or_abort(&err); - g_assert(!opts); - opts = qemu_opts_parse(&opts_list_01, "number1=08", false, &err); - error_free_or_abort(&err); - g_assert(!opts); - opts = qemu_opts_parse(&opts_list_01, "number1=0 ", false, &err); - error_free_or_abort(&err); - g_assert(!opts); - - qemu_opts_reset(&opts_list_01); -} - -static void test_opts_parse_size(void) -{ - Error *err = NULL; - QemuOpts *opts; - - /* Lower limit zero */ - opts = qemu_opts_parse(&opts_list_02, "size1=0", false, &error_abort); - g_assert_cmpuint(opts_count(opts), ==, 1); - g_assert_cmpuint(qemu_opt_get_size(opts, "size1", 1), ==, 0); - - /* Note: precision is 53 bits since we're parsing with strtod() */ - - /* Around limit of precision: 2^53-1, 2^53, 2^54 */ - opts = qemu_opts_parse(&opts_list_02, - "size1=9007199254740991," - "size2=9007199254740992," - "size3=9007199254740993", - false, &error_abort); - g_assert_cmpuint(opts_count(opts), ==, 3); - g_assert_cmphex(qemu_opt_get_size(opts, "size1", 1), - ==, 0x1fffffffffffff); - g_assert_cmphex(qemu_opt_get_size(opts, "size2", 1), - ==, 0x20000000000000); - g_assert_cmphex(qemu_opt_get_size(opts, "size3", 1), - ==, 0x20000000000000); - - /* Close to signed upper limit 0x7ffffffffffffc00 (53 msbs set) */ - opts = qemu_opts_parse(&opts_list_02, - "size1=9223372036854774784," /* 7ffffffffffffc00 */ - "size2=9223372036854775295", /* 7ffffffffffffdff */ - false, &error_abort); - g_assert_cmpuint(opts_count(opts), ==, 2); - g_assert_cmphex(qemu_opt_get_size(opts, "size1", 1), - ==, 0x7ffffffffffffc00); - g_assert_cmphex(qemu_opt_get_size(opts, "size2", 1), - ==, 0x7ffffffffffffc00); - - /* Close to actual upper limit 0xfffffffffffff800 (53 msbs set) */ - opts = qemu_opts_parse(&opts_list_02, - "size1=18446744073709549568," /* fffffffffffff800 */ - "size2=18446744073709550591", /* fffffffffffffbff */ - false, &error_abort); - g_assert_cmpuint(opts_count(opts), ==, 2); - g_assert_cmphex(qemu_opt_get_size(opts, "size1", 1), - ==, 0xfffffffffffff800); - g_assert_cmphex(qemu_opt_get_size(opts, "size2", 1), - ==, 0xfffffffffffff800); - - /* Beyond limits */ - opts = qemu_opts_parse(&opts_list_02, "size1=-1", false, &err); - error_free_or_abort(&err); - g_assert(!opts); - opts = qemu_opts_parse(&opts_list_02, - "size1=18446744073709550592", /* fffffffffffffc00 */ - false, &err); - error_free_or_abort(&err); - g_assert(!opts); - - /* Suffixes */ - opts = qemu_opts_parse(&opts_list_02, "size1=8b,size2=1.5k,size3=2M", - false, &error_abort); - g_assert_cmpuint(opts_count(opts), ==, 3); - g_assert_cmphex(qemu_opt_get_size(opts, "size1", 0), ==, 8); - g_assert_cmphex(qemu_opt_get_size(opts, "size2", 0), ==, 1536); - g_assert_cmphex(qemu_opt_get_size(opts, "size3", 0), ==, 2 * MiB); - opts = qemu_opts_parse(&opts_list_02, "size1=0.1G,size2=16777215T", - false, &error_abort); - g_assert_cmpuint(opts_count(opts), ==, 2); - g_assert_cmphex(qemu_opt_get_size(opts, "size1", 0), ==, GiB / 10); - g_assert_cmphex(qemu_opt_get_size(opts, "size2", 0), ==, 16777215ULL * TiB); - - /* Beyond limit with suffix */ - opts = qemu_opts_parse(&opts_list_02, "size1=16777216T", - false, &err); - error_free_or_abort(&err); - g_assert(!opts); - - /* Trailing crap */ - opts = qemu_opts_parse(&opts_list_02, "size1=16E", false, &err); - error_free_or_abort(&err); - g_assert(!opts); - opts = qemu_opts_parse(&opts_list_02, "size1=16Gi", false, &err); - error_free_or_abort(&err); - g_assert(!opts); - - qemu_opts_reset(&opts_list_02); -} - -static void test_has_help_option(void) -{ - static const struct { - const char *params; - /* expected value of qemu_opt_has_help_opt() with implied=false */ - bool expect; - /* expected value of qemu_opt_has_help_opt() with implied=true */ - bool expect_implied; - } test[] = { - { "help", true, false }, - { "?", true, false }, - { "helpme", false, false }, - { "?me", false, false }, - { "a,help", true, true }, - { "a,?", true, true }, - { "a=0,help,b", true, true }, - { "a=0,?,b", true, true }, - { "help,b=1", true, false }, - { "?,b=1", true, false }, - { "a,b,,help", true, true }, - { "a,b,,?", true, true }, - }; - int i; - QemuOpts *opts; - - for (i = 0; i < ARRAY_SIZE(test); i++) { - g_assert_cmpint(has_help_option(test[i].params), - ==, test[i].expect); - opts = qemu_opts_parse(&opts_list_03, test[i].params, false, - &error_abort); - g_assert_cmpint(qemu_opt_has_help_opt(opts), - ==, test[i].expect); - qemu_opts_del(opts); - opts = qemu_opts_parse(&opts_list_03, test[i].params, true, - &error_abort); - g_assert_cmpint(qemu_opt_has_help_opt(opts), - ==, test[i].expect_implied); - qemu_opts_del(opts); - } -} - -static void append_verify_list_01(QemuOptDesc *desc, bool with_overlapping) -{ - int i = 0; - - if (with_overlapping) { - g_assert_cmpstr(desc[i].name, ==, "str1"); - g_assert_cmpint(desc[i].type, ==, QEMU_OPT_STRING); - g_assert_cmpstr(desc[i].help, ==, - "Help texts are preserved in qemu_opts_append"); - g_assert_cmpstr(desc[i].def_value_str, ==, "default"); - i++; - - g_assert_cmpstr(desc[i].name, ==, "str2"); - g_assert_cmpint(desc[i].type, ==, QEMU_OPT_STRING); - g_assert_cmpstr(desc[i].help, ==, NULL); - g_assert_cmpstr(desc[i].def_value_str, ==, NULL); - i++; - } - - g_assert_cmpstr(desc[i].name, ==, "str3"); - g_assert_cmpint(desc[i].type, ==, QEMU_OPT_STRING); - g_assert_cmpstr(desc[i].help, ==, NULL); - g_assert_cmpstr(desc[i].def_value_str, ==, NULL); - i++; - - g_assert_cmpstr(desc[i].name, ==, "number1"); - g_assert_cmpint(desc[i].type, ==, QEMU_OPT_NUMBER); - g_assert_cmpstr(desc[i].help, ==, - "Having help texts only for some options is okay"); - g_assert_cmpstr(desc[i].def_value_str, ==, NULL); - i++; - - g_assert_cmpstr(desc[i].name, ==, "number2"); - g_assert_cmpint(desc[i].type, ==, QEMU_OPT_NUMBER); - g_assert_cmpstr(desc[i].help, ==, NULL); - g_assert_cmpstr(desc[i].def_value_str, ==, NULL); - i++; - - g_assert_cmpstr(desc[i].name, ==, NULL); -} - -static void append_verify_list_02(QemuOptDesc *desc) -{ - int i = 0; - - g_assert_cmpstr(desc[i].name, ==, "str1"); - g_assert_cmpint(desc[i].type, ==, QEMU_OPT_STRING); - g_assert_cmpstr(desc[i].help, ==, NULL); - g_assert_cmpstr(desc[i].def_value_str, ==, NULL); - i++; - - g_assert_cmpstr(desc[i].name, ==, "str2"); - g_assert_cmpint(desc[i].type, ==, QEMU_OPT_STRING); - g_assert_cmpstr(desc[i].help, ==, NULL); - g_assert_cmpstr(desc[i].def_value_str, ==, NULL); - i++; - - g_assert_cmpstr(desc[i].name, ==, "bool1"); - g_assert_cmpint(desc[i].type, ==, QEMU_OPT_BOOL); - g_assert_cmpstr(desc[i].help, ==, NULL); - g_assert_cmpstr(desc[i].def_value_str, ==, NULL); - i++; - - g_assert_cmpstr(desc[i].name, ==, "bool2"); - g_assert_cmpint(desc[i].type, ==, QEMU_OPT_BOOL); - g_assert_cmpstr(desc[i].help, ==, NULL); - g_assert_cmpstr(desc[i].def_value_str, ==, NULL); - i++; - - g_assert_cmpstr(desc[i].name, ==, "size1"); - g_assert_cmpint(desc[i].type, ==, QEMU_OPT_SIZE); - g_assert_cmpstr(desc[i].help, ==, NULL); - g_assert_cmpstr(desc[i].def_value_str, ==, NULL); - i++; - - g_assert_cmpstr(desc[i].name, ==, "size2"); - g_assert_cmpint(desc[i].type, ==, QEMU_OPT_SIZE); - g_assert_cmpstr(desc[i].help, ==, NULL); - g_assert_cmpstr(desc[i].def_value_str, ==, NULL); - i++; - - g_assert_cmpstr(desc[i].name, ==, "size3"); - g_assert_cmpint(desc[i].type, ==, QEMU_OPT_SIZE); - g_assert_cmpstr(desc[i].help, ==, NULL); - g_assert_cmpstr(desc[i].def_value_str, ==, NULL); -} - -static void test_opts_append_to_null(void) -{ - QemuOptsList *merged; - - merged = qemu_opts_append(NULL, &opts_list_01); - g_assert(merged != &opts_list_01); - - g_assert_cmpstr(merged->name, ==, NULL); - g_assert_cmpstr(merged->implied_opt_name, ==, NULL); - g_assert_false(merged->merge_lists); - - append_verify_list_01(merged->desc, true); - - qemu_opts_free(merged); -} - -static void test_opts_append(void) -{ - QemuOptsList *first, *merged; - - first = qemu_opts_append(NULL, &opts_list_02); - merged = qemu_opts_append(first, &opts_list_01); - g_assert(first != &opts_list_02); - g_assert(merged != &opts_list_01); - - g_assert_cmpstr(merged->name, ==, NULL); - g_assert_cmpstr(merged->implied_opt_name, ==, NULL); - g_assert_false(merged->merge_lists); - - append_verify_list_02(&merged->desc[0]); - append_verify_list_01(&merged->desc[7], false); - - qemu_opts_free(merged); -} - -static void test_opts_to_qdict_basic(void) -{ - QemuOpts *opts; - QDict *dict; - - opts = qemu_opts_parse(&opts_list_01, "str1=foo,str2=,str3=bar,number1=42", - false, &error_abort); - g_assert(opts != NULL); - - dict = qemu_opts_to_qdict(opts, NULL); - g_assert(dict != NULL); - - g_assert_cmpstr(qdict_get_str(dict, "str1"), ==, "foo"); - g_assert_cmpstr(qdict_get_str(dict, "str2"), ==, ""); - g_assert_cmpstr(qdict_get_str(dict, "str3"), ==, "bar"); - g_assert_cmpstr(qdict_get_str(dict, "number1"), ==, "42"); - g_assert_false(qdict_haskey(dict, "number2")); - - qobject_unref(dict); - qemu_opts_del(opts); -} - -static void test_opts_to_qdict_filtered(void) -{ - QemuOptsList *first, *merged; - QemuOpts *opts; - QDict *dict; - - first = qemu_opts_append(NULL, &opts_list_02); - merged = qemu_opts_append(first, &opts_list_01); - - opts = qemu_opts_parse(merged, - "str1=foo,str2=,str3=bar,bool1=off,number1=42", - false, &error_abort); - g_assert(opts != NULL); - - /* Convert to QDict without deleting from opts */ - dict = qemu_opts_to_qdict_filtered(opts, NULL, &opts_list_01, false); - g_assert(dict != NULL); - g_assert_cmpstr(qdict_get_str(dict, "str1"), ==, "foo"); - g_assert_cmpstr(qdict_get_str(dict, "str2"), ==, ""); - g_assert_cmpstr(qdict_get_str(dict, "str3"), ==, "bar"); - g_assert_cmpstr(qdict_get_str(dict, "number1"), ==, "42"); - g_assert_false(qdict_haskey(dict, "number2")); - g_assert_false(qdict_haskey(dict, "bool1")); - qobject_unref(dict); - - dict = qemu_opts_to_qdict_filtered(opts, NULL, &opts_list_02, false); - g_assert(dict != NULL); - g_assert_cmpstr(qdict_get_str(dict, "str1"), ==, "foo"); - g_assert_cmpstr(qdict_get_str(dict, "str2"), ==, ""); - g_assert_cmpstr(qdict_get_str(dict, "bool1"), ==, "off"); - g_assert_false(qdict_haskey(dict, "str3")); - g_assert_false(qdict_haskey(dict, "number1")); - g_assert_false(qdict_haskey(dict, "number2")); - qobject_unref(dict); - - /* Now delete converted options from opts */ - dict = qemu_opts_to_qdict_filtered(opts, NULL, &opts_list_01, true); - g_assert(dict != NULL); - g_assert_cmpstr(qdict_get_str(dict, "str1"), ==, "foo"); - g_assert_cmpstr(qdict_get_str(dict, "str2"), ==, ""); - g_assert_cmpstr(qdict_get_str(dict, "str3"), ==, "bar"); - g_assert_cmpstr(qdict_get_str(dict, "number1"), ==, "42"); - g_assert_false(qdict_haskey(dict, "number2")); - g_assert_false(qdict_haskey(dict, "bool1")); - qobject_unref(dict); - - dict = qemu_opts_to_qdict_filtered(opts, NULL, &opts_list_02, true); - g_assert(dict != NULL); - g_assert_cmpstr(qdict_get_str(dict, "bool1"), ==, "off"); - g_assert_false(qdict_haskey(dict, "str1")); - g_assert_false(qdict_haskey(dict, "str2")); - g_assert_false(qdict_haskey(dict, "str3")); - g_assert_false(qdict_haskey(dict, "number1")); - g_assert_false(qdict_haskey(dict, "number2")); - qobject_unref(dict); - - g_assert_true(QTAILQ_EMPTY(&opts->head)); - - qemu_opts_del(opts); - qemu_opts_free(merged); -} - -static void test_opts_to_qdict_duplicates(void) -{ - QemuOpts *opts; - QemuOpt *opt; - QDict *dict; - - opts = qemu_opts_parse(&opts_list_03, "foo=a,foo=b", false, &error_abort); - g_assert(opts != NULL); - - /* Verify that opts has two options with the same name */ - opt = QTAILQ_FIRST(&opts->head); - g_assert_cmpstr(opt->name, ==, "foo"); - g_assert_cmpstr(opt->str , ==, "a"); - - opt = QTAILQ_NEXT(opt, next); - g_assert_cmpstr(opt->name, ==, "foo"); - g_assert_cmpstr(opt->str , ==, "b"); - - opt = QTAILQ_NEXT(opt, next); - g_assert(opt == NULL); - - /* In the conversion to QDict, the last one wins */ - dict = qemu_opts_to_qdict(opts, NULL); - g_assert(dict != NULL); - g_assert_cmpstr(qdict_get_str(dict, "foo"), ==, "b"); - qobject_unref(dict); - - /* The last one still wins if entries are deleted, and both are deleted */ - dict = qemu_opts_to_qdict_filtered(opts, NULL, NULL, true); - g_assert(dict != NULL); - g_assert_cmpstr(qdict_get_str(dict, "foo"), ==, "b"); - qobject_unref(dict); - - g_assert_true(QTAILQ_EMPTY(&opts->head)); - - qemu_opts_del(opts); -} - -int main(int argc, char *argv[]) -{ - register_opts(); - g_test_init(&argc, &argv, NULL); - g_test_add_func("/qemu-opts/find_unknown_opts", test_find_unknown_opts); - g_test_add_func("/qemu-opts/find_opts", test_qemu_find_opts); - g_test_add_func("/qemu-opts/opts_create", test_qemu_opts_create); - g_test_add_func("/qemu-opts/opt_get", test_qemu_opt_get); - g_test_add_func("/qemu-opts/opt_get_bool", test_qemu_opt_get_bool); - g_test_add_func("/qemu-opts/opt_get_number", test_qemu_opt_get_number); - g_test_add_func("/qemu-opts/opt_get_size", test_qemu_opt_get_size); - g_test_add_func("/qemu-opts/opt_unset", test_qemu_opt_unset); - g_test_add_func("/qemu-opts/opts_reset", test_qemu_opts_reset); - g_test_add_func("/qemu-opts/opts_set", test_qemu_opts_set); - g_test_add_func("/qemu-opts/opts_parse/general", test_opts_parse); - g_test_add_func("/qemu-opts/opts_parse/bool", test_opts_parse_bool); - g_test_add_func("/qemu-opts/opts_parse/number", test_opts_parse_number); - g_test_add_func("/qemu-opts/opts_parse/size", test_opts_parse_size); - g_test_add_func("/qemu-opts/has_help_option", test_has_help_option); - g_test_add_func("/qemu-opts/append_to_null", test_opts_append_to_null); - g_test_add_func("/qemu-opts/append", test_opts_append); - g_test_add_func("/qemu-opts/to_qdict/basic", test_opts_to_qdict_basic); - g_test_add_func("/qemu-opts/to_qdict/filtered", test_opts_to_qdict_filtered); - g_test_add_func("/qemu-opts/to_qdict/duplicates", test_opts_to_qdict_duplicates); - g_test_run(); - return 0; -} diff --git a/tests/test-qga.c b/tests/test-qga.c deleted file mode 100644 index eb33264e8e..0000000000 --- a/tests/test-qga.c +++ /dev/null @@ -1,1019 +0,0 @@ -#include "qemu/osdep.h" -#include -#include -#include -#include - -#include "qtest/libqos/libqtest.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" - -typedef struct { - char *test_dir; - GMainLoop *loop; - int fd; - GPid pid; -} TestFixture; - -static int connect_qga(char *path) -{ - int s, ret, len, i = 0; - struct sockaddr_un remote; - - s = socket(AF_UNIX, SOCK_STREAM, 0); - g_assert(s != -1); - - remote.sun_family = AF_UNIX; - do { - strcpy(remote.sun_path, path); - len = strlen(remote.sun_path) + sizeof(remote.sun_family); - ret = connect(s, (struct sockaddr *)&remote, len); - if (ret == -1) { - g_usleep(G_USEC_PER_SEC); - } - if (i++ == 10) { - return -1; - } - } while (ret == -1); - - return s; -} - -static void qga_watch(GPid pid, gint status, gpointer user_data) -{ - TestFixture *fixture = user_data; - - g_assert_cmpint(status, ==, 0); - g_main_loop_quit(fixture->loop); -} - -static void -fixture_setup(TestFixture *fixture, gconstpointer data, gchar **envp) -{ - const gchar *extra_arg = data; - GError *error = NULL; - gchar *cwd, *path, *cmd, **argv = NULL; - - fixture->loop = g_main_loop_new(NULL, FALSE); - - fixture->test_dir = g_strdup("/tmp/qgatest.XXXXXX"); - g_assert_nonnull(mkdtemp(fixture->test_dir)); - - path = g_build_filename(fixture->test_dir, "sock", NULL); - cwd = g_get_current_dir(); - cmd = g_strdup_printf("%s%cqga%cqemu-ga -m unix-listen -t %s -p %s %s %s", - cwd, G_DIR_SEPARATOR, G_DIR_SEPARATOR, - fixture->test_dir, path, - getenv("QTEST_LOG") ? "-v" : "", - extra_arg ?: ""); - g_shell_parse_argv(cmd, NULL, &argv, &error); - g_assert_no_error(error); - - g_spawn_async(fixture->test_dir, argv, envp, - G_SPAWN_SEARCH_PATH|G_SPAWN_DO_NOT_REAP_CHILD, - NULL, NULL, &fixture->pid, &error); - g_assert_no_error(error); - - g_child_watch_add(fixture->pid, qga_watch, fixture); - - fixture->fd = connect_qga(path); - g_assert_cmpint(fixture->fd, !=, -1); - - g_strfreev(argv); - g_free(cmd); - g_free(cwd); - g_free(path); -} - -static void -fixture_tear_down(TestFixture *fixture, gconstpointer data) -{ - gchar *tmp; - - kill(fixture->pid, SIGTERM); - - g_main_loop_run(fixture->loop); - g_main_loop_unref(fixture->loop); - - g_spawn_close_pid(fixture->pid); - - tmp = g_build_filename(fixture->test_dir, "foo", NULL); - g_unlink(tmp); - g_free(tmp); - - tmp = g_build_filename(fixture->test_dir, "qga.state", NULL); - g_unlink(tmp); - g_free(tmp); - - tmp = g_build_filename(fixture->test_dir, "sock", NULL); - g_unlink(tmp); - g_free(tmp); - - g_rmdir(fixture->test_dir); - g_free(fixture->test_dir); - close(fixture->fd); -} - -static void qmp_assertion_message_error(const char *domain, - const char *file, - int line, - const char *func, - const char *expr, - QDict *dict) -{ - const char *class, *desc; - char *s; - QDict *error; - - error = qdict_get_qdict(dict, "error"); - class = qdict_get_try_str(error, "class"); - desc = qdict_get_try_str(error, "desc"); - - s = g_strdup_printf("assertion failed %s: %s %s", expr, class, desc); - g_assertion_message(domain, file, line, func, s); - g_free(s); -} - -#define qmp_assert_no_error(err) do { \ - if (qdict_haskey(err, "error")) { \ - qmp_assertion_message_error(G_LOG_DOMAIN, __FILE__, __LINE__, \ - G_STRFUNC, #err, err); \ - } \ -} while (0) - -static void test_qga_sync_delimited(gconstpointer fix) -{ - const TestFixture *fixture = fix; - guint32 v, r = g_test_rand_int(); - unsigned char c; - QDict *ret; - - qmp_fd_send_raw(fixture->fd, "\xff"); - qmp_fd_send(fixture->fd, - "{'execute': 'guest-sync-delimited'," - " 'arguments': {'id': %u } }", - r); - - /* - * Read and ignore garbage until resynchronized. - * - * Note that the full reset sequence would involve checking the - * response of guest-sync-delimited and repeating the loop if - * 'id' field of the response does not match the 'id' field of - * the request. Testing this fully would require inserting - * garbage in the response stream and is left as a future test - * to implement. - * - * TODO: The server shouldn't emit so much garbage (among other - * things, it loudly complains about the client's \xff being - * invalid JSON, even though it is a documented part of the - * handshake. - */ - do { - v = read(fixture->fd, &c, 1); - g_assert_cmpint(v, ==, 1); - } while (c != 0xff); - - ret = qmp_fd_receive(fixture->fd); - g_assert_nonnull(ret); - qmp_assert_no_error(ret); - - v = qdict_get_int(ret, "return"); - g_assert_cmpint(r, ==, v); - - qobject_unref(ret); -} - -static void test_qga_sync(gconstpointer fix) -{ - const TestFixture *fixture = fix; - guint32 v, r = g_test_rand_int(); - QDict *ret; - - /* - * TODO guest-sync is inherently limited: we cannot distinguish - * failure caused by reacting to garbage on the wire prior to this - * command, from failure of this actual command. Clients are - * supposed to be able to send a raw '\xff' byte to at least - * re-synchronize the server's parser prior to this command, but - * we are not in a position to test that here because (at least - * for now) it causes the server to issue an error message about - * invalid JSON. Testing of '\xff' handling is done in - * guest-sync-delimited instead. - */ - ret = qmp_fd(fixture->fd, - "{'execute': 'guest-sync', 'arguments': {'id': %u } }", - r); - - g_assert_nonnull(ret); - qmp_assert_no_error(ret); - - v = qdict_get_int(ret, "return"); - g_assert_cmpint(r, ==, v); - - qobject_unref(ret); -} - -static void test_qga_ping(gconstpointer fix) -{ - const TestFixture *fixture = fix; - QDict *ret; - - ret = qmp_fd(fixture->fd, "{'execute': 'guest-ping'}"); - g_assert_nonnull(ret); - qmp_assert_no_error(ret); - - qobject_unref(ret); -} - -static void test_qga_id(gconstpointer fix) -{ - const TestFixture *fixture = fix; - QDict *ret; - - ret = qmp_fd(fixture->fd, "{'execute': 'guest-ping', 'id': 1}"); - g_assert_nonnull(ret); - qmp_assert_no_error(ret); - g_assert_cmpint(qdict_get_int(ret, "id"), ==, 1); - - qobject_unref(ret); -} - -static void test_qga_invalid_oob(gconstpointer fix) -{ - const TestFixture *fixture = fix; - QDict *ret; - - ret = qmp_fd(fixture->fd, "{'exec-oob': 'guest-ping'}"); - g_assert_nonnull(ret); - - qmp_expect_error_and_unref(ret, "GenericError"); -} - -static void test_qga_invalid_args(gconstpointer fix) -{ - const TestFixture *fixture = fix; - QDict *ret, *error; - const gchar *class, *desc; - - ret = qmp_fd(fixture->fd, "{'execute': 'guest-ping', " - "'arguments': {'foo': 42 }}"); - g_assert_nonnull(ret); - - error = qdict_get_qdict(ret, "error"); - class = qdict_get_try_str(error, "class"); - desc = qdict_get_try_str(error, "desc"); - - g_assert_cmpstr(class, ==, "GenericError"); - g_assert_cmpstr(desc, ==, "Parameter 'foo' is unexpected"); - - qobject_unref(ret); -} - -static void test_qga_invalid_cmd(gconstpointer fix) -{ - const TestFixture *fixture = fix; - QDict *ret, *error; - const gchar *class, *desc; - - ret = qmp_fd(fixture->fd, "{'execute': 'guest-invalid-cmd'}"); - g_assert_nonnull(ret); - - error = qdict_get_qdict(ret, "error"); - class = qdict_get_try_str(error, "class"); - desc = qdict_get_try_str(error, "desc"); - - g_assert_cmpstr(class, ==, "CommandNotFound"); - g_assert_cmpint(strlen(desc), >, 0); - - qobject_unref(ret); -} - -static void test_qga_info(gconstpointer fix) -{ - const TestFixture *fixture = fix; - QDict *ret, *val; - const gchar *version; - - ret = qmp_fd(fixture->fd, "{'execute': 'guest-info'}"); - g_assert_nonnull(ret); - qmp_assert_no_error(ret); - - val = qdict_get_qdict(ret, "return"); - version = qdict_get_try_str(val, "version"); - g_assert_cmpstr(version, ==, QEMU_VERSION); - - qobject_unref(ret); -} - -static void test_qga_get_vcpus(gconstpointer fix) -{ - const TestFixture *fixture = fix; - QDict *ret; - QList *list; - const QListEntry *entry; - - ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-vcpus'}"); - g_assert_nonnull(ret); - qmp_assert_no_error(ret); - - /* check there is at least a cpu */ - list = qdict_get_qlist(ret, "return"); - entry = qlist_first(list); - g_assert(qdict_haskey(qobject_to(QDict, entry->value), "online")); - g_assert(qdict_haskey(qobject_to(QDict, entry->value), "logical-id")); - - qobject_unref(ret); -} - -static void test_qga_get_fsinfo(gconstpointer fix) -{ - const TestFixture *fixture = fix; - QDict *ret; - QList *list; - const QListEntry *entry; - - ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-fsinfo'}"); - g_assert_nonnull(ret); - qmp_assert_no_error(ret); - - /* sanity-check the response if there are any filesystems */ - list = qdict_get_qlist(ret, "return"); - entry = qlist_first(list); - if (entry) { - g_assert(qdict_haskey(qobject_to(QDict, entry->value), "name")); - g_assert(qdict_haskey(qobject_to(QDict, entry->value), "mountpoint")); - g_assert(qdict_haskey(qobject_to(QDict, entry->value), "type")); - g_assert(qdict_haskey(qobject_to(QDict, entry->value), "disk")); - } - - qobject_unref(ret); -} - -static void test_qga_get_memory_block_info(gconstpointer fix) -{ - const TestFixture *fixture = fix; - QDict *ret, *val; - int64_t size; - - ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-memory-block-info'}"); - g_assert_nonnull(ret); - - /* some systems might not expose memory block info in sysfs */ - if (!qdict_haskey(ret, "error")) { - /* check there is at least some memory */ - val = qdict_get_qdict(ret, "return"); - size = qdict_get_int(val, "size"); - g_assert_cmpint(size, >, 0); - } - - qobject_unref(ret); -} - -static void test_qga_get_memory_blocks(gconstpointer fix) -{ - const TestFixture *fixture = fix; - QDict *ret; - QList *list; - const QListEntry *entry; - - ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-memory-blocks'}"); - g_assert_nonnull(ret); - - /* some systems might not expose memory block info in sysfs */ - if (!qdict_haskey(ret, "error")) { - list = qdict_get_qlist(ret, "return"); - entry = qlist_first(list); - /* newer versions of qga may return empty list without error */ - if (entry) { - g_assert(qdict_haskey(qobject_to(QDict, entry->value), - "phys-index")); - g_assert(qdict_haskey(qobject_to(QDict, entry->value), "online")); - } - } - - qobject_unref(ret); -} - -static void test_qga_network_get_interfaces(gconstpointer fix) -{ - const TestFixture *fixture = fix; - QDict *ret; - QList *list; - const QListEntry *entry; - - ret = qmp_fd(fixture->fd, "{'execute': 'guest-network-get-interfaces'}"); - g_assert_nonnull(ret); - qmp_assert_no_error(ret); - - /* check there is at least an interface */ - list = qdict_get_qlist(ret, "return"); - entry = qlist_first(list); - g_assert(qdict_haskey(qobject_to(QDict, entry->value), "name")); - - qobject_unref(ret); -} - -static void test_qga_file_ops(gconstpointer fix) -{ - const TestFixture *fixture = fix; - const unsigned char helloworld[] = "Hello World!\n"; - const char *b64; - gchar *path, *enc; - unsigned char *dec; - QDict *ret, *val; - int64_t id, eof; - gsize count; - FILE *f; - char tmp[100]; - - /* open */ - ret = qmp_fd(fixture->fd, "{'execute': 'guest-file-open'," - " 'arguments': { 'path': 'foo', 'mode': 'w+' } }"); - g_assert_nonnull(ret); - qmp_assert_no_error(ret); - id = qdict_get_int(ret, "return"); - qobject_unref(ret); - - enc = g_base64_encode(helloworld, sizeof(helloworld)); - /* write */ - ret = qmp_fd(fixture->fd, - "{'execute': 'guest-file-write'," - " 'arguments': { 'handle': %" PRId64 ", 'buf-b64': %s } }", - id, enc); - g_assert_nonnull(ret); - qmp_assert_no_error(ret); - - val = qdict_get_qdict(ret, "return"); - count = qdict_get_int(val, "count"); - eof = qdict_get_bool(val, "eof"); - g_assert_cmpint(count, ==, sizeof(helloworld)); - g_assert_cmpint(eof, ==, 0); - qobject_unref(ret); - - /* flush */ - ret = qmp_fd(fixture->fd, - "{'execute': 'guest-file-flush'," - " 'arguments': {'handle': %" PRId64 "} }", - id); - qobject_unref(ret); - - /* close */ - ret = qmp_fd(fixture->fd, - "{'execute': 'guest-file-close'," - " 'arguments': {'handle': %" PRId64 "} }", - id); - qobject_unref(ret); - - /* check content */ - path = g_build_filename(fixture->test_dir, "foo", NULL); - f = fopen(path, "r"); - g_free(path); - g_assert_nonnull(f); - count = fread(tmp, 1, sizeof(tmp), f); - g_assert_cmpint(count, ==, sizeof(helloworld)); - tmp[count] = 0; - g_assert_cmpstr(tmp, ==, (char *)helloworld); - fclose(f); - - /* open */ - ret = qmp_fd(fixture->fd, "{'execute': 'guest-file-open'," - " 'arguments': { 'path': 'foo', 'mode': 'r' } }"); - g_assert_nonnull(ret); - qmp_assert_no_error(ret); - id = qdict_get_int(ret, "return"); - qobject_unref(ret); - - /* read */ - ret = qmp_fd(fixture->fd, - "{'execute': 'guest-file-read'," - " 'arguments': { 'handle': %" PRId64 "} }", - id); - val = qdict_get_qdict(ret, "return"); - count = qdict_get_int(val, "count"); - eof = qdict_get_bool(val, "eof"); - b64 = qdict_get_str(val, "buf-b64"); - g_assert_cmpint(count, ==, sizeof(helloworld)); - g_assert(eof); - g_assert_cmpstr(b64, ==, enc); - - qobject_unref(ret); - g_free(enc); - - /* read eof */ - ret = qmp_fd(fixture->fd, - "{'execute': 'guest-file-read'," - " 'arguments': { 'handle': %" PRId64 "} }", - id); - val = qdict_get_qdict(ret, "return"); - count = qdict_get_int(val, "count"); - eof = qdict_get_bool(val, "eof"); - b64 = qdict_get_str(val, "buf-b64"); - g_assert_cmpint(count, ==, 0); - g_assert(eof); - g_assert_cmpstr(b64, ==, ""); - qobject_unref(ret); - - /* seek */ - ret = qmp_fd(fixture->fd, - "{'execute': 'guest-file-seek'," - " 'arguments': { 'handle': %" PRId64 ", " - " 'offset': %d, 'whence': %s } }", - id, 6, "set"); - qmp_assert_no_error(ret); - val = qdict_get_qdict(ret, "return"); - count = qdict_get_int(val, "position"); - eof = qdict_get_bool(val, "eof"); - g_assert_cmpint(count, ==, 6); - g_assert(!eof); - qobject_unref(ret); - - /* partial read */ - ret = qmp_fd(fixture->fd, - "{'execute': 'guest-file-read'," - " 'arguments': { 'handle': %" PRId64 "} }", - id); - val = qdict_get_qdict(ret, "return"); - count = qdict_get_int(val, "count"); - eof = qdict_get_bool(val, "eof"); - b64 = qdict_get_str(val, "buf-b64"); - g_assert_cmpint(count, ==, sizeof(helloworld) - 6); - g_assert(eof); - dec = g_base64_decode(b64, &count); - g_assert_cmpint(count, ==, sizeof(helloworld) - 6); - g_assert_cmpmem(dec, count, helloworld + 6, sizeof(helloworld) - 6); - g_free(dec); - - qobject_unref(ret); - - /* close */ - ret = qmp_fd(fixture->fd, - "{'execute': 'guest-file-close'," - " 'arguments': {'handle': %" PRId64 "} }", - id); - qobject_unref(ret); -} - -static void test_qga_file_write_read(gconstpointer fix) -{ - const TestFixture *fixture = fix; - const unsigned char helloworld[] = "Hello World!\n"; - const char *b64; - gchar *enc; - QDict *ret, *val; - int64_t id, eof; - gsize count; - - /* open */ - ret = qmp_fd(fixture->fd, "{'execute': 'guest-file-open'," - " 'arguments': { 'path': 'foo', 'mode': 'w+' } }"); - g_assert_nonnull(ret); - qmp_assert_no_error(ret); - id = qdict_get_int(ret, "return"); - qobject_unref(ret); - - enc = g_base64_encode(helloworld, sizeof(helloworld)); - /* write */ - ret = qmp_fd(fixture->fd, - "{'execute': 'guest-file-write'," - " 'arguments': { 'handle': %" PRId64 "," - " 'buf-b64': %s } }", id, enc); - g_assert_nonnull(ret); - qmp_assert_no_error(ret); - - val = qdict_get_qdict(ret, "return"); - count = qdict_get_int(val, "count"); - eof = qdict_get_bool(val, "eof"); - g_assert_cmpint(count, ==, sizeof(helloworld)); - g_assert_cmpint(eof, ==, 0); - qobject_unref(ret); - - /* read (check implicit flush) */ - ret = qmp_fd(fixture->fd, - "{'execute': 'guest-file-read'," - " 'arguments': { 'handle': %" PRId64 "} }", - id); - val = qdict_get_qdict(ret, "return"); - count = qdict_get_int(val, "count"); - eof = qdict_get_bool(val, "eof"); - b64 = qdict_get_str(val, "buf-b64"); - g_assert_cmpint(count, ==, 0); - g_assert(eof); - g_assert_cmpstr(b64, ==, ""); - qobject_unref(ret); - - /* seek to 0 */ - ret = qmp_fd(fixture->fd, - "{'execute': 'guest-file-seek'," - " 'arguments': { 'handle': %" PRId64 ", " - " 'offset': %d, 'whence': %s } }", - id, 0, "set"); - qmp_assert_no_error(ret); - val = qdict_get_qdict(ret, "return"); - count = qdict_get_int(val, "position"); - eof = qdict_get_bool(val, "eof"); - g_assert_cmpint(count, ==, 0); - g_assert(!eof); - qobject_unref(ret); - - /* read */ - ret = qmp_fd(fixture->fd, - "{'execute': 'guest-file-read'," - " 'arguments': { 'handle': %" PRId64 "} }", - id); - val = qdict_get_qdict(ret, "return"); - count = qdict_get_int(val, "count"); - eof = qdict_get_bool(val, "eof"); - b64 = qdict_get_str(val, "buf-b64"); - g_assert_cmpint(count, ==, sizeof(helloworld)); - g_assert(eof); - g_assert_cmpstr(b64, ==, enc); - qobject_unref(ret); - g_free(enc); - - /* close */ - ret = qmp_fd(fixture->fd, - "{'execute': 'guest-file-close'," - " 'arguments': {'handle': %" PRId64 "} }", - id); - qobject_unref(ret); -} - -static void test_qga_get_time(gconstpointer fix) -{ - const TestFixture *fixture = fix; - QDict *ret; - int64_t time; - - ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-time'}"); - g_assert_nonnull(ret); - qmp_assert_no_error(ret); - - time = qdict_get_int(ret, "return"); - g_assert_cmpint(time, >, 0); - - qobject_unref(ret); -} - -static void test_qga_blacklist(gconstpointer data) -{ - TestFixture fix; - QDict *ret, *error; - const gchar *class, *desc; - - fixture_setup(&fix, "-b guest-ping,guest-get-time", NULL); - - /* check blacklist */ - ret = qmp_fd(fix.fd, "{'execute': 'guest-ping'}"); - g_assert_nonnull(ret); - error = qdict_get_qdict(ret, "error"); - class = qdict_get_try_str(error, "class"); - desc = qdict_get_try_str(error, "desc"); - g_assert_cmpstr(class, ==, "CommandNotFound"); - g_assert_nonnull(g_strstr_len(desc, -1, "has been disabled")); - qobject_unref(ret); - - ret = qmp_fd(fix.fd, "{'execute': 'guest-get-time'}"); - g_assert_nonnull(ret); - error = qdict_get_qdict(ret, "error"); - class = qdict_get_try_str(error, "class"); - desc = qdict_get_try_str(error, "desc"); - g_assert_cmpstr(class, ==, "CommandNotFound"); - g_assert_nonnull(g_strstr_len(desc, -1, "has been disabled")); - qobject_unref(ret); - - /* check something work */ - ret = qmp_fd(fix.fd, "{'execute': 'guest-get-fsinfo'}"); - qmp_assert_no_error(ret); - qobject_unref(ret); - - fixture_tear_down(&fix, NULL); -} - -static void test_qga_config(gconstpointer data) -{ - GError *error = NULL; - char *cwd, *cmd, *out, *err, *str, **strv, **argv = NULL; - char *env[2]; - int status; - gsize n; - GKeyFile *kf; - - cwd = g_get_current_dir(); - cmd = g_strdup_printf("%s%cqga%cqemu-ga -D", - cwd, G_DIR_SEPARATOR, G_DIR_SEPARATOR); - g_free(cwd); - g_shell_parse_argv(cmd, NULL, &argv, &error); - g_free(cmd); - g_assert_no_error(error); - - env[0] = g_strdup_printf("QGA_CONF=tests%cdata%ctest-qga-config", - G_DIR_SEPARATOR, G_DIR_SEPARATOR); - env[1] = NULL; - g_spawn_sync(NULL, argv, env, 0, - NULL, NULL, &out, &err, &status, &error); - g_strfreev(argv); - - g_assert_no_error(error); - g_assert_cmpstr(err, ==, ""); - g_assert_cmpint(status, ==, 0); - - kf = g_key_file_new(); - g_key_file_load_from_data(kf, out, -1, G_KEY_FILE_NONE, &error); - g_assert_no_error(error); - - str = g_key_file_get_start_group(kf); - g_assert_cmpstr(str, ==, "general"); - g_free(str); - - g_assert_false(g_key_file_get_boolean(kf, "general", "daemon", &error)); - g_assert_no_error(error); - - str = g_key_file_get_string(kf, "general", "method", &error); - g_assert_no_error(error); - g_assert_cmpstr(str, ==, "virtio-serial"); - g_free(str); - - str = g_key_file_get_string(kf, "general", "path", &error); - g_assert_no_error(error); - g_assert_cmpstr(str, ==, "/path/to/org.qemu.guest_agent.0"); - g_free(str); - - str = g_key_file_get_string(kf, "general", "pidfile", &error); - g_assert_no_error(error); - g_assert_cmpstr(str, ==, "/var/foo/qemu-ga.pid"); - g_free(str); - - str = g_key_file_get_string(kf, "general", "statedir", &error); - g_assert_no_error(error); - g_assert_cmpstr(str, ==, "/var/state"); - g_free(str); - - g_assert_true(g_key_file_get_boolean(kf, "general", "verbose", &error)); - g_assert_no_error(error); - - strv = g_key_file_get_string_list(kf, "general", "blacklist", &n, &error); - g_assert_cmpint(n, ==, 2); - g_assert_true(g_strv_contains((const char * const *)strv, - "guest-ping")); - g_assert_true(g_strv_contains((const char * const *)strv, - "guest-get-time")); - g_assert_no_error(error); - g_strfreev(strv); - - g_free(out); - g_free(err); - g_free(env[0]); - g_key_file_free(kf); -} - -static void test_qga_fsfreeze_status(gconstpointer fix) -{ - const TestFixture *fixture = fix; - QDict *ret; - const gchar *status; - - ret = qmp_fd(fixture->fd, "{'execute': 'guest-fsfreeze-status'}"); - g_assert_nonnull(ret); - qmp_assert_no_error(ret); - - status = qdict_get_try_str(ret, "return"); - g_assert_cmpstr(status, ==, "thawed"); - - qobject_unref(ret); -} - -static void test_qga_guest_exec(gconstpointer fix) -{ - const TestFixture *fixture = fix; - QDict *ret, *val; - const gchar *out; - guchar *decoded; - int64_t pid, now, exitcode; - gsize len; - bool exited; - - /* exec 'echo foo bar' */ - ret = qmp_fd(fixture->fd, "{'execute': 'guest-exec', 'arguments': {" - " 'path': '/bin/echo', 'arg': [ '-n', '\" test_str \"' ]," - " 'capture-output': true } }"); - g_assert_nonnull(ret); - qmp_assert_no_error(ret); - val = qdict_get_qdict(ret, "return"); - pid = qdict_get_int(val, "pid"); - g_assert_cmpint(pid, >, 0); - qobject_unref(ret); - - /* wait for completion */ - now = g_get_monotonic_time(); - do { - ret = qmp_fd(fixture->fd, - "{'execute': 'guest-exec-status'," - " 'arguments': { 'pid': %" PRId64 " } }", pid); - g_assert_nonnull(ret); - val = qdict_get_qdict(ret, "return"); - exited = qdict_get_bool(val, "exited"); - if (!exited) { - qobject_unref(ret); - } - } while (!exited && - g_get_monotonic_time() < now + 5 * G_TIME_SPAN_SECOND); - g_assert(exited); - - /* check stdout */ - exitcode = qdict_get_int(val, "exitcode"); - g_assert_cmpint(exitcode, ==, 0); - out = qdict_get_str(val, "out-data"); - decoded = g_base64_decode(out, &len); - g_assert_cmpint(len, ==, 12); - g_assert_cmpstr((char *)decoded, ==, "\" test_str \""); - g_free(decoded); - qobject_unref(ret); -} - -static void test_qga_guest_exec_invalid(gconstpointer fix) -{ - const TestFixture *fixture = fix; - QDict *ret, *error; - const gchar *class, *desc; - - /* invalid command */ - ret = qmp_fd(fixture->fd, "{'execute': 'guest-exec', 'arguments': {" - " 'path': '/bin/invalid-cmd42' } }"); - g_assert_nonnull(ret); - error = qdict_get_qdict(ret, "error"); - g_assert_nonnull(error); - class = qdict_get_str(error, "class"); - desc = qdict_get_str(error, "desc"); - g_assert_cmpstr(class, ==, "GenericError"); - g_assert_cmpint(strlen(desc), >, 0); - qobject_unref(ret); - - /* invalid pid */ - ret = qmp_fd(fixture->fd, "{'execute': 'guest-exec-status'," - " 'arguments': { 'pid': 0 } }"); - g_assert_nonnull(ret); - error = qdict_get_qdict(ret, "error"); - g_assert_nonnull(error); - class = qdict_get_str(error, "class"); - desc = qdict_get_str(error, "desc"); - g_assert_cmpstr(class, ==, "GenericError"); - g_assert_cmpint(strlen(desc), >, 0); - qobject_unref(ret); -} - -static void test_qga_guest_get_host_name(gconstpointer fix) -{ - const TestFixture *fixture = fix; - QDict *ret, *val; - - ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-host-name'}"); - g_assert_nonnull(ret); - qmp_assert_no_error(ret); - - val = qdict_get_qdict(ret, "return"); - g_assert(qdict_haskey(val, "host-name")); - - qobject_unref(ret); -} - -static void test_qga_guest_get_timezone(gconstpointer fix) -{ - const TestFixture *fixture = fix; - QDict *ret, *val; - - ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-timezone'}"); - g_assert_nonnull(ret); - qmp_assert_no_error(ret); - - /* Make sure there's at least offset */ - val = qdict_get_qdict(ret, "return"); - g_assert(qdict_haskey(val, "offset")); - - qobject_unref(ret); -} - -static void test_qga_guest_get_users(gconstpointer fix) -{ - const TestFixture *fixture = fix; - QDict *ret; - QList *val; - - ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-users'}"); - g_assert_nonnull(ret); - qmp_assert_no_error(ret); - - /* There is not much to test here */ - val = qdict_get_qlist(ret, "return"); - g_assert_nonnull(val); - - qobject_unref(ret); -} - -static void test_qga_guest_get_osinfo(gconstpointer data) -{ - TestFixture fixture; - const gchar *str; - gchar *cwd, *env[2]; - QDict *ret, *val; - - cwd = g_get_current_dir(); - env[0] = g_strdup_printf( - "QGA_OS_RELEASE=%s%ctests%cdata%ctest-qga-os-release", - cwd, G_DIR_SEPARATOR, G_DIR_SEPARATOR, G_DIR_SEPARATOR); - env[1] = NULL; - g_free(cwd); - fixture_setup(&fixture, NULL, env); - - ret = qmp_fd(fixture.fd, "{'execute': 'guest-get-osinfo'}"); - g_assert_nonnull(ret); - qmp_assert_no_error(ret); - - val = qdict_get_qdict(ret, "return"); - - str = qdict_get_try_str(val, "id"); - g_assert_nonnull(str); - g_assert_cmpstr(str, ==, "qemu-ga-test"); - - str = qdict_get_try_str(val, "name"); - g_assert_nonnull(str); - g_assert_cmpstr(str, ==, "QEMU-GA"); - - str = qdict_get_try_str(val, "pretty-name"); - g_assert_nonnull(str); - g_assert_cmpstr(str, ==, "QEMU Guest Agent test"); - - str = qdict_get_try_str(val, "version"); - g_assert_nonnull(str); - g_assert_cmpstr(str, ==, "Test 1"); - - str = qdict_get_try_str(val, "version-id"); - g_assert_nonnull(str); - g_assert_cmpstr(str, ==, "1"); - - str = qdict_get_try_str(val, "variant"); - g_assert_nonnull(str); - g_assert_cmpstr(str, ==, "Unit test \"'$`\\ and \\\\ etc."); - - str = qdict_get_try_str(val, "variant-id"); - g_assert_nonnull(str); - g_assert_cmpstr(str, ==, "unit-test"); - - qobject_unref(ret); - g_free(env[0]); - fixture_tear_down(&fixture, NULL); -} - -int main(int argc, char **argv) -{ - TestFixture fix; - int ret; - - setlocale (LC_ALL, ""); - g_test_init(&argc, &argv, NULL); - fixture_setup(&fix, NULL, NULL); - - g_test_add_data_func("/qga/sync-delimited", &fix, test_qga_sync_delimited); - g_test_add_data_func("/qga/sync", &fix, test_qga_sync); - g_test_add_data_func("/qga/ping", &fix, test_qga_ping); - g_test_add_data_func("/qga/info", &fix, test_qga_info); - g_test_add_data_func("/qga/network-get-interfaces", &fix, - test_qga_network_get_interfaces); - if (!access("/sys/devices/system/cpu/cpu0", F_OK)) { - g_test_add_data_func("/qga/get-vcpus", &fix, test_qga_get_vcpus); - } - g_test_add_data_func("/qga/get-fsinfo", &fix, test_qga_get_fsinfo); - g_test_add_data_func("/qga/get-memory-block-info", &fix, - test_qga_get_memory_block_info); - g_test_add_data_func("/qga/get-memory-blocks", &fix, - test_qga_get_memory_blocks); - g_test_add_data_func("/qga/file-ops", &fix, test_qga_file_ops); - g_test_add_data_func("/qga/file-write-read", &fix, test_qga_file_write_read); - g_test_add_data_func("/qga/get-time", &fix, test_qga_get_time); - g_test_add_data_func("/qga/id", &fix, test_qga_id); - g_test_add_data_func("/qga/invalid-oob", &fix, test_qga_invalid_oob); - g_test_add_data_func("/qga/invalid-cmd", &fix, test_qga_invalid_cmd); - g_test_add_data_func("/qga/invalid-args", &fix, test_qga_invalid_args); - g_test_add_data_func("/qga/fsfreeze-status", &fix, - test_qga_fsfreeze_status); - - g_test_add_data_func("/qga/blacklist", NULL, test_qga_blacklist); - g_test_add_data_func("/qga/config", NULL, test_qga_config); - g_test_add_data_func("/qga/guest-exec", &fix, test_qga_guest_exec); - g_test_add_data_func("/qga/guest-exec-invalid", &fix, - test_qga_guest_exec_invalid); - g_test_add_data_func("/qga/guest-get-osinfo", &fix, - test_qga_guest_get_osinfo); - g_test_add_data_func("/qga/guest-get-host-name", &fix, - test_qga_guest_get_host_name); - g_test_add_data_func("/qga/guest-get-timezone", &fix, - test_qga_guest_get_timezone); - g_test_add_data_func("/qga/guest-get-users", &fix, - test_qga_guest_get_users); - - ret = g_test_run(); - - fixture_tear_down(&fix, NULL); - - return ret; -} diff --git a/tests/test-qgraph.c b/tests/test-qgraph.c deleted file mode 100644 index ae2f7b2dd8..0000000000 --- a/tests/test-qgraph.c +++ /dev/null @@ -1,434 +0,0 @@ -/* - * libqos driver framework - * - * Copyright (c) 2018 Emanuele Giuseppe Esposito - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License version 2.1 as published by the Free Software Foundation. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see - */ - -#include "qemu/osdep.h" -#include "qtest/libqos/qgraph.h" -#include "qtest/libqos/qgraph_internal.h" - -#define MACHINE_PC "x86_64/pc" -#define MACHINE_RASPI2 "arm/raspi2" -#define I440FX "i440FX-pcihost" -#define PCIBUS_PC "pcibus-pc" -#define SDHCI "sdhci" -#define PCIBUS "pci-bus" -#define SDHCI_PCI "sdhci-pci" -#define SDHCI_MM "generic-sdhci" -#define REGISTER_TEST "register-test" - -int npath; - -static void *machinefunct(QTestState *qts) -{ - return NULL; -} - -static void *driverfunct(void *obj, QGuestAllocator *machine, void *arg) -{ - return NULL; -} - -static void testfunct(void *obj, void *arg, QGuestAllocator *alloc) -{ - return; -} - -static void check_interface(const char *interface) -{ - g_assert_cmpint(qos_graph_has_machine(interface), ==, FALSE); - g_assert_nonnull(qos_graph_get_node(interface)); - g_assert_cmpint(qos_graph_has_node(interface), ==, TRUE); - g_assert_cmpint(qos_graph_get_node_type(interface), ==, QNODE_INTERFACE); - qos_graph_node_set_availability(interface, TRUE); - g_assert_cmpint(qos_graph_get_node_availability(interface), ==, TRUE); -} - -static void check_machine(const char *machine) -{ - qos_node_create_machine(machine, machinefunct); - g_assert_nonnull(qos_graph_get_machine(machine)); - g_assert_cmpint(qos_graph_has_machine(machine), ==, TRUE); - g_assert_nonnull(qos_graph_get_node(machine)); - g_assert_cmpint(qos_graph_get_node_availability(machine), ==, FALSE); - qos_graph_node_set_availability(machine, TRUE); - g_assert_cmpint(qos_graph_get_node_availability(machine), ==, TRUE); - g_assert_cmpint(qos_graph_has_node(machine), ==, TRUE); - g_assert_cmpint(qos_graph_get_node_type(machine), ==, QNODE_MACHINE); -} - -static void check_contains(const char *machine, const char *driver) -{ - QOSGraphEdge *edge; - qos_node_contains(machine, driver, NULL); - - edge = qos_graph_get_edge(machine, driver); - g_assert_nonnull(edge); - g_assert_cmpint(qos_graph_edge_get_type(edge), ==, QEDGE_CONTAINS); - g_assert_cmpint(qos_graph_has_edge(machine, driver), ==, TRUE); -} - -static void check_produces(const char *machine, const char *interface) -{ - QOSGraphEdge *edge; - - qos_node_produces(machine, interface); - check_interface(interface); - edge = qos_graph_get_edge(machine, interface); - g_assert_nonnull(edge); - g_assert_cmpint(qos_graph_edge_get_type(edge), ==, - QEDGE_PRODUCES); - g_assert_cmpint(qos_graph_has_edge(machine, interface), ==, TRUE); -} - -static void check_consumes(const char *driver, const char *interface) -{ - QOSGraphEdge *edge; - - qos_node_consumes(driver, interface, NULL); - check_interface(interface); - edge = qos_graph_get_edge(interface, driver); - g_assert_nonnull(edge); - g_assert_cmpint(qos_graph_edge_get_type(edge), ==, QEDGE_CONSUMED_BY); - g_assert_cmpint(qos_graph_has_edge(interface, driver), ==, TRUE); -} - -static void check_driver(const char *driver) -{ - qos_node_create_driver(driver, driverfunct); - g_assert_cmpint(qos_graph_has_machine(driver), ==, FALSE); - g_assert_nonnull(qos_graph_get_node(driver)); - g_assert_cmpint(qos_graph_has_node(driver), ==, TRUE); - g_assert_cmpint(qos_graph_get_node_type(driver), ==, QNODE_DRIVER); - g_assert_cmpint(qos_graph_get_node_availability(driver), ==, FALSE); - qos_graph_node_set_availability(driver, TRUE); - g_assert_cmpint(qos_graph_get_node_availability(driver), ==, TRUE); -} - -static void check_test(const char *test, const char *interface) -{ - QOSGraphEdge *edge; - char *full_name = g_strdup_printf("%s-tests/%s", interface, test); - - qos_add_test(test, interface, testfunct, NULL); - g_assert_cmpint(qos_graph_has_machine(test), ==, FALSE); - g_assert_cmpint(qos_graph_has_machine(full_name), ==, FALSE); - g_assert_nonnull(qos_graph_get_node(full_name)); - g_assert_cmpint(qos_graph_has_node(full_name), ==, TRUE); - g_assert_cmpint(qos_graph_get_node_type(full_name), ==, QNODE_TEST); - edge = qos_graph_get_edge(interface, full_name); - g_assert_nonnull(edge); - g_assert_cmpint(qos_graph_edge_get_type(edge), ==, - QEDGE_CONSUMED_BY); - g_assert_cmpint(qos_graph_has_edge(interface, full_name), ==, TRUE); - g_assert_cmpint(qos_graph_get_node_availability(full_name), ==, TRUE); - qos_graph_node_set_availability(full_name, FALSE); - g_assert_cmpint(qos_graph_get_node_availability(full_name), ==, FALSE); - g_free(full_name); -} - -static void count_each_test(QOSGraphNode *path, int len) -{ - npath++; -} - -static void check_leaf_discovered(int n) -{ - npath = 0; - qos_graph_foreach_test_path(count_each_test); - g_assert_cmpint(n, ==, npath); -} - -/* G_Test functions */ - -static void init_nop(void) -{ - qos_graph_init(); - qos_graph_destroy(); -} - -static void test_machine(void) -{ - qos_graph_init(); - check_machine(MACHINE_PC); - qos_graph_destroy(); -} - -static void test_contains(void) -{ - qos_graph_init(); - check_contains(MACHINE_PC, I440FX); - g_assert_null(qos_graph_get_machine(MACHINE_PC)); - g_assert_null(qos_graph_get_machine(I440FX)); - g_assert_null(qos_graph_get_node(MACHINE_PC)); - g_assert_null(qos_graph_get_node(I440FX)); - qos_graph_destroy(); -} - -static void test_multiple_contains(void) -{ - qos_graph_init(); - check_contains(MACHINE_PC, I440FX); - check_contains(MACHINE_PC, PCIBUS_PC); - qos_graph_destroy(); -} - -static void test_produces(void) -{ - qos_graph_init(); - check_produces(MACHINE_PC, I440FX); - g_assert_null(qos_graph_get_machine(MACHINE_PC)); - g_assert_null(qos_graph_get_machine(I440FX)); - g_assert_null(qos_graph_get_node(MACHINE_PC)); - g_assert_nonnull(qos_graph_get_node(I440FX)); - qos_graph_destroy(); -} - -static void test_multiple_produces(void) -{ - qos_graph_init(); - check_produces(MACHINE_PC, I440FX); - check_produces(MACHINE_PC, PCIBUS_PC); - qos_graph_destroy(); -} - -static void test_consumes(void) -{ - qos_graph_init(); - check_consumes(I440FX, SDHCI); - g_assert_null(qos_graph_get_machine(I440FX)); - g_assert_null(qos_graph_get_machine(SDHCI)); - g_assert_null(qos_graph_get_node(I440FX)); - g_assert_nonnull(qos_graph_get_node(SDHCI)); - qos_graph_destroy(); -} - -static void test_multiple_consumes(void) -{ - qos_graph_init(); - check_consumes(I440FX, SDHCI); - check_consumes(PCIBUS_PC, SDHCI); - qos_graph_destroy(); -} - -static void test_driver(void) -{ - qos_graph_init(); - check_driver(I440FX); - qos_graph_destroy(); -} - -static void test_test(void) -{ - qos_graph_init(); - check_test(REGISTER_TEST, SDHCI); - qos_graph_destroy(); -} - -static void test_machine_contains_driver(void) -{ - qos_graph_init(); - check_machine(MACHINE_PC); - check_driver(I440FX); - check_contains(MACHINE_PC, I440FX); - qos_graph_destroy(); -} - -static void test_driver_contains_driver(void) -{ - qos_graph_init(); - check_driver(PCIBUS_PC); - check_driver(I440FX); - check_contains(PCIBUS_PC, I440FX); - qos_graph_destroy(); -} - -static void test_machine_produces_interface(void) -{ - qos_graph_init(); - check_machine(MACHINE_PC); - check_produces(MACHINE_PC, SDHCI); - qos_graph_destroy(); -} - -static void test_driver_produces_interface(void) -{ - qos_graph_init(); - check_driver(I440FX); - check_produces(I440FX, SDHCI); - qos_graph_destroy(); -} - -static void test_machine_consumes_interface(void) -{ - qos_graph_init(); - check_machine(MACHINE_PC); - check_consumes(MACHINE_PC, SDHCI); - qos_graph_destroy(); -} - -static void test_driver_consumes_interface(void) -{ - qos_graph_init(); - check_driver(I440FX); - check_consumes(I440FX, SDHCI); - qos_graph_destroy(); -} - -static void test_test_consumes_interface(void) -{ - qos_graph_init(); - check_test(REGISTER_TEST, SDHCI); - qos_graph_destroy(); -} - -static void test_full_sample(void) -{ - qos_graph_init(); - check_machine(MACHINE_PC); - check_contains(MACHINE_PC, I440FX); - check_driver(I440FX); - check_driver(PCIBUS_PC); - check_contains(I440FX, PCIBUS_PC); - check_produces(PCIBUS_PC, PCIBUS); - check_driver(SDHCI_PCI); - qos_node_consumes(SDHCI_PCI, PCIBUS, NULL); - check_produces(SDHCI_PCI, SDHCI); - check_driver(SDHCI_MM); - check_produces(SDHCI_MM, SDHCI); - qos_add_test(REGISTER_TEST, SDHCI, testfunct, NULL); - check_leaf_discovered(1); - qos_print_graph(); - qos_graph_destroy(); -} - -static void test_full_sample_raspi(void) -{ - qos_graph_init(); - check_machine(MACHINE_PC); - check_contains(MACHINE_PC, I440FX); - check_driver(I440FX); - check_driver(PCIBUS_PC); - check_contains(I440FX, PCIBUS_PC); - check_produces(PCIBUS_PC, PCIBUS); - check_driver(SDHCI_PCI); - qos_node_consumes(SDHCI_PCI, PCIBUS, NULL); - check_produces(SDHCI_PCI, SDHCI); - check_machine(MACHINE_RASPI2); - check_contains(MACHINE_RASPI2, SDHCI_MM); - check_driver(SDHCI_MM); - check_produces(SDHCI_MM, SDHCI); - qos_add_test(REGISTER_TEST, SDHCI, testfunct, NULL); - qos_print_graph(); - check_leaf_discovered(2); - qos_graph_destroy(); -} - -static void test_cycle(void) -{ - qos_graph_init(); - check_machine(MACHINE_RASPI2); - check_driver("B"); - check_driver("C"); - check_driver("D"); - check_contains(MACHINE_RASPI2, "B"); - check_contains("B", "C"); - check_contains("C", "D"); - check_contains("D", MACHINE_RASPI2); - check_leaf_discovered(0); - qos_print_graph(); - qos_graph_destroy(); -} - -static void test_two_test_same_interface(void) -{ - qos_graph_init(); - check_machine(MACHINE_RASPI2); - check_produces(MACHINE_RASPI2, "B"); - qos_add_test("C", "B", testfunct, NULL); - qos_add_test("D", "B", testfunct, NULL); - check_contains(MACHINE_RASPI2, "B"); - check_leaf_discovered(4); - qos_print_graph(); - qos_graph_destroy(); -} - -static void test_test_in_path(void) -{ - qos_graph_init(); - check_machine(MACHINE_RASPI2); - check_produces(MACHINE_RASPI2, "B"); - qos_add_test("C", "B", testfunct, NULL); - check_driver("D"); - check_consumes("D", "B"); - check_produces("D", "E"); - qos_add_test("F", "E", testfunct, NULL); - check_leaf_discovered(2); - qos_print_graph(); - qos_graph_destroy(); -} - -static void test_double_edge(void) -{ - qos_graph_init(); - check_machine(MACHINE_RASPI2); - check_produces("B", "C"); - qos_node_consumes("C", "B", NULL); - qos_add_test("D", "C", testfunct, NULL); - check_contains(MACHINE_RASPI2, "B"); - qos_print_graph(); - qos_graph_destroy(); -} - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - g_test_add_func("/qgraph/init_nop", init_nop); - g_test_add_func("/qgraph/test_machine", test_machine); - g_test_add_func("/qgraph/test_contains", test_contains); - g_test_add_func("/qgraph/test_multiple_contains", test_multiple_contains); - g_test_add_func("/qgraph/test_produces", test_produces); - g_test_add_func("/qgraph/test_multiple_produces", test_multiple_produces); - g_test_add_func("/qgraph/test_consumes", test_consumes); - g_test_add_func("/qgraph/test_multiple_consumes", - test_multiple_consumes); - g_test_add_func("/qgraph/test_driver", test_driver); - g_test_add_func("/qgraph/test_test", test_test); - g_test_add_func("/qgraph/test_machine_contains_driver", - test_machine_contains_driver); - g_test_add_func("/qgraph/test_driver_contains_driver", - test_driver_contains_driver); - g_test_add_func("/qgraph/test_machine_produces_interface", - test_machine_produces_interface); - g_test_add_func("/qgraph/test_driver_produces_interface", - test_driver_produces_interface); - g_test_add_func("/qgraph/test_machine_consumes_interface", - test_machine_consumes_interface); - g_test_add_func("/qgraph/test_driver_consumes_interface", - test_driver_consumes_interface); - g_test_add_func("/qgraph/test_test_consumes_interface", - test_test_consumes_interface); - g_test_add_func("/qgraph/test_full_sample", test_full_sample); - g_test_add_func("/qgraph/test_full_sample_raspi", test_full_sample_raspi); - g_test_add_func("/qgraph/test_cycle", test_cycle); - g_test_add_func("/qgraph/test_two_test_same_interface", - test_two_test_same_interface); - g_test_add_func("/qgraph/test_test_in_path", test_test_in_path); - g_test_add_func("/qgraph/test_double_edge", test_double_edge); - - g_test_run(); - return 0; -} diff --git a/tests/test-qht.c b/tests/test-qht.c deleted file mode 100644 index 4d23cefab6..0000000000 --- a/tests/test-qht.c +++ /dev/null @@ -1,260 +0,0 @@ -/* - * Copyright (C) 2016, Emilio G. Cota - * - * License: GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ -#include "qemu/osdep.h" -#include "qemu/qht.h" -#include "qemu/rcu.h" - -#define N 5000 - -static struct qht ht; -static int32_t arr[N * 2]; - -static bool is_equal(const void *ap, const void *bp) -{ - const int32_t *a = ap; - const int32_t *b = bp; - - return *a == *b; -} - -static void insert(int a, int b) -{ - int i; - - for (i = a; i < b; i++) { - uint32_t hash; - void *existing; - bool inserted; - - arr[i] = i; - hash = i; - - inserted = qht_insert(&ht, &arr[i], hash, NULL); - g_assert_true(inserted); - inserted = qht_insert(&ht, &arr[i], hash, &existing); - g_assert_false(inserted); - g_assert_true(existing == &arr[i]); - } -} - -static void do_rm(int init, int end, bool exist) -{ - int i; - - for (i = init; i < end; i++) { - uint32_t hash; - - hash = arr[i]; - if (exist) { - g_assert_true(qht_remove(&ht, &arr[i], hash)); - } else { - g_assert_false(qht_remove(&ht, &arr[i], hash)); - } - } -} - -static void rm(int init, int end) -{ - do_rm(init, end, true); -} - -static void rm_nonexist(int init, int end) -{ - do_rm(init, end, false); -} - -static void check(int a, int b, bool expected) -{ - struct qht_stats stats; - int i; - - rcu_read_lock(); - for (i = a; i < b; i++) { - void *p; - uint32_t hash; - int32_t val; - - val = i; - hash = i; - /* test both lookup variants; results should be the same */ - if (i % 2) { - p = qht_lookup(&ht, &val, hash); - } else { - p = qht_lookup_custom(&ht, &val, hash, is_equal); - } - g_assert_true(!!p == expected); - } - rcu_read_unlock(); - - qht_statistics_init(&ht, &stats); - if (stats.used_head_buckets) { - g_assert_cmpfloat(qdist_avg(&stats.chain), >=, 1.0); - } - g_assert_cmpuint(stats.head_buckets, >, 0); - qht_statistics_destroy(&stats); -} - -static void count_func(void *p, uint32_t hash, void *userp) -{ - unsigned int *curr = userp; - - (*curr)++; -} - -static void check_n(size_t expected) -{ - struct qht_stats stats; - - qht_statistics_init(&ht, &stats); - g_assert_cmpuint(stats.entries, ==, expected); - qht_statistics_destroy(&stats); -} - -static void iter_check(unsigned int count) -{ - unsigned int curr = 0; - - qht_iter(&ht, count_func, &curr); - g_assert_cmpuint(curr, ==, count); -} - -static void sum_func(void *p, uint32_t hash, void *userp) -{ - uint32_t *sum = userp; - uint32_t a = *(uint32_t *)p; - - *sum += a; -} - -static void iter_sum_check(unsigned int expected) -{ - unsigned int sum = 0; - - qht_iter(&ht, sum_func, &sum); - g_assert_cmpuint(sum, ==, expected); -} - -static bool rm_mod_func(void *p, uint32_t hash, void *userp) -{ - uint32_t a = *(uint32_t *)p; - unsigned int mod = *(unsigned int *)userp; - - return a % mod == 0; -} - -static void iter_rm_mod(unsigned int mod) -{ - qht_iter_remove(&ht, rm_mod_func, &mod); -} - -static void iter_rm_mod_check(unsigned int mod) -{ - unsigned int expected = 0; - unsigned int i; - - for (i = 0; i < N; i++) { - if (i % mod == 0) { - continue; - } - expected += i; - } - iter_sum_check(expected); -} - -static void qht_do_test(unsigned int mode, size_t init_entries) -{ - /* under KVM we might fetch stats from an uninitialized qht */ - check_n(0); - - qht_init(&ht, is_equal, 0, mode); - rm_nonexist(0, 4); - /* - * Test that we successfully delete the last element in a bucket. - * This is a hard-to-reach code path when resizing is on, but without - * resizing we can easily hit it if init_entries <= 1. - * Given that the number of elements per bucket can be 4 or 6 depending on - * the host's pointer size, test the removal of the 4th and 6th elements. - */ - insert(0, 4); - rm_nonexist(5, 6); - rm(3, 4); - check_n(3); - insert(3, 6); - rm(5, 6); - check_n(5); - rm_nonexist(7, 8); - iter_rm_mod(1); - - if (!(mode & QHT_MODE_AUTO_RESIZE)) { - qht_resize(&ht, init_entries * 4 + 4); - } - - check_n(0); - rm_nonexist(0, 10); - insert(0, N); - check(0, N, true); - check_n(N); - check(-N, -1, false); - iter_check(N); - - rm(101, 102); - check_n(N - 1); - insert(N, N * 2); - check_n(N + N - 1); - rm(N, N * 2); - check_n(N - 1); - insert(101, 102); - check_n(N); - - rm(10, 200); - check_n(N - 190); - insert(150, 200); - check_n(N - 190 + 50); - insert(10, 150); - check_n(N); - - qht_reset(&ht); - insert(0, N); - rm_nonexist(N, N + 32); - iter_rm_mod(10); - iter_rm_mod_check(10); - check_n(N * 9 / 10); - qht_reset_size(&ht, 0); - check_n(0); - check(0, N, false); - - qht_destroy(&ht); -} - -static void qht_test(unsigned int mode) -{ - qht_do_test(mode, 0); - qht_do_test(mode, 1); - qht_do_test(mode, 2); - qht_do_test(mode, 8); - qht_do_test(mode, 16); - qht_do_test(mode, 8192); - qht_do_test(mode, 16384); -} - -static void test_default(void) -{ - qht_test(0); -} - -static void test_resize(void) -{ - qht_test(QHT_MODE_AUTO_RESIZE); -} - -int main(int argc, char *argv[]) -{ - g_test_init(&argc, &argv, NULL); - g_test_add_func("/qht/mode/default", test_default); - g_test_add_func("/qht/mode/resize", test_resize); - return g_test_run(); -} diff --git a/tests/test-qmp-cmds.c b/tests/test-qmp-cmds.c deleted file mode 100644 index d3413bfef0..0000000000 --- a/tests/test-qmp-cmds.c +++ /dev/null @@ -1,359 +0,0 @@ -#include "qemu/osdep.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qjson.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qstring.h" -#include "qapi/error.h" -#include "qapi/qobject-input-visitor.h" -#include "tests/test-qapi-types.h" -#include "tests/test-qapi-visit.h" -#include "test-qapi-commands.h" -#include "test-qapi-init-commands.h" - -static QmpCommandList qmp_commands; - -#if defined(TEST_IF_STRUCT) && defined(TEST_IF_CMD) -UserDefThree *qmp_TestIfCmd(TestIfStruct *foo, Error **errp) -{ - return NULL; -} -#endif - -UserDefThree *qmp_TestCmdReturnDefThree(Error **errp) -{ - return NULL; -} - -void qmp_user_def_cmd(Error **errp) -{ -} - -void qmp_test_flags_command(Error **errp) -{ -} - -void qmp_cmd_success_response(Error **errp) -{ -} - -void qmp_coroutine_cmd(Error **errp) -{ -} - -Empty2 *qmp_user_def_cmd0(Error **errp) -{ - return g_new0(Empty2, 1); -} - -void qmp_user_def_cmd1(UserDefOne * ud1, Error **errp) -{ -} - -void qmp_test_features0(FeatureStruct0 *fs0, FeatureStruct1 *fs1, - FeatureStruct2 *fs2, FeatureStruct3 *fs3, - FeatureStruct4 *fs4, CondFeatureStruct1 *cfs1, - CondFeatureStruct2 *cfs2, CondFeatureStruct3 *cfs3, - Error **errp) -{ -} - -void qmp_test_command_features1(Error **errp) -{ -} - -void qmp_test_command_features3(Error **errp) -{ -} - -void qmp_test_command_cond_features1(Error **errp) -{ -} - -void qmp_test_command_cond_features2(Error **errp) -{ -} - -void qmp_test_command_cond_features3(Error **errp) -{ -} - -UserDefTwo *qmp_user_def_cmd2(UserDefOne *ud1a, - bool has_udb1, UserDefOne *ud1b, - Error **errp) -{ - UserDefTwo *ret; - UserDefOne *ud1c = g_malloc0(sizeof(UserDefOne)); - UserDefOne *ud1d = g_malloc0(sizeof(UserDefOne)); - - ud1c->string = strdup(ud1a->string); - ud1c->integer = ud1a->integer; - ud1d->string = strdup(has_udb1 ? ud1b->string : "blah0"); - ud1d->integer = has_udb1 ? ud1b->integer : 0; - - ret = g_new0(UserDefTwo, 1); - ret->string0 = strdup("blah1"); - ret->dict1 = g_new0(UserDefTwoDict, 1); - ret->dict1->string1 = strdup("blah2"); - ret->dict1->dict2 = g_new0(UserDefTwoDictDict, 1); - ret->dict1->dict2->userdef = ud1c; - ret->dict1->dict2->string = strdup("blah3"); - ret->dict1->dict3 = g_new0(UserDefTwoDictDict, 1); - ret->dict1->has_dict3 = true; - ret->dict1->dict3->userdef = ud1d; - ret->dict1->dict3->string = strdup("blah4"); - - return ret; -} - -int64_t qmp_guest_get_time(int64_t a, bool has_b, int64_t b, Error **errp) -{ - return a + (has_b ? b : 0); -} - -QObject *qmp_guest_sync(QObject *arg, Error **errp) -{ - return arg; -} - -void qmp_boxed_struct(UserDefZero *arg, Error **errp) -{ -} - -void qmp_boxed_union(UserDefListUnion *arg, Error **errp) -{ -} - -void qmp_boxed_empty(Empty1 *arg, Error **errp) -{ -} - -__org_qemu_x_Union1 *qmp___org_qemu_x_command(__org_qemu_x_EnumList *a, - __org_qemu_x_StructList *b, - __org_qemu_x_Union2 *c, - __org_qemu_x_Alt *d, - Error **errp) -{ - __org_qemu_x_Union1 *ret = g_new0(__org_qemu_x_Union1, 1); - - ret->type = ORG_QEMU_X_UNION1_KIND___ORG_QEMU_X_BRANCH; - ret->u.__org_qemu_x_branch.data = strdup("blah1"); - - /* Also test that 'wchar-t' was munged to 'q_wchar_t' */ - if (b && b->value && !b->value->has_q_wchar_t) { - b->value->q_wchar_t = 1; - } - return ret; -} - - -static QObject *do_qmp_dispatch(bool allow_oob, const char *template, ...) -{ - va_list ap; - QDict *req, *resp; - QObject *ret; - - va_start(ap, template); - req = qdict_from_vjsonf_nofail(template, ap); - va_end(ap); - - resp = qmp_dispatch(&qmp_commands, QOBJECT(req), allow_oob, NULL); - g_assert(resp); - ret = qdict_get(resp, "return"); - g_assert(ret); - g_assert(qdict_size(resp) == 1); - - qobject_ref(ret); - qobject_unref(resp); - qobject_unref(req); - return ret; -} - -static void do_qmp_dispatch_error(bool allow_oob, ErrorClass cls, - const char *template, ...) -{ - va_list ap; - QDict *req, *resp; - QDict *error; - - va_start(ap, template); - req = qdict_from_vjsonf_nofail(template, ap); - va_end(ap); - - resp = qmp_dispatch(&qmp_commands, QOBJECT(req), allow_oob, NULL); - g_assert(resp); - error = qdict_get_qdict(resp, "error"); - g_assert(error); - g_assert_cmpstr(qdict_get_try_str(error, "class"), - ==, QapiErrorClass_str(cls)); - g_assert(qdict_get_try_str(error, "desc")); - g_assert(qdict_size(error) == 2); - g_assert(qdict_size(resp) == 1); - - qobject_unref(resp); - qobject_unref(req); -} - -/* test commands with no input and no return value */ -static void test_dispatch_cmd(void) -{ - QDict *ret; - - ret = qobject_to(QDict, - do_qmp_dispatch(false, - "{ 'execute': 'user_def_cmd' }")); - assert(ret && qdict_size(ret) == 0); - qobject_unref(ret); -} - -static void test_dispatch_cmd_oob(void) -{ - QDict *ret; - - ret = qobject_to(QDict, - do_qmp_dispatch(true, - "{ 'exec-oob': 'test-flags-command' }")); - assert(ret && qdict_size(ret) == 0); - qobject_unref(ret); -} - -/* test commands that return an error due to invalid parameters */ -static void test_dispatch_cmd_failure(void) -{ - /* missing arguments */ - do_qmp_dispatch_error(false, ERROR_CLASS_GENERIC_ERROR, - "{ 'execute': 'user_def_cmd2' }"); - - /* extra arguments */ - do_qmp_dispatch_error(false, ERROR_CLASS_GENERIC_ERROR, - "{ 'execute': 'user_def_cmd'," - " 'arguments': { 'a': 66 } }"); -} - -static void test_dispatch_cmd_success_response(void) -{ - QDict *req = qdict_new(); - QDict *resp; - - qdict_put_str(req, "execute", "cmd-success-response"); - resp = qmp_dispatch(&qmp_commands, QOBJECT(req), false, NULL); - g_assert_null(resp); - qobject_unref(req); -} - -/* test commands that involve both input parameters and return values */ -static void test_dispatch_cmd_io(void) -{ - QDict *ret, *ret_dict, *ret_dict_dict, *ret_dict_dict_userdef; - QDict *ret_dict_dict2, *ret_dict_dict2_userdef; - QNum *ret3; - int64_t val; - - ret = qobject_to(QDict, do_qmp_dispatch(false, - "{ 'execute': 'user_def_cmd2', 'arguments': {" - " 'ud1a': { 'integer': 42, 'string': 'hello' }," - " 'ud1b': { 'integer': 422, 'string': 'hello2' } } }")); - - assert(!strcmp(qdict_get_str(ret, "string0"), "blah1")); - ret_dict = qdict_get_qdict(ret, "dict1"); - assert(!strcmp(qdict_get_str(ret_dict, "string1"), "blah2")); - ret_dict_dict = qdict_get_qdict(ret_dict, "dict2"); - ret_dict_dict_userdef = qdict_get_qdict(ret_dict_dict, "userdef"); - assert(qdict_get_int(ret_dict_dict_userdef, "integer") == 42); - assert(!strcmp(qdict_get_str(ret_dict_dict_userdef, "string"), "hello")); - assert(!strcmp(qdict_get_str(ret_dict_dict, "string"), "blah3")); - ret_dict_dict2 = qdict_get_qdict(ret_dict, "dict3"); - ret_dict_dict2_userdef = qdict_get_qdict(ret_dict_dict2, "userdef"); - assert(qdict_get_int(ret_dict_dict2_userdef, "integer") == 422); - assert(!strcmp(qdict_get_str(ret_dict_dict2_userdef, "string"), "hello2")); - assert(!strcmp(qdict_get_str(ret_dict_dict2, "string"), "blah4")); - qobject_unref(ret); - - ret3 = qobject_to(QNum, do_qmp_dispatch(false, - "{ 'execute': 'guest-get-time', 'arguments': { 'a': 66 } }")); - g_assert(qnum_get_try_int(ret3, &val)); - g_assert_cmpint(val, ==, 66); - qobject_unref(ret3); -} - -/* test generated dealloc functions for generated types */ -static void test_dealloc_types(void) -{ - UserDefOne *ud1test, *ud1a, *ud1b; - UserDefOneList *ud1list; - - ud1test = g_malloc0(sizeof(UserDefOne)); - ud1test->integer = 42; - ud1test->string = g_strdup("hi there 42"); - - qapi_free_UserDefOne(ud1test); - - ud1a = g_malloc0(sizeof(UserDefOne)); - ud1a->integer = 43; - ud1a->string = g_strdup("hi there 43"); - - ud1b = g_malloc0(sizeof(UserDefOne)); - ud1b->integer = 44; - ud1b->string = g_strdup("hi there 44"); - - ud1list = g_malloc0(sizeof(UserDefOneList)); - ud1list->value = ud1a; - ud1list->next = g_malloc0(sizeof(UserDefOneList)); - ud1list->next->value = ud1b; - - qapi_free_UserDefOneList(ud1list); -} - -/* test generated deallocation on an object whose construction was prematurely - * terminated due to an error */ -static void test_dealloc_partial(void) -{ - static const char text[] = "don't leak me"; - - UserDefTwo *ud2 = NULL; - Error *err = NULL; - - /* create partial object */ - { - QDict *ud2_dict; - Visitor *v; - - ud2_dict = qdict_new(); - qdict_put_str(ud2_dict, "string0", text); - - v = qobject_input_visitor_new(QOBJECT(ud2_dict)); - visit_type_UserDefTwo(v, NULL, &ud2, &err); - visit_free(v); - qobject_unref(ud2_dict); - } - - /* verify that visit_type_XXX() cleans up properly on error */ - error_free_or_abort(&err); - assert(!ud2); - - /* Manually create a partial object, leaving ud2->dict1 at NULL */ - ud2 = g_new0(UserDefTwo, 1); - ud2->string0 = g_strdup(text); - - /* tear down partial object */ - qapi_free_UserDefTwo(ud2); -} - - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - - g_test_add_func("/qmp/dispatch_cmd", test_dispatch_cmd); - g_test_add_func("/qmp/dispatch_cmd_oob", test_dispatch_cmd_oob); - g_test_add_func("/qmp/dispatch_cmd_failure", test_dispatch_cmd_failure); - g_test_add_func("/qmp/dispatch_cmd_io", test_dispatch_cmd_io); - g_test_add_func("/qmp/dispatch_cmd_success_response", - test_dispatch_cmd_success_response); - g_test_add_func("/qmp/dealloc_types", test_dealloc_types); - g_test_add_func("/qmp/dealloc_partial", test_dealloc_partial); - - test_qmp_init_marshal(&qmp_commands); - g_test_run(); - - return 0; -} diff --git a/tests/test-qmp-event.c b/tests/test-qmp-event.c deleted file mode 100644 index 7dd0053190..0000000000 --- a/tests/test-qmp-event.c +++ /dev/null @@ -1,154 +0,0 @@ -/* - * qapi event unit-tests. - * - * Copyright (c) 2014 Wenchao Xia - * - * Authors: - * Wenchao Xia - * - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - * - */ - -#include "qemu/osdep.h" - -#include "qemu-common.h" -#include "qapi/error.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qjson.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qstring.h" -#include "qapi/qmp-event.h" -#include "test-qapi-events.h" -#include "test-qapi-emit-events.h" - -typedef struct TestEventData { - QDict *expect; - bool emitted; -} TestEventData; - -TestEventData *test_event_data; -static GMutex test_event_lock; - -void test_qapi_event_emit(test_QAPIEvent event, QDict *d) -{ - QDict *t; - int64_t s, ms; - - /* Verify that we have timestamp, then remove it to compare other fields */ - t = qdict_get_qdict(d, "timestamp"); - g_assert(t); - s = qdict_get_try_int(t, "seconds", -2); - ms = qdict_get_try_int(t, "microseconds", -2); - if (s == -1) { - g_assert(ms == -1); - } else { - g_assert(s >= 0); - g_assert(ms >= 0 && ms <= 999999); - } - g_assert(qdict_size(t) == 2); - - qdict_del(d, "timestamp"); - - g_assert(qobject_is_equal(QOBJECT(d), QOBJECT(test_event_data->expect))); - test_event_data->emitted = true; -} - -static void event_prepare(TestEventData *data, - const void *unused) -{ - /* Global variable test_event_data was used to pass the expectation, so - test cases can't be executed at same time. */ - g_mutex_lock(&test_event_lock); - test_event_data = data; -} - -static void event_teardown(TestEventData *data, - const void *unused) -{ - test_event_data = NULL; - g_mutex_unlock(&test_event_lock); -} - -static void event_test_add(const char *testpath, - void (*test_func)(TestEventData *data, - const void *user_data)) -{ - g_test_add(testpath, TestEventData, NULL, event_prepare, test_func, - event_teardown); -} - - -/* Test cases */ - -static void test_event_a(TestEventData *data, - const void *unused) -{ - data->expect = qdict_from_jsonf_nofail("{ 'event': 'EVENT_A' }"); - qapi_event_send_event_a(); - g_assert(data->emitted); - qobject_unref(data->expect); -} - -static void test_event_b(TestEventData *data, - const void *unused) -{ - data->expect = qdict_from_jsonf_nofail("{ 'event': 'EVENT_B' }"); - qapi_event_send_event_b(); - g_assert(data->emitted); - qobject_unref(data->expect); -} - -static void test_event_c(TestEventData *data, - const void *unused) -{ - UserDefOne b = { .integer = 2, .string = (char *)"test1" }; - - data->expect = qdict_from_jsonf_nofail( - "{ 'event': 'EVENT_C', 'data': {" - " 'a': 1, 'b': { 'integer': 2, 'string': 'test1' }, 'c': 'test2' } }"); - qapi_event_send_event_c(true, 1, true, &b, "test2"); - g_assert(data->emitted); - qobject_unref(data->expect); -} - -/* Complex type */ -static void test_event_d(TestEventData *data, - const void *unused) -{ - UserDefOne struct1 = { - .integer = 2, .string = (char *)"test1", - .has_enum1 = true, .enum1 = ENUM_ONE_VALUE1, - }; - EventStructOne a = { - .struct1 = &struct1, - .string = (char *)"test2", - .has_enum2 = true, - .enum2 = ENUM_ONE_VALUE2, - }; - - data->expect = qdict_from_jsonf_nofail( - "{ 'event': 'EVENT_D', 'data': {" - " 'a': {" - " 'struct1': { 'integer': 2, 'string': 'test1', 'enum1': 'value1' }," - " 'string': 'test2', 'enum2': 'value2' }," - " 'b': 'test3', 'enum3': 'value3' } }"); - qapi_event_send_event_d(&a, "test3", false, NULL, true, ENUM_ONE_VALUE3); - g_assert(data->emitted); - qobject_unref(data->expect); -} - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - - event_test_add("/event/event_a", test_event_a); - event_test_add("/event/event_b", test_event_b); - event_test_add("/event/event_c", test_event_c); - event_test_add("/event/event_d", test_event_d); - g_test_run(); - - return 0; -} diff --git a/tests/test-qobject-input-visitor.c b/tests/test-qobject-input-visitor.c deleted file mode 100644 index e41b91a2a6..0000000000 --- a/tests/test-qobject-input-visitor.c +++ /dev/null @@ -1,1379 +0,0 @@ -/* - * QObject Input Visitor unit-tests. - * - * Copyright (C) 2011-2016 Red Hat Inc. - * - * Authors: - * Luiz Capitulino - * Paolo Bonzini - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" - -#include "qemu-common.h" -#include "qapi/error.h" -#include "qapi/qapi-visit-introspect.h" -#include "qapi/qobject-input-visitor.h" -#include "test-qapi-visit.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qnull.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qstring.h" -#include "qapi/qmp/qjson.h" -#include "test-qapi-introspect.h" -#include "qapi/qapi-introspect.h" - -typedef struct TestInputVisitorData { - QObject *obj; - Visitor *qiv; -} TestInputVisitorData; - -static void visitor_input_teardown(TestInputVisitorData *data, - const void *unused) -{ - qobject_unref(data->obj); - data->obj = NULL; - - if (data->qiv) { - visit_free(data->qiv); - data->qiv = NULL; - } -} - -/* The various test_init functions are provided instead of a test setup - function so that the JSON string used by the tests are kept in the test - functions (and not in main()). */ - -static Visitor *test_init_internal(TestInputVisitorData *data, bool keyval, - QObject *obj) -{ - visitor_input_teardown(data, NULL); - - data->obj = obj; - - if (keyval) { - data->qiv = qobject_input_visitor_new_keyval(data->obj); - } else { - data->qiv = qobject_input_visitor_new(data->obj); - } - g_assert(data->qiv); - return data->qiv; -} - -static GCC_FMT_ATTR(3, 4) -Visitor *visitor_input_test_init_full(TestInputVisitorData *data, - bool keyval, - const char *json_string, ...) -{ - Visitor *v; - va_list ap; - - va_start(ap, json_string); - v = test_init_internal(data, keyval, - qobject_from_vjsonf_nofail(json_string, ap)); - va_end(ap); - return v; -} - -static GCC_FMT_ATTR(2, 3) -Visitor *visitor_input_test_init(TestInputVisitorData *data, - const char *json_string, ...) -{ - Visitor *v; - va_list ap; - - va_start(ap, json_string); - v = test_init_internal(data, false, - qobject_from_vjsonf_nofail(json_string, ap)); - va_end(ap); - return v; -} - -/* similar to visitor_input_test_init(), but does not expect a string - * literal/format json_string argument and so can be used for - * programatically generated strings (and we can't pass in programatically - * generated strings via %s format parameters since qobject_from_jsonv() - * will wrap those in double-quotes and treat the entire object as a - * string) - */ -static Visitor *visitor_input_test_init_raw(TestInputVisitorData *data, - const char *json_string) -{ - return test_init_internal(data, false, - qobject_from_json(json_string, &error_abort)); -} - -static void test_visitor_in_int(TestInputVisitorData *data, - const void *unused) -{ - int64_t res = 0; - double dbl; - int value = -42; - Visitor *v; - - v = visitor_input_test_init(data, "%d", value); - - visit_type_int(v, NULL, &res, &error_abort); - g_assert_cmpint(res, ==, value); - - visit_type_number(v, NULL, &dbl, &error_abort); - g_assert_cmpfloat(dbl, ==, -42.0); -} - -static void test_visitor_in_uint(TestInputVisitorData *data, - const void *unused) -{ - uint64_t res = 0; - int64_t i64; - double dbl; - int value = 42; - Visitor *v; - - v = visitor_input_test_init(data, "%d", value); - - visit_type_uint64(v, NULL, &res, &error_abort); - g_assert_cmpuint(res, ==, (uint64_t)value); - - visit_type_int(v, NULL, &i64, &error_abort); - g_assert_cmpint(i64, ==, value); - - visit_type_number(v, NULL, &dbl, &error_abort); - g_assert_cmpfloat(dbl, ==, value); - - /* BUG: value between INT64_MIN and -1 accepted modulo 2^64 */ - v = visitor_input_test_init(data, "%d", -value); - - visit_type_uint64(v, NULL, &res, &error_abort); - g_assert_cmpuint(res, ==, (uint64_t)-value); - - v = visitor_input_test_init(data, "18446744073709551574"); - - visit_type_uint64(v, NULL, &res, &error_abort); - g_assert_cmpuint(res, ==, 18446744073709551574U); - - visit_type_number(v, NULL, &dbl, &error_abort); - g_assert_cmpfloat(dbl, ==, 18446744073709552000.0); -} - -static void test_visitor_in_int_overflow(TestInputVisitorData *data, - const void *unused) -{ - int64_t res = 0; - Error *err = NULL; - Visitor *v; - - /* - * This will overflow a QNUM_I64, so should be deserialized into a - * QNUM_DOUBLE field instead, leading to an error if we pass it to - * visit_type_int(). Confirm this. - */ - v = visitor_input_test_init(data, "%f", DBL_MAX); - - visit_type_int(v, NULL, &res, &err); - error_free_or_abort(&err); -} - -static void test_visitor_in_int_keyval(TestInputVisitorData *data, - const void *unused) -{ - int64_t res = 0, value = -42; - Error *err = NULL; - Visitor *v; - - v = visitor_input_test_init_full(data, true, "%" PRId64, value); - visit_type_int(v, NULL, &res, &err); - error_free_or_abort(&err); -} - -static void test_visitor_in_int_str_keyval(TestInputVisitorData *data, - const void *unused) -{ - int64_t res = 0, value = -42; - Visitor *v; - - v = visitor_input_test_init_full(data, true, "\"-42\""); - - visit_type_int(v, NULL, &res, &error_abort); - g_assert_cmpint(res, ==, value); -} - -static void test_visitor_in_int_str_fail(TestInputVisitorData *data, - const void *unused) -{ - int64_t res = 0; - Visitor *v; - Error *err = NULL; - - v = visitor_input_test_init(data, "\"-42\""); - - visit_type_int(v, NULL, &res, &err); - error_free_or_abort(&err); -} - -static void test_visitor_in_bool(TestInputVisitorData *data, - const void *unused) -{ - bool res = false; - Visitor *v; - - v = visitor_input_test_init(data, "true"); - - visit_type_bool(v, NULL, &res, &error_abort); - g_assert_cmpint(res, ==, true); -} - -static void test_visitor_in_bool_keyval(TestInputVisitorData *data, - const void *unused) -{ - bool res = false; - Error *err = NULL; - Visitor *v; - - v = visitor_input_test_init_full(data, true, "true"); - - visit_type_bool(v, NULL, &res, &err); - error_free_or_abort(&err); -} - -static void test_visitor_in_bool_str_keyval(TestInputVisitorData *data, - const void *unused) -{ - bool res = false; - Visitor *v; - - v = visitor_input_test_init_full(data, true, "\"on\""); - - visit_type_bool(v, NULL, &res, &error_abort); - g_assert_cmpint(res, ==, true); -} - -static void test_visitor_in_bool_str_fail(TestInputVisitorData *data, - const void *unused) -{ - bool res = false; - Visitor *v; - Error *err = NULL; - - v = visitor_input_test_init(data, "\"true\""); - - visit_type_bool(v, NULL, &res, &err); - error_free_or_abort(&err); -} - -static void test_visitor_in_number(TestInputVisitorData *data, - const void *unused) -{ - double res = 0, value = 3.14; - Visitor *v; - - v = visitor_input_test_init(data, "%f", value); - - visit_type_number(v, NULL, &res, &error_abort); - g_assert_cmpfloat(res, ==, value); -} - -static void test_visitor_in_large_number(TestInputVisitorData *data, - const void *unused) -{ - Error *err = NULL; - double res = 0; - int64_t i64; - uint64_t u64; - Visitor *v; - - v = visitor_input_test_init(data, "-18446744073709551616"); /* -2^64 */ - - visit_type_number(v, NULL, &res, &error_abort); - g_assert_cmpfloat(res, ==, -18446744073709552e3); - - visit_type_int(v, NULL, &i64, &err); - error_free_or_abort(&err); - - visit_type_uint64(v, NULL, &u64, &err); - error_free_or_abort(&err); -} - -static void test_visitor_in_number_keyval(TestInputVisitorData *data, - const void *unused) -{ - double res = 0, value = 3.14; - Error *err = NULL; - Visitor *v; - - v = visitor_input_test_init_full(data, true, "%f", value); - - visit_type_number(v, NULL, &res, &err); - error_free_or_abort(&err); -} - -static void test_visitor_in_number_str_keyval(TestInputVisitorData *data, - const void *unused) -{ - double res = 0, value = 3.14; - Visitor *v; - Error *err = NULL; - - v = visitor_input_test_init_full(data, true, "\"3.14\""); - - visit_type_number(v, NULL, &res, &error_abort); - g_assert_cmpfloat(res, ==, value); - - v = visitor_input_test_init_full(data, true, "\"inf\""); - - visit_type_number(v, NULL, &res, &err); - error_free_or_abort(&err); -} - -static void test_visitor_in_number_str_fail(TestInputVisitorData *data, - const void *unused) -{ - double res = 0; - Visitor *v; - Error *err = NULL; - - v = visitor_input_test_init(data, "\"3.14\""); - - visit_type_number(v, NULL, &res, &err); - error_free_or_abort(&err); -} - -static void test_visitor_in_size_str_keyval(TestInputVisitorData *data, - const void *unused) -{ - uint64_t res, value = 500 * 1024 * 1024; - Visitor *v; - - v = visitor_input_test_init_full(data, true, "\"500M\""); - - visit_type_size(v, NULL, &res, &error_abort); - g_assert_cmpfloat(res, ==, value); -} - -static void test_visitor_in_size_str_fail(TestInputVisitorData *data, - const void *unused) -{ - uint64_t res = 0; - Visitor *v; - Error *err = NULL; - - v = visitor_input_test_init(data, "\"500M\""); - - visit_type_size(v, NULL, &res, &err); - error_free_or_abort(&err); -} - -static void test_visitor_in_string(TestInputVisitorData *data, - const void *unused) -{ - char *res = NULL, *value = (char *) "Q E M U"; - Visitor *v; - - v = visitor_input_test_init(data, "%s", value); - - visit_type_str(v, NULL, &res, &error_abort); - g_assert_cmpstr(res, ==, value); - - g_free(res); -} - -static void test_visitor_in_enum(TestInputVisitorData *data, - const void *unused) -{ - Visitor *v; - EnumOne i; - - for (i = 0; i < ENUM_ONE__MAX; i++) { - EnumOne res = -1; - - v = visitor_input_test_init(data, "%s", EnumOne_str(i)); - - visit_type_EnumOne(v, NULL, &res, &error_abort); - g_assert_cmpint(i, ==, res); - } -} - - -static void test_visitor_in_struct(TestInputVisitorData *data, - const void *unused) -{ - TestStruct *p = NULL; - Visitor *v; - - v = visitor_input_test_init(data, "{ 'integer': -42, 'boolean': true, 'string': 'foo' }"); - - visit_type_TestStruct(v, NULL, &p, &error_abort); - g_assert_cmpint(p->integer, ==, -42); - g_assert(p->boolean == true); - g_assert_cmpstr(p->string, ==, "foo"); - - g_free(p->string); - g_free(p); -} - -static void test_visitor_in_struct_nested(TestInputVisitorData *data, - const void *unused) -{ - g_autoptr(UserDefTwo) udp = NULL; - Visitor *v; - - v = visitor_input_test_init(data, "{ 'string0': 'string0', " - "'dict1': { 'string1': 'string1', " - "'dict2': { 'userdef': { 'integer': 42, " - "'string': 'string' }, 'string': 'string2'}}}"); - - visit_type_UserDefTwo(v, NULL, &udp, &error_abort); - - g_assert_cmpstr(udp->string0, ==, "string0"); - g_assert_cmpstr(udp->dict1->string1, ==, "string1"); - g_assert_cmpint(udp->dict1->dict2->userdef->integer, ==, 42); - g_assert_cmpstr(udp->dict1->dict2->userdef->string, ==, "string"); - g_assert_cmpstr(udp->dict1->dict2->string, ==, "string2"); - g_assert(udp->dict1->has_dict3 == false); -} - -static void test_visitor_in_list(TestInputVisitorData *data, - const void *unused) -{ - UserDefOneList *item, *head = NULL; - Visitor *v; - int i; - - v = visitor_input_test_init(data, "[ { 'string': 'string0', 'integer': 42 }, { 'string': 'string1', 'integer': 43 }, { 'string': 'string2', 'integer': 44 } ]"); - - visit_type_UserDefOneList(v, NULL, &head, &error_abort); - g_assert(head != NULL); - - for (i = 0, item = head; item; item = item->next, i++) { - char string[12]; - - snprintf(string, sizeof(string), "string%d", i); - g_assert_cmpstr(item->value->string, ==, string); - g_assert_cmpint(item->value->integer, ==, 42 + i); - } - - qapi_free_UserDefOneList(head); - head = NULL; - - /* An empty list is valid */ - v = visitor_input_test_init(data, "[]"); - visit_type_UserDefOneList(v, NULL, &head, &error_abort); - g_assert(!head); -} - -static void test_visitor_in_any(TestInputVisitorData *data, - const void *unused) -{ - QObject *res = NULL; - Visitor *v; - QNum *qnum; - QBool *qbool; - QString *qstring; - QDict *qdict; - QObject *qobj; - int64_t val; - - v = visitor_input_test_init(data, "-42"); - visit_type_any(v, NULL, &res, &error_abort); - qnum = qobject_to(QNum, res); - g_assert(qnum); - g_assert(qnum_get_try_int(qnum, &val)); - g_assert_cmpint(val, ==, -42); - qobject_unref(res); - - v = visitor_input_test_init(data, "{ 'integer': -42, 'boolean': true, 'string': 'foo' }"); - visit_type_any(v, NULL, &res, &error_abort); - qdict = qobject_to(QDict, res); - g_assert(qdict && qdict_size(qdict) == 3); - qobj = qdict_get(qdict, "integer"); - g_assert(qobj); - qnum = qobject_to(QNum, qobj); - g_assert(qnum); - g_assert(qnum_get_try_int(qnum, &val)); - g_assert_cmpint(val, ==, -42); - qobj = qdict_get(qdict, "boolean"); - g_assert(qobj); - qbool = qobject_to(QBool, qobj); - g_assert(qbool); - g_assert(qbool_get_bool(qbool) == true); - qobj = qdict_get(qdict, "string"); - g_assert(qobj); - qstring = qobject_to(QString, qobj); - g_assert(qstring); - g_assert_cmpstr(qstring_get_str(qstring), ==, "foo"); - qobject_unref(res); -} - -static void test_visitor_in_null(TestInputVisitorData *data, - const void *unused) -{ - Visitor *v; - Error *err = NULL; - QNull *null; - char *tmp; - - /* - * FIXME: Since QAPI doesn't know the 'null' type yet, we can't - * test visit_type_null() by reading into a QAPI struct then - * checking that it was populated correctly. The best we can do - * for now is ensure that we consumed null from the input, proven - * by the fact that we can't re-read the key; and that we detect - * when input is not null. - */ - - v = visitor_input_test_init_full(data, false, - "{ 'a': null, 'b': '' }"); - visit_start_struct(v, NULL, NULL, 0, &error_abort); - visit_type_null(v, "a", &null, &error_abort); - g_assert(qobject_type(QOBJECT(null)) == QTYPE_QNULL); - qobject_unref(null); - visit_type_null(v, "b", &null, &err); - error_free_or_abort(&err); - g_assert(!null); - visit_type_str(v, "c", &tmp, &err); - error_free_or_abort(&err); - g_assert(!tmp); - visit_check_struct(v, &error_abort); - visit_end_struct(v, NULL); -} - -static void test_visitor_in_union_flat(TestInputVisitorData *data, - const void *unused) -{ - Visitor *v; - g_autoptr(UserDefFlatUnion) tmp = NULL; - UserDefUnionBase *base; - - v = visitor_input_test_init(data, - "{ 'enum1': 'value1', " - "'integer': 41, " - "'string': 'str', " - "'boolean': true }"); - - visit_type_UserDefFlatUnion(v, NULL, &tmp, &error_abort); - g_assert_cmpint(tmp->enum1, ==, ENUM_ONE_VALUE1); - g_assert_cmpstr(tmp->string, ==, "str"); - g_assert_cmpint(tmp->integer, ==, 41); - g_assert_cmpint(tmp->u.value1.boolean, ==, true); - - base = qapi_UserDefFlatUnion_base(tmp); - g_assert(&base->enum1 == &tmp->enum1); -} - -static void test_visitor_in_alternate(TestInputVisitorData *data, - const void *unused) -{ - Visitor *v; - UserDefAlternate *tmp; - WrapAlternate *wrap; - - v = visitor_input_test_init(data, "42"); - visit_type_UserDefAlternate(v, NULL, &tmp, &error_abort); - g_assert_cmpint(tmp->type, ==, QTYPE_QNUM); - g_assert_cmpint(tmp->u.i, ==, 42); - qapi_free_UserDefAlternate(tmp); - - v = visitor_input_test_init(data, "'value1'"); - visit_type_UserDefAlternate(v, NULL, &tmp, &error_abort); - g_assert_cmpint(tmp->type, ==, QTYPE_QSTRING); - g_assert_cmpint(tmp->u.e, ==, ENUM_ONE_VALUE1); - qapi_free_UserDefAlternate(tmp); - - v = visitor_input_test_init(data, "null"); - visit_type_UserDefAlternate(v, NULL, &tmp, &error_abort); - g_assert_cmpint(tmp->type, ==, QTYPE_QNULL); - qapi_free_UserDefAlternate(tmp); - - v = visitor_input_test_init(data, "{'integer':1, 'string':'str', " - "'enum1':'value1', 'boolean':true}"); - visit_type_UserDefAlternate(v, NULL, &tmp, &error_abort); - g_assert_cmpint(tmp->type, ==, QTYPE_QDICT); - g_assert_cmpint(tmp->u.udfu.integer, ==, 1); - g_assert_cmpstr(tmp->u.udfu.string, ==, "str"); - g_assert_cmpint(tmp->u.udfu.enum1, ==, ENUM_ONE_VALUE1); - g_assert_cmpint(tmp->u.udfu.u.value1.boolean, ==, true); - g_assert_cmpint(tmp->u.udfu.u.value1.has_a_b, ==, false); - qapi_free_UserDefAlternate(tmp); - - v = visitor_input_test_init(data, "{ 'alt': 42 }"); - visit_type_WrapAlternate(v, NULL, &wrap, &error_abort); - g_assert_cmpint(wrap->alt->type, ==, QTYPE_QNUM); - g_assert_cmpint(wrap->alt->u.i, ==, 42); - qapi_free_WrapAlternate(wrap); - - v = visitor_input_test_init(data, "{ 'alt': 'value1' }"); - visit_type_WrapAlternate(v, NULL, &wrap, &error_abort); - g_assert_cmpint(wrap->alt->type, ==, QTYPE_QSTRING); - g_assert_cmpint(wrap->alt->u.e, ==, ENUM_ONE_VALUE1); - qapi_free_WrapAlternate(wrap); - - v = visitor_input_test_init(data, "{ 'alt': {'integer':1, 'string':'str', " - "'enum1':'value1', 'boolean':true} }"); - visit_type_WrapAlternate(v, NULL, &wrap, &error_abort); - g_assert_cmpint(wrap->alt->type, ==, QTYPE_QDICT); - g_assert_cmpint(wrap->alt->u.udfu.integer, ==, 1); - g_assert_cmpstr(wrap->alt->u.udfu.string, ==, "str"); - g_assert_cmpint(wrap->alt->u.udfu.enum1, ==, ENUM_ONE_VALUE1); - g_assert_cmpint(wrap->alt->u.udfu.u.value1.boolean, ==, true); - g_assert_cmpint(wrap->alt->u.udfu.u.value1.has_a_b, ==, false); - qapi_free_WrapAlternate(wrap); -} - -static void test_visitor_in_alternate_number(TestInputVisitorData *data, - const void *unused) -{ - Visitor *v; - Error *err = NULL; - AltEnumBool *aeb; - AltEnumNum *aen; - AltNumEnum *ans; - AltEnumInt *asi; - - /* Parsing an int */ - - v = visitor_input_test_init(data, "42"); - visit_type_AltEnumBool(v, NULL, &aeb, &err); - error_free_or_abort(&err); - qapi_free_AltEnumBool(aeb); - - v = visitor_input_test_init(data, "42"); - visit_type_AltEnumNum(v, NULL, &aen, &error_abort); - g_assert_cmpint(aen->type, ==, QTYPE_QNUM); - g_assert_cmpfloat(aen->u.n, ==, 42); - qapi_free_AltEnumNum(aen); - - v = visitor_input_test_init(data, "42"); - visit_type_AltNumEnum(v, NULL, &ans, &error_abort); - g_assert_cmpint(ans->type, ==, QTYPE_QNUM); - g_assert_cmpfloat(ans->u.n, ==, 42); - qapi_free_AltNumEnum(ans); - - v = visitor_input_test_init(data, "42"); - visit_type_AltEnumInt(v, NULL, &asi, &error_abort); - g_assert_cmpint(asi->type, ==, QTYPE_QNUM); - g_assert_cmpint(asi->u.i, ==, 42); - qapi_free_AltEnumInt(asi); - - /* Parsing a double */ - - v = visitor_input_test_init(data, "42.5"); - visit_type_AltEnumBool(v, NULL, &aeb, &err); - error_free_or_abort(&err); - qapi_free_AltEnumBool(aeb); - - v = visitor_input_test_init(data, "42.5"); - visit_type_AltEnumNum(v, NULL, &aen, &error_abort); - g_assert_cmpint(aen->type, ==, QTYPE_QNUM); - g_assert_cmpfloat(aen->u.n, ==, 42.5); - qapi_free_AltEnumNum(aen); - - v = visitor_input_test_init(data, "42.5"); - visit_type_AltNumEnum(v, NULL, &ans, &error_abort); - g_assert_cmpint(ans->type, ==, QTYPE_QNUM); - g_assert_cmpfloat(ans->u.n, ==, 42.5); - qapi_free_AltNumEnum(ans); - - v = visitor_input_test_init(data, "42.5"); - visit_type_AltEnumInt(v, NULL, &asi, &err); - error_free_or_abort(&err); - qapi_free_AltEnumInt(asi); -} - -static void test_list_union_integer_helper(TestInputVisitorData *data, - const void *unused, - UserDefListUnionKind kind) -{ - g_autoptr(UserDefListUnion) cvalue = NULL; - Visitor *v; - GString *gstr_list = g_string_new(""); - GString *gstr_union = g_string_new(""); - int i; - - for (i = 0; i < 32; i++) { - g_string_append_printf(gstr_list, "%d", i); - if (i != 31) { - g_string_append(gstr_list, ", "); - } - } - g_string_append_printf(gstr_union, "{ 'type': '%s', 'data': [ %s ] }", - UserDefListUnionKind_str(kind), - gstr_list->str); - v = visitor_input_test_init_raw(data, gstr_union->str); - - visit_type_UserDefListUnion(v, NULL, &cvalue, &error_abort); - g_assert(cvalue != NULL); - g_assert_cmpint(cvalue->type, ==, kind); - - switch (kind) { - case USER_DEF_LIST_UNION_KIND_INTEGER: { - intList *elem = NULL; - for (i = 0, elem = cvalue->u.integer.data; - elem; elem = elem->next, i++) { - g_assert_cmpint(elem->value, ==, i); - } - break; - } - case USER_DEF_LIST_UNION_KIND_S8: { - int8List *elem = NULL; - for (i = 0, elem = cvalue->u.s8.data; elem; elem = elem->next, i++) { - g_assert_cmpint(elem->value, ==, i); - } - break; - } - case USER_DEF_LIST_UNION_KIND_S16: { - int16List *elem = NULL; - for (i = 0, elem = cvalue->u.s16.data; elem; elem = elem->next, i++) { - g_assert_cmpint(elem->value, ==, i); - } - break; - } - case USER_DEF_LIST_UNION_KIND_S32: { - int32List *elem = NULL; - for (i = 0, elem = cvalue->u.s32.data; elem; elem = elem->next, i++) { - g_assert_cmpint(elem->value, ==, i); - } - break; - } - case USER_DEF_LIST_UNION_KIND_S64: { - int64List *elem = NULL; - for (i = 0, elem = cvalue->u.s64.data; elem; elem = elem->next, i++) { - g_assert_cmpint(elem->value, ==, i); - } - break; - } - case USER_DEF_LIST_UNION_KIND_U8: { - uint8List *elem = NULL; - for (i = 0, elem = cvalue->u.u8.data; elem; elem = elem->next, i++) { - g_assert_cmpint(elem->value, ==, i); - } - break; - } - case USER_DEF_LIST_UNION_KIND_U16: { - uint16List *elem = NULL; - for (i = 0, elem = cvalue->u.u16.data; elem; elem = elem->next, i++) { - g_assert_cmpint(elem->value, ==, i); - } - break; - } - case USER_DEF_LIST_UNION_KIND_U32: { - uint32List *elem = NULL; - for (i = 0, elem = cvalue->u.u32.data; elem; elem = elem->next, i++) { - g_assert_cmpint(elem->value, ==, i); - } - break; - } - case USER_DEF_LIST_UNION_KIND_U64: { - uint64List *elem = NULL; - for (i = 0, elem = cvalue->u.u64.data; elem; elem = elem->next, i++) { - g_assert_cmpint(elem->value, ==, i); - } - break; - } - default: - g_assert_not_reached(); - } - - g_string_free(gstr_union, true); - g_string_free(gstr_list, true); -} - -static void test_visitor_in_list_union_int(TestInputVisitorData *data, - const void *unused) -{ - test_list_union_integer_helper(data, unused, - USER_DEF_LIST_UNION_KIND_INTEGER); -} - -static void test_visitor_in_list_union_int8(TestInputVisitorData *data, - const void *unused) -{ - test_list_union_integer_helper(data, unused, - USER_DEF_LIST_UNION_KIND_S8); -} - -static void test_visitor_in_list_union_int16(TestInputVisitorData *data, - const void *unused) -{ - test_list_union_integer_helper(data, unused, - USER_DEF_LIST_UNION_KIND_S16); -} - -static void test_visitor_in_list_union_int32(TestInputVisitorData *data, - const void *unused) -{ - test_list_union_integer_helper(data, unused, - USER_DEF_LIST_UNION_KIND_S32); -} - -static void test_visitor_in_list_union_int64(TestInputVisitorData *data, - const void *unused) -{ - test_list_union_integer_helper(data, unused, - USER_DEF_LIST_UNION_KIND_S64); -} - -static void test_visitor_in_list_union_uint8(TestInputVisitorData *data, - const void *unused) -{ - test_list_union_integer_helper(data, unused, - USER_DEF_LIST_UNION_KIND_U8); -} - -static void test_visitor_in_list_union_uint16(TestInputVisitorData *data, - const void *unused) -{ - test_list_union_integer_helper(data, unused, - USER_DEF_LIST_UNION_KIND_U16); -} - -static void test_visitor_in_list_union_uint32(TestInputVisitorData *data, - const void *unused) -{ - test_list_union_integer_helper(data, unused, - USER_DEF_LIST_UNION_KIND_U32); -} - -static void test_visitor_in_list_union_uint64(TestInputVisitorData *data, - const void *unused) -{ - test_list_union_integer_helper(data, unused, - USER_DEF_LIST_UNION_KIND_U64); -} - -static void test_visitor_in_list_union_bool(TestInputVisitorData *data, - const void *unused) -{ - g_autoptr(UserDefListUnion) cvalue = NULL; - boolList *elem = NULL; - Visitor *v; - GString *gstr_list = g_string_new(""); - GString *gstr_union = g_string_new(""); - int i; - - for (i = 0; i < 32; i++) { - g_string_append_printf(gstr_list, "%s", - (i % 3 == 0) ? "true" : "false"); - if (i != 31) { - g_string_append(gstr_list, ", "); - } - } - g_string_append_printf(gstr_union, "{ 'type': 'boolean', 'data': [ %s ] }", - gstr_list->str); - v = visitor_input_test_init_raw(data, gstr_union->str); - - visit_type_UserDefListUnion(v, NULL, &cvalue, &error_abort); - g_assert(cvalue != NULL); - g_assert_cmpint(cvalue->type, ==, USER_DEF_LIST_UNION_KIND_BOOLEAN); - - for (i = 0, elem = cvalue->u.boolean.data; elem; elem = elem->next, i++) { - g_assert_cmpint(elem->value, ==, (i % 3 == 0) ? 1 : 0); - } - - g_string_free(gstr_union, true); - g_string_free(gstr_list, true); -} - -static void test_visitor_in_list_union_string(TestInputVisitorData *data, - const void *unused) -{ - g_autoptr(UserDefListUnion) cvalue = NULL; - strList *elem = NULL; - Visitor *v; - GString *gstr_list = g_string_new(""); - GString *gstr_union = g_string_new(""); - int i; - - for (i = 0; i < 32; i++) { - g_string_append_printf(gstr_list, "'%d'", i); - if (i != 31) { - g_string_append(gstr_list, ", "); - } - } - g_string_append_printf(gstr_union, "{ 'type': 'string', 'data': [ %s ] }", - gstr_list->str); - v = visitor_input_test_init_raw(data, gstr_union->str); - - visit_type_UserDefListUnion(v, NULL, &cvalue, &error_abort); - g_assert(cvalue != NULL); - g_assert_cmpint(cvalue->type, ==, USER_DEF_LIST_UNION_KIND_STRING); - - for (i = 0, elem = cvalue->u.string.data; elem; elem = elem->next, i++) { - gchar str[8]; - sprintf(str, "%d", i); - g_assert_cmpstr(elem->value, ==, str); - } - - g_string_free(gstr_union, true); - g_string_free(gstr_list, true); -} - -#define DOUBLE_STR_MAX 16 - -static void test_visitor_in_list_union_number(TestInputVisitorData *data, - const void *unused) -{ - g_autoptr(UserDefListUnion) cvalue = NULL; - numberList *elem = NULL; - Visitor *v; - GString *gstr_list = g_string_new(""); - GString *gstr_union = g_string_new(""); - int i; - - for (i = 0; i < 32; i++) { - g_string_append_printf(gstr_list, "%f", (double)i / 3); - if (i != 31) { - g_string_append(gstr_list, ", "); - } - } - g_string_append_printf(gstr_union, "{ 'type': 'number', 'data': [ %s ] }", - gstr_list->str); - v = visitor_input_test_init_raw(data, gstr_union->str); - - visit_type_UserDefListUnion(v, NULL, &cvalue, &error_abort); - g_assert(cvalue != NULL); - g_assert_cmpint(cvalue->type, ==, USER_DEF_LIST_UNION_KIND_NUMBER); - - for (i = 0, elem = cvalue->u.number.data; elem; elem = elem->next, i++) { - GString *double_expected = g_string_new(""); - GString *double_actual = g_string_new(""); - - g_string_printf(double_expected, "%.6f", (double)i / 3); - g_string_printf(double_actual, "%.6f", elem->value); - g_assert_cmpstr(double_expected->str, ==, double_actual->str); - - g_string_free(double_expected, true); - g_string_free(double_actual, true); - } - - g_string_free(gstr_union, true); - g_string_free(gstr_list, true); -} - -static void input_visitor_test_add(const char *testpath, - const void *user_data, - void (*test_func)(TestInputVisitorData *data, - const void *user_data)) -{ - g_test_add(testpath, TestInputVisitorData, user_data, NULL, test_func, - visitor_input_teardown); -} - -static void test_visitor_in_errors(TestInputVisitorData *data, - const void *unused) -{ - TestStruct *p = NULL; - Error *err = NULL; - Visitor *v; - strList *q = NULL; - UserDefTwo *r = NULL; - WrapAlternate *s = NULL; - - v = visitor_input_test_init(data, "{ 'integer': false, 'boolean': 'foo', " - "'string': -42 }"); - - visit_type_TestStruct(v, NULL, &p, &err); - error_free_or_abort(&err); - g_assert(!p); - - v = visitor_input_test_init(data, "[ '1', '2', false, '3' ]"); - visit_type_strList(v, NULL, &q, &err); - error_free_or_abort(&err); - assert(!q); - - v = visitor_input_test_init(data, "{ 'str':'hi' }"); - visit_type_UserDefTwo(v, NULL, &r, &err); - error_free_or_abort(&err); - assert(!r); - - v = visitor_input_test_init(data, "{ }"); - visit_type_WrapAlternate(v, NULL, &s, &err); - error_free_or_abort(&err); - assert(!s); -} - -static void test_visitor_in_wrong_type(TestInputVisitorData *data, - const void *unused) -{ - TestStruct *p = NULL; - Visitor *v; - strList *q = NULL; - int64_t i; - Error *err = NULL; - - /* Make sure arrays and structs cannot be confused */ - - v = visitor_input_test_init(data, "[]"); - visit_type_TestStruct(v, NULL, &p, &err); - error_free_or_abort(&err); - g_assert(!p); - - v = visitor_input_test_init(data, "{}"); - visit_type_strList(v, NULL, &q, &err); - error_free_or_abort(&err); - assert(!q); - - /* Make sure primitives and struct cannot be confused */ - - v = visitor_input_test_init(data, "1"); - visit_type_TestStruct(v, NULL, &p, &err); - error_free_or_abort(&err); - g_assert(!p); - - v = visitor_input_test_init(data, "{}"); - visit_type_int(v, NULL, &i, &err); - error_free_or_abort(&err); - - /* Make sure primitives and arrays cannot be confused */ - - v = visitor_input_test_init(data, "1"); - visit_type_strList(v, NULL, &q, &err); - error_free_or_abort(&err); - assert(!q); - - v = visitor_input_test_init(data, "[]"); - visit_type_int(v, NULL, &i, &err); - error_free_or_abort(&err); -} - -static void test_visitor_in_fail_struct(TestInputVisitorData *data, - const void *unused) -{ - TestStruct *p = NULL; - Error *err = NULL; - Visitor *v; - - v = visitor_input_test_init(data, "{ 'integer': -42, 'boolean': true, 'string': 'foo', 'extra': 42 }"); - - visit_type_TestStruct(v, NULL, &p, &err); - error_free_or_abort(&err); - g_assert(!p); -} - -static void test_visitor_in_fail_struct_nested(TestInputVisitorData *data, - const void *unused) -{ - UserDefTwo *udp = NULL; - Error *err = NULL; - Visitor *v; - - v = visitor_input_test_init(data, "{ 'string0': 'string0', 'dict1': { 'string1': 'string1', 'dict2': { 'userdef1': { 'integer': 42, 'string': 'string', 'extra': [42, 23, {'foo':'bar'}] }, 'string2': 'string2'}}}"); - - visit_type_UserDefTwo(v, NULL, &udp, &err); - error_free_or_abort(&err); - g_assert(!udp); -} - -static void test_visitor_in_fail_struct_in_list(TestInputVisitorData *data, - const void *unused) -{ - UserDefOneList *head = NULL; - Error *err = NULL; - Visitor *v; - - v = visitor_input_test_init(data, "[ { 'string': 'string0', 'integer': 42 }, { 'string': 'string1', 'integer': 43 }, { 'string': 'string2', 'integer': 44, 'extra': 'ggg' } ]"); - - visit_type_UserDefOneList(v, NULL, &head, &err); - error_free_or_abort(&err); - g_assert(!head); -} - -static void test_visitor_in_fail_struct_missing(TestInputVisitorData *data, - const void *unused) -{ - Error *err = NULL; - Visitor *v; - QObject *any; - QNull *null; - GenericAlternate *alt; - bool present; - int en; - int64_t i64; - uint32_t u32; - int8_t i8; - char *str; - double dbl; - - v = visitor_input_test_init(data, "{ 'sub': [ {} ] }"); - visit_start_struct(v, NULL, NULL, 0, &error_abort); - visit_start_struct(v, "struct", NULL, 0, &err); - error_free_or_abort(&err); - visit_start_list(v, "list", NULL, 0, &err); - error_free_or_abort(&err); - visit_start_alternate(v, "alternate", &alt, sizeof(*alt), &err); - error_free_or_abort(&err); - visit_optional(v, "optional", &present); - g_assert(!present); - visit_type_enum(v, "enum", &en, &EnumOne_lookup, &err); - error_free_or_abort(&err); - visit_type_int(v, "i64", &i64, &err); - error_free_or_abort(&err); - visit_type_uint32(v, "u32", &u32, &err); - error_free_or_abort(&err); - visit_type_int8(v, "i8", &i8, &err); - error_free_or_abort(&err); - visit_type_str(v, "i8", &str, &err); - error_free_or_abort(&err); - visit_type_number(v, "dbl", &dbl, &err); - error_free_or_abort(&err); - visit_type_any(v, "any", &any, &err); - error_free_or_abort(&err); - visit_type_null(v, "null", &null, &err); - error_free_or_abort(&err); - visit_start_list(v, "sub", NULL, 0, &error_abort); - visit_start_struct(v, NULL, NULL, 0, &error_abort); - visit_type_int(v, "i64", &i64, &err); - error_free_or_abort(&err); - visit_end_struct(v, NULL); - visit_end_list(v, NULL); - visit_end_struct(v, NULL); -} - -static void test_visitor_in_fail_list(TestInputVisitorData *data, - const void *unused) -{ - int64_t i64 = -1; - Error *err = NULL; - Visitor *v; - - /* Unvisited list tail */ - - v = visitor_input_test_init(data, "[ 1, 2, 3 ]"); - - visit_start_list(v, NULL, NULL, 0, &error_abort); - visit_type_int(v, NULL, &i64, &error_abort); - g_assert_cmpint(i64, ==, 1); - visit_type_int(v, NULL, &i64, &error_abort); - g_assert_cmpint(i64, ==, 2); - visit_check_list(v, &err); - error_free_or_abort(&err); - visit_end_list(v, NULL); - - /* Visit beyond end of list */ - v = visitor_input_test_init(data, "[]"); - - visit_start_list(v, NULL, NULL, 0, &error_abort); - visit_type_int(v, NULL, &i64, &err); - error_free_or_abort(&err); - visit_end_list(v, NULL); -} - -static void test_visitor_in_fail_list_nested(TestInputVisitorData *data, - const void *unused) -{ - int64_t i64 = -1; - Error *err = NULL; - Visitor *v; - - /* Unvisited nested list tail */ - - v = visitor_input_test_init(data, "[ 0, [ 1, 2, 3 ] ]"); - - visit_start_list(v, NULL, NULL, 0, &error_abort); - visit_type_int(v, NULL, &i64, &error_abort); - g_assert_cmpint(i64, ==, 0); - visit_start_list(v, NULL, NULL, 0, &error_abort); - visit_type_int(v, NULL, &i64, &error_abort); - g_assert_cmpint(i64, ==, 1); - visit_check_list(v, &err); - error_free_or_abort(&err); - visit_end_list(v, NULL); - visit_check_list(v, &error_abort); - visit_end_list(v, NULL); -} - -static void test_visitor_in_fail_union_list(TestInputVisitorData *data, - const void *unused) -{ - UserDefListUnion *tmp = NULL; - Error *err = NULL; - Visitor *v; - - v = visitor_input_test_init(data, - "{ 'type': 'integer', 'data' : [ 'string' ] }"); - - visit_type_UserDefListUnion(v, NULL, &tmp, &err); - error_free_or_abort(&err); - g_assert(!tmp); -} - -static void test_visitor_in_fail_union_flat(TestInputVisitorData *data, - const void *unused) -{ - UserDefFlatUnion *tmp = NULL; - Error *err = NULL; - Visitor *v; - - v = visitor_input_test_init(data, "{ 'string': 'c', 'integer': 41, 'boolean': true }"); - - visit_type_UserDefFlatUnion(v, NULL, &tmp, &err); - error_free_or_abort(&err); - g_assert(!tmp); -} - -static void test_visitor_in_fail_union_flat_no_discrim(TestInputVisitorData *data, - const void *unused) -{ - UserDefFlatUnion2 *tmp = NULL; - Error *err = NULL; - Visitor *v; - - /* test situation where discriminator field ('enum1' here) is missing */ - v = visitor_input_test_init(data, "{ 'integer': 42, 'string': 'c', 'string1': 'd', 'string2': 'e' }"); - - visit_type_UserDefFlatUnion2(v, NULL, &tmp, &err); - error_free_or_abort(&err); - g_assert(!tmp); -} - -static void test_visitor_in_fail_alternate(TestInputVisitorData *data, - const void *unused) -{ - UserDefAlternate *tmp; - Visitor *v; - Error *err = NULL; - - v = visitor_input_test_init(data, "3.14"); - - visit_type_UserDefAlternate(v, NULL, &tmp, &err); - error_free_or_abort(&err); - g_assert(!tmp); -} - -static void do_test_visitor_in_qmp_introspect(TestInputVisitorData *data, - const QLitObject *qlit) -{ - g_autoptr(SchemaInfoList) schema = NULL; - QObject *obj = qobject_from_qlit(qlit); - Visitor *v; - - v = qobject_input_visitor_new(obj); - - visit_type_SchemaInfoList(v, NULL, &schema, &error_abort); - g_assert(schema); - - qobject_unref(obj); - visit_free(v); -} - -static void test_visitor_in_qmp_introspect(TestInputVisitorData *data, - const void *unused) -{ - do_test_visitor_in_qmp_introspect(data, &test_qmp_schema_qlit); -} - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - - input_visitor_test_add("/visitor/input/int", - NULL, test_visitor_in_int); - input_visitor_test_add("/visitor/input/uint", - NULL, test_visitor_in_uint); - input_visitor_test_add("/visitor/input/int_overflow", - NULL, test_visitor_in_int_overflow); - input_visitor_test_add("/visitor/input/int_keyval", - NULL, test_visitor_in_int_keyval); - input_visitor_test_add("/visitor/input/int_str_keyval", - NULL, test_visitor_in_int_str_keyval); - input_visitor_test_add("/visitor/input/int_str_fail", - NULL, test_visitor_in_int_str_fail); - input_visitor_test_add("/visitor/input/bool", - NULL, test_visitor_in_bool); - input_visitor_test_add("/visitor/input/bool_keyval", - NULL, test_visitor_in_bool_keyval); - input_visitor_test_add("/visitor/input/bool_str_keyval", - NULL, test_visitor_in_bool_str_keyval); - input_visitor_test_add("/visitor/input/bool_str_fail", - NULL, test_visitor_in_bool_str_fail); - input_visitor_test_add("/visitor/input/number", - NULL, test_visitor_in_number); - input_visitor_test_add("/visitor/input/large_number", - NULL, test_visitor_in_large_number); - input_visitor_test_add("/visitor/input/number_keyval", - NULL, test_visitor_in_number_keyval); - input_visitor_test_add("/visitor/input/number_str_keyval", - NULL, test_visitor_in_number_str_keyval); - input_visitor_test_add("/visitor/input/number_str_fail", - NULL, test_visitor_in_number_str_fail); - input_visitor_test_add("/visitor/input/size_str_keyval", - NULL, test_visitor_in_size_str_keyval); - input_visitor_test_add("/visitor/input/size_str_fail", - NULL, test_visitor_in_size_str_fail); - input_visitor_test_add("/visitor/input/string", - NULL, test_visitor_in_string); - input_visitor_test_add("/visitor/input/enum", - NULL, test_visitor_in_enum); - input_visitor_test_add("/visitor/input/struct", - NULL, test_visitor_in_struct); - input_visitor_test_add("/visitor/input/struct-nested", - NULL, test_visitor_in_struct_nested); - input_visitor_test_add("/visitor/input/list", - NULL, test_visitor_in_list); - input_visitor_test_add("/visitor/input/any", - NULL, test_visitor_in_any); - input_visitor_test_add("/visitor/input/null", - NULL, test_visitor_in_null); - input_visitor_test_add("/visitor/input/union-flat", - NULL, test_visitor_in_union_flat); - input_visitor_test_add("/visitor/input/alternate", - NULL, test_visitor_in_alternate); - input_visitor_test_add("/visitor/input/errors", - NULL, test_visitor_in_errors); - input_visitor_test_add("/visitor/input/wrong-type", - NULL, test_visitor_in_wrong_type); - input_visitor_test_add("/visitor/input/alternate-number", - NULL, test_visitor_in_alternate_number); - input_visitor_test_add("/visitor/input/list_union/int", - NULL, test_visitor_in_list_union_int); - input_visitor_test_add("/visitor/input/list_union/int8", - NULL, test_visitor_in_list_union_int8); - input_visitor_test_add("/visitor/input/list_union/int16", - NULL, test_visitor_in_list_union_int16); - input_visitor_test_add("/visitor/input/list_union/int32", - NULL, test_visitor_in_list_union_int32); - input_visitor_test_add("/visitor/input/list_union/int64", - NULL, test_visitor_in_list_union_int64); - input_visitor_test_add("/visitor/input/list_union/uint8", - NULL, test_visitor_in_list_union_uint8); - input_visitor_test_add("/visitor/input/list_union/uint16", - NULL, test_visitor_in_list_union_uint16); - input_visitor_test_add("/visitor/input/list_union/uint32", - NULL, test_visitor_in_list_union_uint32); - input_visitor_test_add("/visitor/input/list_union/uint64", - NULL, test_visitor_in_list_union_uint64); - input_visitor_test_add("/visitor/input/list_union/bool", - NULL, test_visitor_in_list_union_bool); - input_visitor_test_add("/visitor/input/list_union/str", - NULL, test_visitor_in_list_union_string); - input_visitor_test_add("/visitor/input/list_union/number", - NULL, test_visitor_in_list_union_number); - input_visitor_test_add("/visitor/input/fail/struct", - NULL, test_visitor_in_fail_struct); - input_visitor_test_add("/visitor/input/fail/struct-nested", - NULL, test_visitor_in_fail_struct_nested); - input_visitor_test_add("/visitor/input/fail/struct-in-list", - NULL, test_visitor_in_fail_struct_in_list); - input_visitor_test_add("/visitor/input/fail/struct-missing", - NULL, test_visitor_in_fail_struct_missing); - input_visitor_test_add("/visitor/input/fail/list", - NULL, test_visitor_in_fail_list); - input_visitor_test_add("/visitor/input/fail/list-nested", - NULL, test_visitor_in_fail_list_nested); - input_visitor_test_add("/visitor/input/fail/union-flat", - NULL, test_visitor_in_fail_union_flat); - input_visitor_test_add("/visitor/input/fail/union-flat-no-discriminator", - NULL, test_visitor_in_fail_union_flat_no_discrim); - input_visitor_test_add("/visitor/input/fail/alternate", - NULL, test_visitor_in_fail_alternate); - input_visitor_test_add("/visitor/input/fail/union-list", - NULL, test_visitor_in_fail_union_list); - input_visitor_test_add("/visitor/input/qapi-introspect", - NULL, test_visitor_in_qmp_introspect); - - g_test_run(); - - return 0; -} diff --git a/tests/test-qobject-output-visitor.c b/tests/test-qobject-output-visitor.c deleted file mode 100644 index 9dc1e075e7..0000000000 --- a/tests/test-qobject-output-visitor.c +++ /dev/null @@ -1,807 +0,0 @@ -/* - * QObject Output Visitor unit-tests. - * - * Copyright (C) 2011-2016 Red Hat Inc. - * - * Authors: - * Luiz Capitulino - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" - -#include "qemu-common.h" -#include "qapi/error.h" -#include "qapi/qobject-output-visitor.h" -#include "test-qapi-visit.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qnull.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qstring.h" - -typedef struct TestOutputVisitorData { - Visitor *ov; - QObject *obj; -} TestOutputVisitorData; - -static void visitor_output_setup(TestOutputVisitorData *data, - const void *unused) -{ - data->ov = qobject_output_visitor_new(&data->obj); - g_assert(data->ov); -} - -static void visitor_output_teardown(TestOutputVisitorData *data, - const void *unused) -{ - visit_free(data->ov); - data->ov = NULL; - qobject_unref(data->obj); - data->obj = NULL; -} - -static QObject *visitor_get(TestOutputVisitorData *data) -{ - visit_complete(data->ov, &data->obj); - g_assert(data->obj); - return data->obj; -} - -static void visitor_reset(TestOutputVisitorData *data) -{ - visitor_output_teardown(data, NULL); - visitor_output_setup(data, NULL); -} - -static void test_visitor_out_int(TestOutputVisitorData *data, - const void *unused) -{ - int64_t value = -42; - int64_t val; - QNum *qnum; - - visit_type_int(data->ov, NULL, &value, &error_abort); - - qnum = qobject_to(QNum, visitor_get(data)); - g_assert(qnum); - g_assert(qnum_get_try_int(qnum, &val)); - g_assert_cmpint(val, ==, value); -} - -static void test_visitor_out_bool(TestOutputVisitorData *data, - const void *unused) -{ - bool value = true; - QBool *qbool; - - visit_type_bool(data->ov, NULL, &value, &error_abort); - - qbool = qobject_to(QBool, visitor_get(data)); - g_assert(qbool); - g_assert(qbool_get_bool(qbool) == value); -} - -static void test_visitor_out_number(TestOutputVisitorData *data, - const void *unused) -{ - double value = 3.14; - QNum *qnum; - - visit_type_number(data->ov, NULL, &value, &error_abort); - - qnum = qobject_to(QNum, visitor_get(data)); - g_assert(qnum); - g_assert(qnum_get_double(qnum) == value); -} - -static void test_visitor_out_string(TestOutputVisitorData *data, - const void *unused) -{ - char *string = (char *) "Q E M U"; - QString *qstr; - - visit_type_str(data->ov, NULL, &string, &error_abort); - - qstr = qobject_to(QString, visitor_get(data)); - g_assert(qstr); - g_assert_cmpstr(qstring_get_str(qstr), ==, string); -} - -static void test_visitor_out_no_string(TestOutputVisitorData *data, - const void *unused) -{ - char *string = NULL; - QString *qstr; - - /* A null string should return "" */ - visit_type_str(data->ov, NULL, &string, &error_abort); - - qstr = qobject_to(QString, visitor_get(data)); - g_assert(qstr); - g_assert_cmpstr(qstring_get_str(qstr), ==, ""); -} - -static void test_visitor_out_enum(TestOutputVisitorData *data, - const void *unused) -{ - EnumOne i; - QString *qstr; - - for (i = 0; i < ENUM_ONE__MAX; i++) { - visit_type_EnumOne(data->ov, "unused", &i, &error_abort); - - qstr = qobject_to(QString, visitor_get(data)); - g_assert(qstr); - g_assert_cmpstr(qstring_get_str(qstr), ==, EnumOne_str(i)); - visitor_reset(data); - } -} - -static void test_visitor_out_struct(TestOutputVisitorData *data, - const void *unused) -{ - TestStruct test_struct = { .integer = 42, - .boolean = false, - .string = (char *) "foo"}; - TestStruct *p = &test_struct; - QDict *qdict; - - visit_type_TestStruct(data->ov, NULL, &p, &error_abort); - - qdict = qobject_to(QDict, visitor_get(data)); - g_assert(qdict); - g_assert_cmpint(qdict_size(qdict), ==, 3); - g_assert_cmpint(qdict_get_int(qdict, "integer"), ==, 42); - g_assert_cmpint(qdict_get_bool(qdict, "boolean"), ==, false); - g_assert_cmpstr(qdict_get_str(qdict, "string"), ==, "foo"); -} - -static void test_visitor_out_struct_nested(TestOutputVisitorData *data, - const void *unused) -{ - int64_t value = 42; - UserDefTwo *ud2; - QDict *qdict, *dict1, *dict2, *dict3, *userdef; - const char *string = "user def string"; - const char *strings[] = { "forty two", "forty three", "forty four", - "forty five" }; - - ud2 = g_malloc0(sizeof(*ud2)); - ud2->string0 = g_strdup(strings[0]); - - ud2->dict1 = g_malloc0(sizeof(*ud2->dict1)); - ud2->dict1->string1 = g_strdup(strings[1]); - - ud2->dict1->dict2 = g_malloc0(sizeof(*ud2->dict1->dict2)); - ud2->dict1->dict2->userdef = g_new0(UserDefOne, 1); - ud2->dict1->dict2->userdef->string = g_strdup(string); - ud2->dict1->dict2->userdef->integer = value; - ud2->dict1->dict2->string = g_strdup(strings[2]); - - ud2->dict1->dict3 = g_malloc0(sizeof(*ud2->dict1->dict3)); - ud2->dict1->has_dict3 = true; - ud2->dict1->dict3->userdef = g_new0(UserDefOne, 1); - ud2->dict1->dict3->userdef->string = g_strdup(string); - ud2->dict1->dict3->userdef->integer = value; - ud2->dict1->dict3->string = g_strdup(strings[3]); - - visit_type_UserDefTwo(data->ov, "unused", &ud2, &error_abort); - - qdict = qobject_to(QDict, visitor_get(data)); - g_assert(qdict); - g_assert_cmpint(qdict_size(qdict), ==, 2); - g_assert_cmpstr(qdict_get_str(qdict, "string0"), ==, strings[0]); - - dict1 = qdict_get_qdict(qdict, "dict1"); - g_assert_cmpint(qdict_size(dict1), ==, 3); - g_assert_cmpstr(qdict_get_str(dict1, "string1"), ==, strings[1]); - - dict2 = qdict_get_qdict(dict1, "dict2"); - g_assert_cmpint(qdict_size(dict2), ==, 2); - g_assert_cmpstr(qdict_get_str(dict2, "string"), ==, strings[2]); - userdef = qdict_get_qdict(dict2, "userdef"); - g_assert_cmpint(qdict_size(userdef), ==, 2); - g_assert_cmpint(qdict_get_int(userdef, "integer"), ==, value); - g_assert_cmpstr(qdict_get_str(userdef, "string"), ==, string); - - dict3 = qdict_get_qdict(dict1, "dict3"); - g_assert_cmpint(qdict_size(dict3), ==, 2); - g_assert_cmpstr(qdict_get_str(dict3, "string"), ==, strings[3]); - userdef = qdict_get_qdict(dict3, "userdef"); - g_assert_cmpint(qdict_size(userdef), ==, 2); - g_assert_cmpint(qdict_get_int(userdef, "integer"), ==, value); - g_assert_cmpstr(qdict_get_str(userdef, "string"), ==, string); - - qapi_free_UserDefTwo(ud2); -} - -static void test_visitor_out_list(TestOutputVisitorData *data, - const void *unused) -{ - const char *value_str = "list value"; - TestStruct *value; - TestStructList *head = NULL; - const int max_items = 10; - bool value_bool = true; - int value_int = 10; - QListEntry *entry; - QList *qlist; - int i; - - /* Build the list in reverse order... */ - for (i = 0; i < max_items; i++) { - value = g_malloc0(sizeof(*value)); - value->integer = value_int + (max_items - i - 1); - value->boolean = value_bool; - value->string = g_strdup(value_str); - - QAPI_LIST_PREPEND(head, value); - } - - visit_type_TestStructList(data->ov, NULL, &head, &error_abort); - - qlist = qobject_to(QList, visitor_get(data)); - g_assert(qlist); - g_assert(!qlist_empty(qlist)); - - /* ...and ensure that the visitor sees it in order */ - i = 0; - QLIST_FOREACH_ENTRY(qlist, entry) { - QDict *qdict; - - qdict = qobject_to(QDict, entry->value); - g_assert(qdict); - g_assert_cmpint(qdict_size(qdict), ==, 3); - g_assert_cmpint(qdict_get_int(qdict, "integer"), ==, value_int + i); - g_assert_cmpint(qdict_get_bool(qdict, "boolean"), ==, value_bool); - g_assert_cmpstr(qdict_get_str(qdict, "string"), ==, value_str); - i++; - } - g_assert_cmpint(i, ==, max_items); - - qapi_free_TestStructList(head); -} - -static void test_visitor_out_list_qapi_free(TestOutputVisitorData *data, - const void *unused) -{ - UserDefTwo *value; - UserDefTwoList *head = NULL; - const char string[] = "foo bar"; - int i, max_count = 1024; - - for (i = 0; i < max_count; i++) { - value = g_malloc0(sizeof(*value)); - - value->string0 = g_strdup(string); - value->dict1 = g_new0(UserDefTwoDict, 1); - value->dict1->string1 = g_strdup(string); - value->dict1->dict2 = g_new0(UserDefTwoDictDict, 1); - value->dict1->dict2->userdef = g_new0(UserDefOne, 1); - value->dict1->dict2->userdef->string = g_strdup(string); - value->dict1->dict2->userdef->integer = 42; - value->dict1->dict2->string = g_strdup(string); - value->dict1->has_dict3 = false; - - QAPI_LIST_PREPEND(head, value); - } - - qapi_free_UserDefTwoList(head); -} - -static void test_visitor_out_any(TestOutputVisitorData *data, - const void *unused) -{ - QObject *qobj; - QNum *qnum; - QBool *qbool; - QString *qstring; - QDict *qdict; - int64_t val; - - qobj = QOBJECT(qnum_from_int(-42)); - visit_type_any(data->ov, NULL, &qobj, &error_abort); - qnum = qobject_to(QNum, visitor_get(data)); - g_assert(qnum); - g_assert(qnum_get_try_int(qnum, &val)); - g_assert_cmpint(val, ==, -42); - qobject_unref(qobj); - - visitor_reset(data); - qdict = qdict_new(); - qdict_put_int(qdict, "integer", -42); - qdict_put_bool(qdict, "boolean", true); - qdict_put_str(qdict, "string", "foo"); - qobj = QOBJECT(qdict); - visit_type_any(data->ov, NULL, &qobj, &error_abort); - qobject_unref(qobj); - qdict = qobject_to(QDict, visitor_get(data)); - g_assert(qdict); - qnum = qobject_to(QNum, qdict_get(qdict, "integer")); - g_assert(qnum); - g_assert(qnum_get_try_int(qnum, &val)); - g_assert_cmpint(val, ==, -42); - qbool = qobject_to(QBool, qdict_get(qdict, "boolean")); - g_assert(qbool); - g_assert(qbool_get_bool(qbool) == true); - qstring = qobject_to(QString, qdict_get(qdict, "string")); - g_assert(qstring); - g_assert_cmpstr(qstring_get_str(qstring), ==, "foo"); -} - -static void test_visitor_out_union_flat(TestOutputVisitorData *data, - const void *unused) -{ - QDict *qdict; - - UserDefFlatUnion *tmp = g_malloc0(sizeof(UserDefFlatUnion)); - tmp->enum1 = ENUM_ONE_VALUE1; - tmp->string = g_strdup("str"); - tmp->integer = 41; - tmp->u.value1.boolean = true; - - visit_type_UserDefFlatUnion(data->ov, NULL, &tmp, &error_abort); - qdict = qobject_to(QDict, visitor_get(data)); - g_assert(qdict); - g_assert_cmpstr(qdict_get_str(qdict, "enum1"), ==, "value1"); - g_assert_cmpstr(qdict_get_str(qdict, "string"), ==, "str"); - g_assert_cmpint(qdict_get_int(qdict, "integer"), ==, 41); - g_assert_cmpint(qdict_get_bool(qdict, "boolean"), ==, true); - - qapi_free_UserDefFlatUnion(tmp); -} - -static void test_visitor_out_alternate(TestOutputVisitorData *data, - const void *unused) -{ - UserDefAlternate *tmp; - QNum *qnum; - QString *qstr; - QDict *qdict; - int64_t val; - - tmp = g_new0(UserDefAlternate, 1); - tmp->type = QTYPE_QNUM; - tmp->u.i = 42; - - visit_type_UserDefAlternate(data->ov, NULL, &tmp, &error_abort); - qnum = qobject_to(QNum, visitor_get(data)); - g_assert(qnum); - g_assert(qnum_get_try_int(qnum, &val)); - g_assert_cmpint(val, ==, 42); - - qapi_free_UserDefAlternate(tmp); - - visitor_reset(data); - tmp = g_new0(UserDefAlternate, 1); - tmp->type = QTYPE_QSTRING; - tmp->u.e = ENUM_ONE_VALUE1; - - visit_type_UserDefAlternate(data->ov, NULL, &tmp, &error_abort); - qstr = qobject_to(QString, visitor_get(data)); - g_assert(qstr); - g_assert_cmpstr(qstring_get_str(qstr), ==, "value1"); - - qapi_free_UserDefAlternate(tmp); - - visitor_reset(data); - tmp = g_new0(UserDefAlternate, 1); - tmp->type = QTYPE_QNULL; - tmp->u.n = qnull(); - - visit_type_UserDefAlternate(data->ov, NULL, &tmp, &error_abort); - g_assert_cmpint(qobject_type(visitor_get(data)), ==, QTYPE_QNULL); - - qapi_free_UserDefAlternate(tmp); - - visitor_reset(data); - tmp = g_new0(UserDefAlternate, 1); - tmp->type = QTYPE_QDICT; - tmp->u.udfu.integer = 1; - tmp->u.udfu.string = g_strdup("str"); - tmp->u.udfu.enum1 = ENUM_ONE_VALUE1; - tmp->u.udfu.u.value1.boolean = true; - - visit_type_UserDefAlternate(data->ov, NULL, &tmp, &error_abort); - qdict = qobject_to(QDict, visitor_get(data)); - g_assert(qdict); - g_assert_cmpint(qdict_size(qdict), ==, 4); - g_assert_cmpint(qdict_get_int(qdict, "integer"), ==, 1); - g_assert_cmpstr(qdict_get_str(qdict, "string"), ==, "str"); - g_assert_cmpstr(qdict_get_str(qdict, "enum1"), ==, "value1"); - g_assert_cmpint(qdict_get_bool(qdict, "boolean"), ==, true); - - qapi_free_UserDefAlternate(tmp); -} - -static void test_visitor_out_null(TestOutputVisitorData *data, - const void *unused) -{ - QNull *null = NULL; - QDict *qdict; - QObject *nil; - - visit_start_struct(data->ov, NULL, NULL, 0, &error_abort); - visit_type_null(data->ov, "a", &null, &error_abort); - visit_check_struct(data->ov, &error_abort); - visit_end_struct(data->ov, NULL); - qdict = qobject_to(QDict, visitor_get(data)); - g_assert(qdict); - g_assert_cmpint(qdict_size(qdict), ==, 1); - nil = qdict_get(qdict, "a"); - g_assert(nil); - g_assert(qobject_type(nil) == QTYPE_QNULL); -} - -static void init_list_union(UserDefListUnion *cvalue) -{ - int i; - switch (cvalue->type) { - case USER_DEF_LIST_UNION_KIND_INTEGER: { - intList **tail = &cvalue->u.integer.data; - for (i = 0; i < 32; i++) { - QAPI_LIST_APPEND(tail, i); - } - break; - } - case USER_DEF_LIST_UNION_KIND_S8: { - int8List **tail = &cvalue->u.s8.data; - for (i = 0; i < 32; i++) { - QAPI_LIST_APPEND(tail, i); - } - break; - } - case USER_DEF_LIST_UNION_KIND_S16: { - int16List **tail = &cvalue->u.s16.data; - for (i = 0; i < 32; i++) { - QAPI_LIST_APPEND(tail, i); - } - break; - } - case USER_DEF_LIST_UNION_KIND_S32: { - int32List **tail = &cvalue->u.s32.data; - for (i = 0; i < 32; i++) { - QAPI_LIST_APPEND(tail, i); - } - break; - } - case USER_DEF_LIST_UNION_KIND_S64: { - int64List **tail = &cvalue->u.s64.data; - for (i = 0; i < 32; i++) { - QAPI_LIST_APPEND(tail, i); - } - break; - } - case USER_DEF_LIST_UNION_KIND_U8: { - uint8List **tail = &cvalue->u.u8.data; - for (i = 0; i < 32; i++) { - QAPI_LIST_APPEND(tail, i); - } - break; - } - case USER_DEF_LIST_UNION_KIND_U16: { - uint16List **tail = &cvalue->u.u16.data; - for (i = 0; i < 32; i++) { - QAPI_LIST_APPEND(tail, i); - } - break; - } - case USER_DEF_LIST_UNION_KIND_U32: { - uint32List **tail = &cvalue->u.u32.data; - for (i = 0; i < 32; i++) { - QAPI_LIST_APPEND(tail, i); - } - break; - } - case USER_DEF_LIST_UNION_KIND_U64: { - uint64List **tail = &cvalue->u.u64.data; - for (i = 0; i < 32; i++) { - QAPI_LIST_APPEND(tail, i); - } - break; - } - case USER_DEF_LIST_UNION_KIND_BOOLEAN: { - boolList **tail = &cvalue->u.boolean.data; - for (i = 0; i < 32; i++) { - QAPI_LIST_APPEND(tail, QEMU_IS_ALIGNED(i, 3)); - } - break; - } - case USER_DEF_LIST_UNION_KIND_STRING: { - strList **tail = &cvalue->u.string.data; - for (i = 0; i < 32; i++) { - QAPI_LIST_APPEND(tail, g_strdup_printf("%d", i)); - } - break; - } - case USER_DEF_LIST_UNION_KIND_NUMBER: { - numberList **tail = &cvalue->u.number.data; - for (i = 0; i < 32; i++) { - QAPI_LIST_APPEND(tail, (double)i / 3); - } - break; - } - default: - g_assert_not_reached(); - } -} - -static void check_list_union(QObject *qobj, - UserDefListUnionKind kind) -{ - QDict *qdict; - QList *qlist; - int i; - - qdict = qobject_to(QDict, qobj); - g_assert(qdict); - g_assert(qdict_haskey(qdict, "data")); - qlist = qlist_copy(qobject_to(QList, qdict_get(qdict, "data"))); - - switch (kind) { - case USER_DEF_LIST_UNION_KIND_U8: - case USER_DEF_LIST_UNION_KIND_U16: - case USER_DEF_LIST_UNION_KIND_U32: - case USER_DEF_LIST_UNION_KIND_U64: - for (i = 0; i < 32; i++) { - QObject *tmp; - QNum *qvalue; - uint64_t val; - - tmp = qlist_peek(qlist); - g_assert(tmp); - qvalue = qobject_to(QNum, tmp); - g_assert(qnum_get_try_uint(qvalue, &val)); - g_assert_cmpint(val, ==, i); - qobject_unref(qlist_pop(qlist)); - } - break; - - case USER_DEF_LIST_UNION_KIND_S8: - case USER_DEF_LIST_UNION_KIND_S16: - case USER_DEF_LIST_UNION_KIND_S32: - case USER_DEF_LIST_UNION_KIND_S64: - /* - * All integer elements in JSON arrays get stored into QNums - * when we convert to QObjects, so we can check them all in - * the same fashion, so simply fall through here. - */ - case USER_DEF_LIST_UNION_KIND_INTEGER: - for (i = 0; i < 32; i++) { - QObject *tmp; - QNum *qvalue; - int64_t val; - - tmp = qlist_peek(qlist); - g_assert(tmp); - qvalue = qobject_to(QNum, tmp); - g_assert(qnum_get_try_int(qvalue, &val)); - g_assert_cmpint(val, ==, i); - qobject_unref(qlist_pop(qlist)); - } - break; - case USER_DEF_LIST_UNION_KIND_BOOLEAN: - for (i = 0; i < 32; i++) { - QObject *tmp; - QBool *qvalue; - tmp = qlist_peek(qlist); - g_assert(tmp); - qvalue = qobject_to(QBool, tmp); - g_assert_cmpint(qbool_get_bool(qvalue), ==, i % 3 == 0); - qobject_unref(qlist_pop(qlist)); - } - break; - case USER_DEF_LIST_UNION_KIND_STRING: - for (i = 0; i < 32; i++) { - QObject *tmp; - QString *qvalue; - gchar str[8]; - tmp = qlist_peek(qlist); - g_assert(tmp); - qvalue = qobject_to(QString, tmp); - sprintf(str, "%d", i); - g_assert_cmpstr(qstring_get_str(qvalue), ==, str); - qobject_unref(qlist_pop(qlist)); - } - break; - case USER_DEF_LIST_UNION_KIND_NUMBER: - for (i = 0; i < 32; i++) { - QObject *tmp; - QNum *qvalue; - GString *double_expected = g_string_new(""); - GString *double_actual = g_string_new(""); - - tmp = qlist_peek(qlist); - g_assert(tmp); - qvalue = qobject_to(QNum, tmp); - g_string_printf(double_expected, "%.6f", (double)i / 3); - g_string_printf(double_actual, "%.6f", qnum_get_double(qvalue)); - g_assert_cmpstr(double_actual->str, ==, double_expected->str); - - qobject_unref(qlist_pop(qlist)); - g_string_free(double_expected, true); - g_string_free(double_actual, true); - } - break; - default: - g_assert_not_reached(); - } - qobject_unref(qlist); -} - -static void test_list_union(TestOutputVisitorData *data, - const void *unused, - UserDefListUnionKind kind) -{ - UserDefListUnion *cvalue = g_new0(UserDefListUnion, 1); - QObject *obj; - - cvalue->type = kind; - init_list_union(cvalue); - - visit_type_UserDefListUnion(data->ov, NULL, &cvalue, &error_abort); - - obj = visitor_get(data); - check_list_union(obj, cvalue->type); - qapi_free_UserDefListUnion(cvalue); -} - -static void test_visitor_out_list_union_int(TestOutputVisitorData *data, - const void *unused) -{ - test_list_union(data, unused, USER_DEF_LIST_UNION_KIND_INTEGER); -} - -static void test_visitor_out_list_union_int8(TestOutputVisitorData *data, - const void *unused) -{ - test_list_union(data, unused, USER_DEF_LIST_UNION_KIND_S8); -} - -static void test_visitor_out_list_union_int16(TestOutputVisitorData *data, - const void *unused) -{ - test_list_union(data, unused, USER_DEF_LIST_UNION_KIND_S16); -} - -static void test_visitor_out_list_union_int32(TestOutputVisitorData *data, - const void *unused) -{ - test_list_union(data, unused, USER_DEF_LIST_UNION_KIND_S32); -} - -static void test_visitor_out_list_union_int64(TestOutputVisitorData *data, - const void *unused) -{ - test_list_union(data, unused, USER_DEF_LIST_UNION_KIND_S64); -} - -static void test_visitor_out_list_union_uint8(TestOutputVisitorData *data, - const void *unused) -{ - test_list_union(data, unused, USER_DEF_LIST_UNION_KIND_U8); -} - -static void test_visitor_out_list_union_uint16(TestOutputVisitorData *data, - const void *unused) -{ - test_list_union(data, unused, USER_DEF_LIST_UNION_KIND_U16); -} - -static void test_visitor_out_list_union_uint32(TestOutputVisitorData *data, - const void *unused) -{ - test_list_union(data, unused, USER_DEF_LIST_UNION_KIND_U32); -} - -static void test_visitor_out_list_union_uint64(TestOutputVisitorData *data, - const void *unused) -{ - test_list_union(data, unused, USER_DEF_LIST_UNION_KIND_U64); -} - -static void test_visitor_out_list_union_bool(TestOutputVisitorData *data, - const void *unused) -{ - test_list_union(data, unused, USER_DEF_LIST_UNION_KIND_BOOLEAN); -} - -static void test_visitor_out_list_union_str(TestOutputVisitorData *data, - const void *unused) -{ - test_list_union(data, unused, USER_DEF_LIST_UNION_KIND_STRING); -} - -static void test_visitor_out_list_union_number(TestOutputVisitorData *data, - const void *unused) -{ - test_list_union(data, unused, USER_DEF_LIST_UNION_KIND_NUMBER); -} - -static void output_visitor_test_add(const char *testpath, - TestOutputVisitorData *data, - void (*test_func)(TestOutputVisitorData *data, const void *user_data)) -{ - g_test_add(testpath, TestOutputVisitorData, data, visitor_output_setup, - test_func, visitor_output_teardown); -} - -int main(int argc, char **argv) -{ - TestOutputVisitorData out_visitor_data; - - g_test_init(&argc, &argv, NULL); - - output_visitor_test_add("/visitor/output/int", - &out_visitor_data, test_visitor_out_int); - output_visitor_test_add("/visitor/output/bool", - &out_visitor_data, test_visitor_out_bool); - output_visitor_test_add("/visitor/output/number", - &out_visitor_data, test_visitor_out_number); - output_visitor_test_add("/visitor/output/string", - &out_visitor_data, test_visitor_out_string); - output_visitor_test_add("/visitor/output/no-string", - &out_visitor_data, test_visitor_out_no_string); - output_visitor_test_add("/visitor/output/enum", - &out_visitor_data, test_visitor_out_enum); - output_visitor_test_add("/visitor/output/struct", - &out_visitor_data, test_visitor_out_struct); - output_visitor_test_add("/visitor/output/struct-nested", - &out_visitor_data, test_visitor_out_struct_nested); - output_visitor_test_add("/visitor/output/list", - &out_visitor_data, test_visitor_out_list); - output_visitor_test_add("/visitor/output/any", - &out_visitor_data, test_visitor_out_any); - output_visitor_test_add("/visitor/output/list-qapi-free", - &out_visitor_data, test_visitor_out_list_qapi_free); - output_visitor_test_add("/visitor/output/union-flat", - &out_visitor_data, test_visitor_out_union_flat); - output_visitor_test_add("/visitor/output/alternate", - &out_visitor_data, test_visitor_out_alternate); - output_visitor_test_add("/visitor/output/null", - &out_visitor_data, test_visitor_out_null); - output_visitor_test_add("/visitor/output/list_union/int", - &out_visitor_data, - test_visitor_out_list_union_int); - output_visitor_test_add("/visitor/output/list_union/int8", - &out_visitor_data, - test_visitor_out_list_union_int8); - output_visitor_test_add("/visitor/output/list_union/int16", - &out_visitor_data, - test_visitor_out_list_union_int16); - output_visitor_test_add("/visitor/output/list_union/int32", - &out_visitor_data, - test_visitor_out_list_union_int32); - output_visitor_test_add("/visitor/output/list_union/int64", - &out_visitor_data, - test_visitor_out_list_union_int64); - output_visitor_test_add("/visitor/output/list_union/uint8", - &out_visitor_data, - test_visitor_out_list_union_uint8); - output_visitor_test_add("/visitor/output/list_union/uint16", - &out_visitor_data, - test_visitor_out_list_union_uint16); - output_visitor_test_add("/visitor/output/list_union/uint32", - &out_visitor_data, - test_visitor_out_list_union_uint32); - output_visitor_test_add("/visitor/output/list_union/uint64", - &out_visitor_data, - test_visitor_out_list_union_uint64); - output_visitor_test_add("/visitor/output/list_union/bool", - &out_visitor_data, - test_visitor_out_list_union_bool); - output_visitor_test_add("/visitor/output/list_union/string", - &out_visitor_data, - test_visitor_out_list_union_str); - output_visitor_test_add("/visitor/output/list_union/number", - &out_visitor_data, - test_visitor_out_list_union_number); - - g_test_run(); - - return 0; -} diff --git a/tests/test-rcu-list.c b/tests/test-rcu-list.c deleted file mode 100644 index 49641e1936..0000000000 --- a/tests/test-rcu-list.c +++ /dev/null @@ -1,381 +0,0 @@ -/* - * rcuq_test.c - * - * usage: rcuq_test - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - * Copyright (c) 2013 Mike D. Day, IBM Corporation. - */ - -#include "qemu/osdep.h" -#include "qemu/atomic.h" -#include "qemu/rcu.h" -#include "qemu/thread.h" -#include "qemu/rcu_queue.h" - -/* - * Test variables. - */ - -static QemuMutex counts_mutex; -static long long n_reads = 0LL; -static long long n_updates = 0LL; -static int64_t n_reclaims; -static int64_t n_nodes_removed; -static long long n_nodes = 0LL; -static int g_test_in_charge = 0; - -static int nthreadsrunning; - -#define GOFLAG_INIT 0 -#define GOFLAG_RUN 1 -#define GOFLAG_STOP 2 - -static int goflag = GOFLAG_INIT; - -#define RCU_READ_RUN 1000 -#define RCU_UPDATE_RUN 10 -#define NR_THREADS 100 -#define RCU_Q_LEN 100 - -static QemuThread threads[NR_THREADS]; -static struct rcu_reader_data *data[NR_THREADS]; -static int n_threads; - -static int select_random_el(int max) -{ - return (rand() % max); -} - - -static void create_thread(void *(*func)(void *)) -{ - if (n_threads >= NR_THREADS) { - fprintf(stderr, "Thread limit of %d exceeded!\n", NR_THREADS); - exit(-1); - } - qemu_thread_create(&threads[n_threads], "test", func, &data[n_threads], - QEMU_THREAD_JOINABLE); - n_threads++; -} - -static void wait_all_threads(void) -{ - int i; - - for (i = 0; i < n_threads; i++) { - qemu_thread_join(&threads[i]); - } - n_threads = 0; -} - -#ifndef TEST_LIST_TYPE -#define TEST_LIST_TYPE 1 -#endif - -struct list_element { -#if TEST_LIST_TYPE == 1 - QLIST_ENTRY(list_element) entry; -#elif TEST_LIST_TYPE == 2 - QSIMPLEQ_ENTRY(list_element) entry; -#elif TEST_LIST_TYPE == 3 - QTAILQ_ENTRY(list_element) entry; -#elif TEST_LIST_TYPE == 4 - QSLIST_ENTRY(list_element) entry; -#else -#error Invalid TEST_LIST_TYPE -#endif - struct rcu_head rcu; -}; - -static void reclaim_list_el(struct rcu_head *prcu) -{ - struct list_element *el = container_of(prcu, struct list_element, rcu); - g_free(el); - /* Accessed only from call_rcu thread. */ - qatomic_set_i64(&n_reclaims, n_reclaims + 1); -} - -#if TEST_LIST_TYPE == 1 -static QLIST_HEAD(, list_element) Q_list_head; - -#define TEST_NAME "qlist" -#define TEST_LIST_REMOVE_RCU QLIST_REMOVE_RCU -#define TEST_LIST_INSERT_AFTER_RCU QLIST_INSERT_AFTER_RCU -#define TEST_LIST_INSERT_HEAD_RCU QLIST_INSERT_HEAD_RCU -#define TEST_LIST_FOREACH_RCU QLIST_FOREACH_RCU -#define TEST_LIST_FOREACH_SAFE_RCU QLIST_FOREACH_SAFE_RCU - -#elif TEST_LIST_TYPE == 2 -static QSIMPLEQ_HEAD(, list_element) Q_list_head = - QSIMPLEQ_HEAD_INITIALIZER(Q_list_head); - -#define TEST_NAME "qsimpleq" -#define TEST_LIST_REMOVE_RCU(el, f) \ - QSIMPLEQ_REMOVE_RCU(&Q_list_head, el, list_element, f) - -#define TEST_LIST_INSERT_AFTER_RCU(list_el, el, f) \ - QSIMPLEQ_INSERT_AFTER_RCU(&Q_list_head, list_el, el, f) - -#define TEST_LIST_INSERT_HEAD_RCU QSIMPLEQ_INSERT_HEAD_RCU -#define TEST_LIST_FOREACH_RCU QSIMPLEQ_FOREACH_RCU -#define TEST_LIST_FOREACH_SAFE_RCU QSIMPLEQ_FOREACH_SAFE_RCU - -#elif TEST_LIST_TYPE == 3 -static QTAILQ_HEAD(, list_element) Q_list_head; - -#define TEST_NAME "qtailq" -#define TEST_LIST_REMOVE_RCU(el, f) QTAILQ_REMOVE_RCU(&Q_list_head, el, f) - -#define TEST_LIST_INSERT_AFTER_RCU(list_el, el, f) \ - QTAILQ_INSERT_AFTER_RCU(&Q_list_head, list_el, el, f) - -#define TEST_LIST_INSERT_HEAD_RCU QTAILQ_INSERT_HEAD_RCU -#define TEST_LIST_FOREACH_RCU QTAILQ_FOREACH_RCU -#define TEST_LIST_FOREACH_SAFE_RCU QTAILQ_FOREACH_SAFE_RCU - -#elif TEST_LIST_TYPE == 4 -static QSLIST_HEAD(, list_element) Q_list_head; - -#define TEST_NAME "qslist" -#define TEST_LIST_REMOVE_RCU(el, f) \ - QSLIST_REMOVE_RCU(&Q_list_head, el, list_element, f) - -#define TEST_LIST_INSERT_AFTER_RCU(list_el, el, f) \ - QSLIST_INSERT_AFTER_RCU(&Q_list_head, list_el, el, f) - -#define TEST_LIST_INSERT_HEAD_RCU QSLIST_INSERT_HEAD_RCU -#define TEST_LIST_FOREACH_RCU QSLIST_FOREACH_RCU -#define TEST_LIST_FOREACH_SAFE_RCU QSLIST_FOREACH_SAFE_RCU -#else -#error Invalid TEST_LIST_TYPE -#endif - -static void *rcu_q_reader(void *arg) -{ - long long n_reads_local = 0; - struct list_element *el; - - rcu_register_thread(); - - *(struct rcu_reader_data **)arg = &rcu_reader; - qatomic_inc(&nthreadsrunning); - while (qatomic_read(&goflag) == GOFLAG_INIT) { - g_usleep(1000); - } - - while (qatomic_read(&goflag) == GOFLAG_RUN) { - rcu_read_lock(); - TEST_LIST_FOREACH_RCU(el, &Q_list_head, entry) { - n_reads_local++; - if (qatomic_read(&goflag) == GOFLAG_STOP) { - break; - } - } - rcu_read_unlock(); - - g_usleep(100); - } - qemu_mutex_lock(&counts_mutex); - n_reads += n_reads_local; - qemu_mutex_unlock(&counts_mutex); - - rcu_unregister_thread(); - return NULL; -} - - -static void *rcu_q_updater(void *arg) -{ - int j, target_el; - long long n_nodes_local = 0; - long long n_updates_local = 0; - long long n_removed_local = 0; - struct list_element *el, *prev_el; - - *(struct rcu_reader_data **)arg = &rcu_reader; - qatomic_inc(&nthreadsrunning); - while (qatomic_read(&goflag) == GOFLAG_INIT) { - g_usleep(1000); - } - - while (qatomic_read(&goflag) == GOFLAG_RUN) { - target_el = select_random_el(RCU_Q_LEN); - j = 0; - /* FOREACH_RCU could work here but let's use both macros */ - TEST_LIST_FOREACH_SAFE_RCU(prev_el, &Q_list_head, entry, el) { - j++; - if (target_el == j) { - TEST_LIST_REMOVE_RCU(prev_el, entry); - /* may be more than one updater in the future */ - call_rcu1(&prev_el->rcu, reclaim_list_el); - n_removed_local++; - break; - } - } - if (qatomic_read(&goflag) == GOFLAG_STOP) { - break; - } - target_el = select_random_el(RCU_Q_LEN); - j = 0; - TEST_LIST_FOREACH_RCU(el, &Q_list_head, entry) { - j++; - if (target_el == j) { - struct list_element *new_el = g_new(struct list_element, 1); - n_nodes_local++; - TEST_LIST_INSERT_AFTER_RCU(el, new_el, entry); - break; - } - } - - n_updates_local += 2; - synchronize_rcu(); - } - synchronize_rcu(); - qemu_mutex_lock(&counts_mutex); - n_nodes += n_nodes_local; - n_updates += n_updates_local; - qatomic_set_i64(&n_nodes_removed, n_nodes_removed + n_removed_local); - qemu_mutex_unlock(&counts_mutex); - return NULL; -} - -static void rcu_qtest_init(void) -{ - struct list_element *new_el; - int i; - nthreadsrunning = 0; - srand(time(0)); - for (i = 0; i < RCU_Q_LEN; i++) { - new_el = g_new(struct list_element, 1); - TEST_LIST_INSERT_HEAD_RCU(&Q_list_head, new_el, entry); - } - qemu_mutex_lock(&counts_mutex); - n_nodes += RCU_Q_LEN; - qemu_mutex_unlock(&counts_mutex); -} - -static void rcu_qtest_run(int duration, int nreaders) -{ - int nthreads = nreaders + 1; - while (qatomic_read(&nthreadsrunning) < nthreads) { - g_usleep(1000); - } - - qatomic_set(&goflag, GOFLAG_RUN); - sleep(duration); - qatomic_set(&goflag, GOFLAG_STOP); - wait_all_threads(); -} - - -static void rcu_qtest(const char *test, int duration, int nreaders) -{ - int i; - long long n_removed_local = 0; - - struct list_element *el, *prev_el; - - rcu_qtest_init(); - for (i = 0; i < nreaders; i++) { - create_thread(rcu_q_reader); - } - create_thread(rcu_q_updater); - rcu_qtest_run(duration, nreaders); - - TEST_LIST_FOREACH_SAFE_RCU(prev_el, &Q_list_head, entry, el) { - TEST_LIST_REMOVE_RCU(prev_el, entry); - call_rcu1(&prev_el->rcu, reclaim_list_el); - n_removed_local++; - } - qemu_mutex_lock(&counts_mutex); - qatomic_set_i64(&n_nodes_removed, n_nodes_removed + n_removed_local); - qemu_mutex_unlock(&counts_mutex); - synchronize_rcu(); - while (qatomic_read_i64(&n_nodes_removed) > - qatomic_read_i64(&n_reclaims)) { - g_usleep(100); - synchronize_rcu(); - } - if (g_test_in_charge) { - g_assert_cmpint(qatomic_read_i64(&n_nodes_removed), ==, - qatomic_read_i64(&n_reclaims)); - } else { - printf("%s: %d readers; 1 updater; nodes read: " \ - "%lld, nodes removed: %"PRIi64"; nodes reclaimed: %"PRIi64"\n", - test, nthreadsrunning - 1, n_reads, - qatomic_read_i64(&n_nodes_removed), - qatomic_read_i64(&n_reclaims)); - exit(0); - } -} - -static void usage(int argc, char *argv[]) -{ - fprintf(stderr, "Usage: %s duration nreaders\n", argv[0]); - exit(-1); -} - -static int gtest_seconds; - -static void gtest_rcuq_one(void) -{ - rcu_qtest("rcuqtest", gtest_seconds / 4, 1); -} - -static void gtest_rcuq_few(void) -{ - rcu_qtest("rcuqtest", gtest_seconds / 4, 5); -} - -static void gtest_rcuq_many(void) -{ - rcu_qtest("rcuqtest", gtest_seconds / 2, 20); -} - - -int main(int argc, char *argv[]) -{ - int duration = 0, readers = 0; - - qemu_mutex_init(&counts_mutex); - if (argc >= 2) { - if (argv[1][0] == '-') { - g_test_init(&argc, &argv, NULL); - if (g_test_quick()) { - gtest_seconds = 4; - } else { - gtest_seconds = 20; - } - g_test_add_func("/rcu/"TEST_NAME"/single-threaded", gtest_rcuq_one); - g_test_add_func("/rcu/"TEST_NAME"/short-few", gtest_rcuq_few); - g_test_add_func("/rcu/"TEST_NAME"/long-many", gtest_rcuq_many); - g_test_in_charge = 1; - return g_test_run(); - } - duration = strtoul(argv[1], NULL, 0); - } - if (argc >= 3) { - readers = strtoul(argv[2], NULL, 0); - } - if (duration && readers) { - rcu_qtest(argv[0], duration, readers); - return 0; - } - - usage(argc, argv); - return -1; -} diff --git a/tests/test-rcu-simpleq.c b/tests/test-rcu-simpleq.c deleted file mode 100644 index 057f7d33f7..0000000000 --- a/tests/test-rcu-simpleq.c +++ /dev/null @@ -1,2 +0,0 @@ -#define TEST_LIST_TYPE 2 -#include "test-rcu-list.c" diff --git a/tests/test-rcu-slist.c b/tests/test-rcu-slist.c deleted file mode 100644 index 868e1e472e..0000000000 --- a/tests/test-rcu-slist.c +++ /dev/null @@ -1,2 +0,0 @@ -#define TEST_LIST_TYPE 4 -#include "test-rcu-list.c" diff --git a/tests/test-rcu-tailq.c b/tests/test-rcu-tailq.c deleted file mode 100644 index 8d487e0ee0..0000000000 --- a/tests/test-rcu-tailq.c +++ /dev/null @@ -1,2 +0,0 @@ -#define TEST_LIST_TYPE 3 -#include "test-rcu-list.c" diff --git a/tests/test-replication.c b/tests/test-replication.c deleted file mode 100644 index b067240add..0000000000 --- a/tests/test-replication.c +++ /dev/null @@ -1,623 +0,0 @@ -/* - * Block replication tests - * - * Copyright (c) 2016 FUJITSU LIMITED - * Author: Changlong Xie - * - * This work is licensed under the terms of the GNU GPL, version 2 or - * later. See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" - -#include "qapi/error.h" -#include "qapi/qmp/qdict.h" -#include "qemu/option.h" -#include "qemu/main-loop.h" -#include "replication.h" -#include "block/block_int.h" -#include "block/qdict.h" -#include "sysemu/block-backend.h" - -#define IMG_SIZE (64 * 1024 * 1024) - -/* primary */ -#define P_ID "primary-id" -static char *p_local_disk; - -/* secondary */ -#define S_ID "secondary-id" -#define S_LOCAL_DISK_ID "secondary-local-disk-id" -static char *s_local_disk; -static char *s_active_disk; -static char *s_hidden_disk; - -/* FIXME: steal from blockdev.c */ -QemuOptsList qemu_drive_opts = { - .name = "drive", - .head = QTAILQ_HEAD_INITIALIZER(qemu_drive_opts.head), - .desc = { - { /* end of list */ } - }, -}; - -#define NOT_DONE 0x7fffffff - -static void blk_rw_done(void *opaque, int ret) -{ - *(int *)opaque = ret; -} - -static void test_blk_read(BlockBackend *blk, long pattern, - int64_t pattern_offset, int64_t pattern_count, - int64_t offset, int64_t count, - bool expect_failed) -{ - void *pattern_buf = NULL; - QEMUIOVector qiov; - void *cmp_buf = NULL; - int async_ret = NOT_DONE; - - if (pattern) { - cmp_buf = g_malloc(pattern_count); - memset(cmp_buf, pattern, pattern_count); - } - - pattern_buf = g_malloc(count); - if (pattern) { - memset(pattern_buf, pattern, count); - } else { - memset(pattern_buf, 0x00, count); - } - - qemu_iovec_init(&qiov, 1); - qemu_iovec_add(&qiov, pattern_buf, count); - - blk_aio_preadv(blk, offset, &qiov, 0, blk_rw_done, &async_ret); - while (async_ret == NOT_DONE) { - main_loop_wait(false); - } - - if (expect_failed) { - g_assert(async_ret != 0); - } else { - g_assert(async_ret == 0); - if (pattern) { - g_assert(memcmp(pattern_buf + pattern_offset, - cmp_buf, pattern_count) <= 0); - } - } - - g_free(pattern_buf); - g_free(cmp_buf); - qemu_iovec_destroy(&qiov); -} - -static void test_blk_write(BlockBackend *blk, long pattern, int64_t offset, - int64_t count, bool expect_failed) -{ - void *pattern_buf = NULL; - QEMUIOVector qiov; - int async_ret = NOT_DONE; - - pattern_buf = g_malloc(count); - if (pattern) { - memset(pattern_buf, pattern, count); - } else { - memset(pattern_buf, 0x00, count); - } - - qemu_iovec_init(&qiov, 1); - qemu_iovec_add(&qiov, pattern_buf, count); - - blk_aio_pwritev(blk, offset, &qiov, 0, blk_rw_done, &async_ret); - while (async_ret == NOT_DONE) { - main_loop_wait(false); - } - - if (expect_failed) { - g_assert(async_ret != 0); - } else { - g_assert(async_ret == 0); - } - - g_free(pattern_buf); - qemu_iovec_destroy(&qiov); -} - -/* - * Create a uniquely-named empty temporary file. - */ -static void make_temp(char *template) -{ - int fd; - - fd = mkstemp(template); - g_assert(fd >= 0); - close(fd); -} - -static void prepare_imgs(void) -{ - make_temp(p_local_disk); - make_temp(s_local_disk); - make_temp(s_active_disk); - make_temp(s_hidden_disk); - - /* Primary */ - bdrv_img_create(p_local_disk, "qcow2", NULL, NULL, NULL, IMG_SIZE, - BDRV_O_RDWR, true, &error_abort); - - /* Secondary */ - bdrv_img_create(s_local_disk, "qcow2", NULL, NULL, NULL, IMG_SIZE, - BDRV_O_RDWR, true, &error_abort); - bdrv_img_create(s_active_disk, "qcow2", NULL, NULL, NULL, IMG_SIZE, - BDRV_O_RDWR, true, &error_abort); - bdrv_img_create(s_hidden_disk, "qcow2", NULL, NULL, NULL, IMG_SIZE, - BDRV_O_RDWR, true, &error_abort); -} - -static void cleanup_imgs(void) -{ - /* Primary */ - unlink(p_local_disk); - - /* Secondary */ - unlink(s_local_disk); - unlink(s_active_disk); - unlink(s_hidden_disk); -} - -static BlockBackend *start_primary(void) -{ - BlockBackend *blk; - QemuOpts *opts; - QDict *qdict; - char *cmdline; - - cmdline = g_strdup_printf("driver=replication,mode=primary,node-name=xxx," - "file.driver=qcow2,file.file.filename=%s," - "file.file.locking=off" - , p_local_disk); - opts = qemu_opts_parse_noisily(&qemu_drive_opts, cmdline, false); - g_free(cmdline); - - qdict = qemu_opts_to_qdict(opts, NULL); - qdict_set_default_str(qdict, BDRV_OPT_CACHE_DIRECT, "off"); - qdict_set_default_str(qdict, BDRV_OPT_CACHE_NO_FLUSH, "off"); - - blk = blk_new_open(NULL, NULL, qdict, BDRV_O_RDWR, &error_abort); - g_assert(blk); - - monitor_add_blk(blk, P_ID, &error_abort); - - qemu_opts_del(opts); - - return blk; -} - -static void teardown_primary(void) -{ - BlockBackend *blk; - AioContext *ctx; - - /* remove P_ID */ - blk = blk_by_name(P_ID); - assert(blk); - - ctx = blk_get_aio_context(blk); - aio_context_acquire(ctx); - monitor_remove_blk(blk); - blk_unref(blk); - aio_context_release(ctx); -} - -static void test_primary_read(void) -{ - BlockBackend *blk; - - blk = start_primary(); - - /* read from 0 to IMG_SIZE */ - test_blk_read(blk, 0, 0, IMG_SIZE, 0, IMG_SIZE, true); - - teardown_primary(); -} - -static void test_primary_write(void) -{ - BlockBackend *blk; - - blk = start_primary(); - - /* write from 0 to IMG_SIZE */ - test_blk_write(blk, 0, 0, IMG_SIZE, true); - - teardown_primary(); -} - -static void test_primary_start(void) -{ - BlockBackend *blk = NULL; - - blk = start_primary(); - - replication_start_all(REPLICATION_MODE_PRIMARY, &error_abort); - - /* read from 0 to IMG_SIZE */ - test_blk_read(blk, 0, 0, IMG_SIZE, 0, IMG_SIZE, true); - - /* write 0x22 from 0 to IMG_SIZE */ - test_blk_write(blk, 0x22, 0, IMG_SIZE, false); - - teardown_primary(); -} - -static void test_primary_stop(void) -{ - bool failover = true; - - start_primary(); - - replication_start_all(REPLICATION_MODE_PRIMARY, &error_abort); - - replication_stop_all(failover, &error_abort); - - teardown_primary(); -} - -static void test_primary_do_checkpoint(void) -{ - start_primary(); - - replication_start_all(REPLICATION_MODE_PRIMARY, &error_abort); - - replication_do_checkpoint_all(&error_abort); - - teardown_primary(); -} - -static void test_primary_get_error_all(void) -{ - start_primary(); - - replication_start_all(REPLICATION_MODE_PRIMARY, &error_abort); - - replication_get_error_all(&error_abort); - - teardown_primary(); -} - -static BlockBackend *start_secondary(void) -{ - QemuOpts *opts; - QDict *qdict; - BlockBackend *blk; - char *cmdline; - - /* add s_local_disk and forge S_LOCAL_DISK_ID */ - cmdline = g_strdup_printf("file.filename=%s,driver=qcow2," - "file.locking=off", - s_local_disk); - opts = qemu_opts_parse_noisily(&qemu_drive_opts, cmdline, false); - g_free(cmdline); - - qdict = qemu_opts_to_qdict(opts, NULL); - qdict_set_default_str(qdict, BDRV_OPT_CACHE_DIRECT, "off"); - qdict_set_default_str(qdict, BDRV_OPT_CACHE_NO_FLUSH, "off"); - - blk = blk_new_open(NULL, NULL, qdict, BDRV_O_RDWR, &error_abort); - assert(blk); - monitor_add_blk(blk, S_LOCAL_DISK_ID, &error_abort); - - /* format s_local_disk with pattern "0x11" */ - test_blk_write(blk, 0x11, 0, IMG_SIZE, false); - - qemu_opts_del(opts); - - /* add S_(ACTIVE/HIDDEN)_DISK and forge S_ID */ - cmdline = g_strdup_printf("driver=replication,mode=secondary,top-id=%s," - "file.driver=qcow2,file.file.filename=%s," - "file.file.locking=off," - "file.backing.driver=qcow2," - "file.backing.file.filename=%s," - "file.backing.file.locking=off," - "file.backing.backing=%s" - , S_ID, s_active_disk, s_hidden_disk - , S_LOCAL_DISK_ID); - opts = qemu_opts_parse_noisily(&qemu_drive_opts, cmdline, false); - g_free(cmdline); - - qdict = qemu_opts_to_qdict(opts, NULL); - qdict_set_default_str(qdict, BDRV_OPT_CACHE_DIRECT, "off"); - qdict_set_default_str(qdict, BDRV_OPT_CACHE_NO_FLUSH, "off"); - - blk = blk_new_open(NULL, NULL, qdict, BDRV_O_RDWR, &error_abort); - assert(blk); - monitor_add_blk(blk, S_ID, &error_abort); - - qemu_opts_del(opts); - - return blk; -} - -static void teardown_secondary(void) -{ - /* only need to destroy two BBs */ - BlockBackend *blk; - AioContext *ctx; - - /* remove S_LOCAL_DISK_ID */ - blk = blk_by_name(S_LOCAL_DISK_ID); - assert(blk); - - ctx = blk_get_aio_context(blk); - aio_context_acquire(ctx); - monitor_remove_blk(blk); - blk_unref(blk); - aio_context_release(ctx); - - /* remove S_ID */ - blk = blk_by_name(S_ID); - assert(blk); - - ctx = blk_get_aio_context(blk); - aio_context_acquire(ctx); - monitor_remove_blk(blk); - blk_unref(blk); - aio_context_release(ctx); -} - -static void test_secondary_read(void) -{ - BlockBackend *blk; - - blk = start_secondary(); - - /* read from 0 to IMG_SIZE */ - test_blk_read(blk, 0, 0, IMG_SIZE, 0, IMG_SIZE, true); - - teardown_secondary(); -} - -static void test_secondary_write(void) -{ - BlockBackend *blk; - - blk = start_secondary(); - - /* write from 0 to IMG_SIZE */ - test_blk_write(blk, 0, 0, IMG_SIZE, true); - - teardown_secondary(); -} - -#ifndef _WIN32 -static void test_secondary_start(void) -{ - BlockBackend *top_blk, *local_blk; - bool failover = true; - - top_blk = start_secondary(); - replication_start_all(REPLICATION_MODE_SECONDARY, &error_abort); - - /* read from s_local_disk (0, IMG_SIZE) */ - test_blk_read(top_blk, 0x11, 0, IMG_SIZE, 0, IMG_SIZE, false); - - /* write 0x22 to s_local_disk (IMG_SIZE / 2, IMG_SIZE) */ - local_blk = blk_by_name(S_LOCAL_DISK_ID); - test_blk_write(local_blk, 0x22, IMG_SIZE / 2, IMG_SIZE / 2, false); - - /* replication will backup s_local_disk to s_hidden_disk */ - test_blk_read(top_blk, 0x11, IMG_SIZE / 2, - IMG_SIZE / 2, 0, IMG_SIZE, false); - - /* write 0x33 to s_active_disk (0, IMG_SIZE / 2) */ - test_blk_write(top_blk, 0x33, 0, IMG_SIZE / 2, false); - - /* read from s_active_disk (0, IMG_SIZE/2) */ - test_blk_read(top_blk, 0x33, 0, IMG_SIZE / 2, - 0, IMG_SIZE / 2, false); - - /* unblock top_bs */ - replication_stop_all(failover, &error_abort); - - teardown_secondary(); -} - - -static void test_secondary_stop(void) -{ - BlockBackend *top_blk, *local_blk; - bool failover = true; - - top_blk = start_secondary(); - replication_start_all(REPLICATION_MODE_SECONDARY, &error_abort); - - /* write 0x22 to s_local_disk (IMG_SIZE / 2, IMG_SIZE) */ - local_blk = blk_by_name(S_LOCAL_DISK_ID); - test_blk_write(local_blk, 0x22, IMG_SIZE / 2, IMG_SIZE / 2, false); - - /* replication will backup s_local_disk to s_hidden_disk */ - test_blk_read(top_blk, 0x11, IMG_SIZE / 2, - IMG_SIZE / 2, 0, IMG_SIZE, false); - - /* write 0x33 to s_active_disk (0, IMG_SIZE / 2) */ - test_blk_write(top_blk, 0x33, 0, IMG_SIZE / 2, false); - - /* do active commit */ - replication_stop_all(failover, &error_abort); - - /* read from s_local_disk (0, IMG_SIZE / 2) */ - test_blk_read(top_blk, 0x33, 0, IMG_SIZE / 2, - 0, IMG_SIZE / 2, false); - - - /* read from s_local_disk (IMG_SIZE / 2, IMG_SIZE) */ - test_blk_read(top_blk, 0x22, IMG_SIZE / 2, - IMG_SIZE / 2, 0, IMG_SIZE, false); - - teardown_secondary(); -} - -static void test_secondary_continuous_replication(void) -{ - BlockBackend *top_blk, *local_blk; - - top_blk = start_secondary(); - replication_start_all(REPLICATION_MODE_SECONDARY, &error_abort); - - /* write 0x22 to s_local_disk (IMG_SIZE / 2, IMG_SIZE) */ - local_blk = blk_by_name(S_LOCAL_DISK_ID); - test_blk_write(local_blk, 0x22, IMG_SIZE / 2, IMG_SIZE / 2, false); - - /* replication will backup s_local_disk to s_hidden_disk */ - test_blk_read(top_blk, 0x11, IMG_SIZE / 2, - IMG_SIZE / 2, 0, IMG_SIZE, false); - - /* write 0x33 to s_active_disk (0, IMG_SIZE / 2) */ - test_blk_write(top_blk, 0x33, 0, IMG_SIZE / 2, false); - - /* do failover (active commit) */ - replication_stop_all(true, &error_abort); - - /* it should ignore all requests from now on */ - - /* start after failover */ - replication_start_all(REPLICATION_MODE_PRIMARY, &error_abort); - - /* checkpoint */ - replication_do_checkpoint_all(&error_abort); - - /* stop */ - replication_stop_all(true, &error_abort); - - /* read from s_local_disk (0, IMG_SIZE / 2) */ - test_blk_read(top_blk, 0x33, 0, IMG_SIZE / 2, - 0, IMG_SIZE / 2, false); - - - /* read from s_local_disk (IMG_SIZE / 2, IMG_SIZE) */ - test_blk_read(top_blk, 0x22, IMG_SIZE / 2, - IMG_SIZE / 2, 0, IMG_SIZE, false); - - teardown_secondary(); -} - -static void test_secondary_do_checkpoint(void) -{ - BlockBackend *top_blk, *local_blk; - bool failover = true; - - top_blk = start_secondary(); - replication_start_all(REPLICATION_MODE_SECONDARY, &error_abort); - - /* write 0x22 to s_local_disk (IMG_SIZE / 2, IMG_SIZE) */ - local_blk = blk_by_name(S_LOCAL_DISK_ID); - test_blk_write(local_blk, 0x22, IMG_SIZE / 2, - IMG_SIZE / 2, false); - - /* replication will backup s_local_disk to s_hidden_disk */ - test_blk_read(top_blk, 0x11, IMG_SIZE / 2, - IMG_SIZE / 2, 0, IMG_SIZE, false); - - replication_do_checkpoint_all(&error_abort); - - /* after checkpoint, read pattern 0x22 from s_local_disk */ - test_blk_read(top_blk, 0x22, IMG_SIZE / 2, - IMG_SIZE / 2, 0, IMG_SIZE, false); - - /* unblock top_bs */ - replication_stop_all(failover, &error_abort); - - teardown_secondary(); -} - -static void test_secondary_get_error_all(void) -{ - bool failover = true; - - start_secondary(); - replication_start_all(REPLICATION_MODE_SECONDARY, &error_abort); - - replication_get_error_all(&error_abort); - - /* unblock top_bs */ - replication_stop_all(failover, &error_abort); - - teardown_secondary(); -} -#endif - -static void sigabrt_handler(int signo) -{ - cleanup_imgs(); -} - -static void setup_sigabrt_handler(void) -{ -#ifdef _WIN32 - signal(SIGABRT, sigabrt_handler); -#else - struct sigaction sigact; - - sigact = (struct sigaction) { - .sa_handler = sigabrt_handler, - .sa_flags = SA_RESETHAND, - }; - sigemptyset(&sigact.sa_mask); - sigaction(SIGABRT, &sigact, NULL); -#endif -} - -int main(int argc, char **argv) -{ - int ret; - const char *tmpdir = g_get_tmp_dir(); - p_local_disk = g_strdup_printf("%s/p_local_disk.XXXXXX", tmpdir); - s_local_disk = g_strdup_printf("%s/s_local_disk.XXXXXX", tmpdir); - s_active_disk = g_strdup_printf("%s/s_active_disk.XXXXXX", tmpdir); - s_hidden_disk = g_strdup_printf("%s/s_hidden_disk.XXXXXX", tmpdir); - qemu_init_main_loop(&error_fatal); - bdrv_init(); - - g_test_init(&argc, &argv, NULL); - setup_sigabrt_handler(); - - prepare_imgs(); - - /* Primary */ - g_test_add_func("/replication/primary/read", test_primary_read); - g_test_add_func("/replication/primary/write", test_primary_write); - g_test_add_func("/replication/primary/start", test_primary_start); - g_test_add_func("/replication/primary/stop", test_primary_stop); - g_test_add_func("/replication/primary/do_checkpoint", - test_primary_do_checkpoint); - g_test_add_func("/replication/primary/get_error_all", - test_primary_get_error_all); - - /* Secondary */ - g_test_add_func("/replication/secondary/read", test_secondary_read); - g_test_add_func("/replication/secondary/write", test_secondary_write); -#ifndef _WIN32 - g_test_add_func("/replication/secondary/start", test_secondary_start); - g_test_add_func("/replication/secondary/stop", test_secondary_stop); - g_test_add_func("/replication/secondary/continuous_replication", - test_secondary_continuous_replication); - g_test_add_func("/replication/secondary/do_checkpoint", - test_secondary_do_checkpoint); - g_test_add_func("/replication/secondary/get_error_all", - test_secondary_get_error_all); -#endif - - ret = g_test_run(); - - cleanup_imgs(); - - g_free(p_local_disk); - g_free(s_local_disk); - g_free(s_active_disk); - g_free(s_hidden_disk); - - return ret; -} diff --git a/tests/test-shift128.c b/tests/test-shift128.c deleted file mode 100644 index f3ff736e5c..0000000000 --- a/tests/test-shift128.c +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Test unsigned left and right shift - * - * This work is licensed under the terms of the GNU LGPL, version 2 or later. - * See the COPYING.LIB file in the top-level directory. - * - */ - -#include "qemu/osdep.h" -#include "qemu/host-utils.h" - -typedef struct { - uint64_t low; - uint64_t high; - uint64_t rlow; - uint64_t rhigh; - int32_t shift; - bool overflow; -} test_data; - -static const test_data test_ltable[] = { - { 0x4C7ULL, 0x0ULL, 0x00000000000004C7ULL, - 0x0000000000000000ULL, 0, false }, - { 0x001ULL, 0x0ULL, 0x0000000000000002ULL, - 0x0000000000000000ULL, 1, false }, - { 0x001ULL, 0x0ULL, 0x0000000000000004ULL, - 0x0000000000000000ULL, 2, false }, - { 0x001ULL, 0x0ULL, 0x0000000000000010ULL, - 0x0000000000000000ULL, 4, false }, - { 0x001ULL, 0x0ULL, 0x0000000000000100ULL, - 0x0000000000000000ULL, 8, false }, - { 0x001ULL, 0x0ULL, 0x0000000000010000ULL, - 0x0000000000000000ULL, 16, false }, - { 0x001ULL, 0x0ULL, 0x0000000080000000ULL, - 0x0000000000000000ULL, 31, false }, - { 0x001ULL, 0x0ULL, 0x0000200000000000ULL, - 0x0000000000000000ULL, 45, false }, - { 0x001ULL, 0x0ULL, 0x1000000000000000ULL, - 0x0000000000000000ULL, 60, false }, - { 0x001ULL, 0x0ULL, 0x0000000000000000ULL, - 0x0000000000000001ULL, 64, false }, - { 0x001ULL, 0x0ULL, 0x0000000000000000ULL, - 0x0000000000010000ULL, 80, false }, - { 0x001ULL, 0x0ULL, 0x0000000000000000ULL, - 0x8000000000000000ULL, 127, false }, - { 0x000ULL, 0x1ULL, 0x0000000000000000ULL, - 0x0000000000000000ULL, 64, true }, - { 0x008ULL, 0x0ULL, 0x0000000000000000ULL, - 0x0000000000000008ULL, 64, false }, - { 0x008ULL, 0x0ULL, 0x0000000000000000ULL, - 0x8000000000000000ULL, 124, false }, - { 0x001ULL, 0x0ULL, 0x0000000000000000ULL, - 0x4000000000000000ULL, 126, false }, - { 0x001ULL, 0x0ULL, 0x0000000000000000ULL, - 0x8000000000000000ULL, 127, false }, - { 0x001ULL, 0x0ULL, 0x0000000000000001ULL, - 0x0000000000000000ULL, 128, false }, - { 0x000ULL, 0x0ULL, 0x0000000000000000ULL, - 0x0000000000000000ULL, 200, false }, - { 0x001ULL, 0x0ULL, 0x0000000000000000ULL, - 0x0000000000000100ULL, 200, false }, - { 0x001ULL, 0x0ULL, 0x0000000000000000ULL, - 0x8000000000000000ULL, -1, false }, - { 0x001ULL, 0x0ULL, 0x0000000000000000ULL, - 0x8000000000000000ULL, INT32_MAX, false }, - { 0x001ULL, 0x0ULL, 0x0000000000000000ULL, - 0x4000000000000000ULL, -2, false }, - { 0x001ULL, 0x0ULL, 0x0000000000000000ULL, - 0x4000000000000000ULL, INT32_MAX - 1, false }, - { 0x8888888888888888ULL, 0x9999999999999999ULL, - 0x8000000000000000ULL, 0x9888888888888888ULL, 60, true }, - { 0x8888888888888888ULL, 0x9999999999999999ULL, - 0x0000000000000000ULL, 0x8888888888888888ULL, 64, true }, -}; - -static const test_data test_rtable[] = { - { 0x00000000000004C7ULL, 0x0ULL, 0x00000000000004C7ULL, 0x0ULL, 0, false }, - { 0x0800000000000000ULL, 0x0ULL, 0x0400000000000000ULL, 0x0ULL, 1, false }, - { 0x0800000000000000ULL, 0x0ULL, 0x0200000000000000ULL, 0x0ULL, 2, false }, - { 0x0800000000000000ULL, 0x0ULL, 0x0008000000000000ULL, 0x0ULL, 8, false }, - { 0x0800000000000000ULL, 0x0ULL, 0x0000080000000000ULL, 0x0ULL, 16, false }, - { 0x0800000000000000ULL, 0x0ULL, 0x0000000008000000ULL, 0x0ULL, 32, false }, - { 0x8000000000000000ULL, 0x0ULL, 0x0000000000000001ULL, 0x0ULL, 63, false }, - { 0x8000000000000000ULL, 0x0ULL, 0x0000000000000000ULL, 0x0ULL, 64, false }, - { 0x0000000000000000ULL, 0x8000000000000000ULL, - 0x0000000000000000ULL, 0x8000000000000000ULL, 128, false }, - { 0x0000000000000000ULL, 0x8000000000000000ULL, - 0x0080000000000000ULL, 0x0000000000000000ULL, 200, false }, - { 0x0000000000000000ULL, 0x0000000000000000ULL, - 0x0000000000000000ULL, 0x0000000000000000ULL, 200, false }, - { 0x0000000000000000ULL, 0x8000000000000000ULL, - 0x0000000000000000ULL, 0x0000000000000080ULL, -200, false }, - { 0x8000000000000000ULL, 0x8000000000000000ULL, - 0x0000000080000000ULL, 0x0000000080000000ULL, 32, false }, - { 0x0800000000000000ULL, 0x0800000000000000ULL, - 0x0800000000000000ULL, 0x0000000000000000ULL, 64, false }, - { 0x0800000000000000ULL, 0x0800000000000000ULL, - 0x0008000000000000ULL, 0x0000000000000000ULL, 72, false }, - { 0x8000000000000000ULL, 0x8000000000000000ULL, - 0x0000000000000001ULL, 0x0000000000000000ULL, 127, false }, - { 0x0000000000000000ULL, 0x8000000000000000ULL, - 0x0000000000000001ULL, 0x0000000000000000ULL, -1, false }, - { 0x0000000000000000ULL, 0x8000000000000000ULL, - 0x0000000000000002ULL, 0x0000000000000000ULL, -2, false }, -}; - -static void test_lshift(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(test_ltable); ++i) { - bool overflow = false; - test_data tmp = test_ltable[i]; - ulshift(&tmp.low, &tmp.high, tmp.shift, &overflow); - g_assert_cmpuint(tmp.low, ==, tmp.rlow); - g_assert_cmpuint(tmp.high, ==, tmp.rhigh); - g_assert_cmpuint(tmp.overflow, ==, overflow); - } -} - -static void test_rshift(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(test_rtable); ++i) { - test_data tmp = test_rtable[i]; - urshift(&tmp.low, &tmp.high, tmp.shift); - g_assert_cmpuint(tmp.low, ==, tmp.rlow); - g_assert_cmpuint(tmp.high, ==, tmp.rhigh); - } -} - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - g_test_add_func("/host-utils/test_lshift", test_lshift); - g_test_add_func("/host-utils/test_rshift", test_rshift); - return g_test_run(); -} diff --git a/tests/test-string-input-visitor.c b/tests/test-string-input-visitor.c deleted file mode 100644 index 249faafc9d..0000000000 --- a/tests/test-string-input-visitor.c +++ /dev/null @@ -1,501 +0,0 @@ -/* - * String Input Visitor unit-tests. - * - * Copyright (C) 2012 Red Hat Inc. - * - * Authors: - * Paolo Bonzini (based on test-qobject-input-visitor) - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" - -#include "qemu-common.h" -#include "qapi/error.h" -#include "qapi/string-input-visitor.h" -#include "test-qapi-visit.h" - -typedef struct TestInputVisitorData { - Visitor *v; -} TestInputVisitorData; - -static void visitor_input_teardown(TestInputVisitorData *data, - const void *unused) -{ - if (data->v) { - visit_free(data->v); - data->v = NULL; - } -} - -/* This is provided instead of a test setup function so that the JSON - string used by the tests are kept in the test functions (and not - int main()) */ -static -Visitor *visitor_input_test_init(TestInputVisitorData *data, - const char *string) -{ - visitor_input_teardown(data, NULL); - - data->v = string_input_visitor_new(string); - g_assert(data->v); - return data->v; -} - -static void test_visitor_in_int(TestInputVisitorData *data, - const void *unused) -{ - int64_t res = 0, value = -42; - Error *err = NULL; - Visitor *v; - - v = visitor_input_test_init(data, "-42"); - - visit_type_int(v, NULL, &res, &error_abort); - g_assert_cmpint(res, ==, value); - - v = visitor_input_test_init(data, "not an int"); - - visit_type_int(v, NULL, &res, &err); - error_free_or_abort(&err); - - v = visitor_input_test_init(data, ""); - - visit_type_int(v, NULL, &res, &err); - error_free_or_abort(&err); -} - -static void check_ilist(Visitor *v, int64_t *expected, size_t n) -{ - int64List *res = NULL; - int64List *tail; - int i; - - visit_type_int64List(v, NULL, &res, &error_abort); - tail = res; - for (i = 0; i < n; i++) { - g_assert(tail); - g_assert_cmpint(tail->value, ==, expected[i]); - tail = tail->next; - } - g_assert(!tail); - - qapi_free_int64List(res); -} - -static void check_ulist(Visitor *v, uint64_t *expected, size_t n) -{ - uint64List *res = NULL; - uint64List *tail; - int i; - - visit_type_uint64List(v, NULL, &res, &error_abort); - tail = res; - for (i = 0; i < n; i++) { - g_assert(tail); - g_assert_cmpuint(tail->value, ==, expected[i]); - tail = tail->next; - } - g_assert(!tail); - - qapi_free_uint64List(res); -} - -static void test_visitor_in_intList(TestInputVisitorData *data, - const void *unused) -{ - int64_t expect1[] = { 1, 2, 0, 2, 3, 4, 20, 5, 6, 7, - 8, 9, 1, 2, 3, 4, 5, 6, 7, 8 }; - int64_t expect2[] = { 32767, -32768, -32767 }; - int64_t expect3[] = { INT64_MIN, INT64_MAX }; - int64_t expect4[] = { 1 }; - int64_t expect5[] = { INT64_MAX - 2, INT64_MAX - 1, INT64_MAX }; - Error *err = NULL; - int64List *res = NULL; - Visitor *v; - int64_t val; - - /* Valid lists */ - - v = visitor_input_test_init(data, "1,2,0,2-4,20,5-9,1-8"); - check_ilist(v, expect1, ARRAY_SIZE(expect1)); - - v = visitor_input_test_init(data, "32767,-32768--32767"); - check_ilist(v, expect2, ARRAY_SIZE(expect2)); - - v = visitor_input_test_init(data, - "-9223372036854775808,9223372036854775807"); - check_ilist(v, expect3, ARRAY_SIZE(expect3)); - - v = visitor_input_test_init(data, "1-1"); - check_ilist(v, expect4, ARRAY_SIZE(expect4)); - - v = visitor_input_test_init(data, - "9223372036854775805-9223372036854775807"); - check_ilist(v, expect5, ARRAY_SIZE(expect5)); - - /* Value too large */ - - v = visitor_input_test_init(data, "9223372036854775808"); - visit_type_int64List(v, NULL, &res, &err); - error_free_or_abort(&err); - g_assert(!res); - - /* Value too small */ - - v = visitor_input_test_init(data, "-9223372036854775809"); - visit_type_int64List(v, NULL, &res, &err); - error_free_or_abort(&err); - g_assert(!res); - - /* Range not ascending */ - - v = visitor_input_test_init(data, "3-1"); - visit_type_int64List(v, NULL, &res, &err); - error_free_or_abort(&err); - g_assert(!res); - - v = visitor_input_test_init(data, "9223372036854775807-0"); - visit_type_int64List(v, NULL, &res, &err); - error_free_or_abort(&err); - g_assert(!res); - - /* Range too big (65536 is the limit against DOS attacks) */ - - v = visitor_input_test_init(data, "0-65536"); - visit_type_int64List(v, NULL, &res, &err); - error_free_or_abort(&err); - g_assert(!res); - - /* Empty list */ - - v = visitor_input_test_init(data, ""); - visit_type_int64List(v, NULL, &res, &error_abort); - g_assert(!res); - - /* Not a list */ - - v = visitor_input_test_init(data, "not an int list"); - - visit_type_int64List(v, NULL, &res, &err); - error_free_or_abort(&err); - g_assert(!res); - - /* Unvisited list tail */ - - v = visitor_input_test_init(data, "0,2-3"); - - visit_start_list(v, NULL, NULL, 0, &error_abort); - visit_type_int64(v, NULL, &val, &error_abort); - g_assert_cmpint(val, ==, 0); - visit_type_int64(v, NULL, &val, &error_abort); - g_assert_cmpint(val, ==, 2); - - visit_check_list(v, &err); - error_free_or_abort(&err); - visit_end_list(v, NULL); - - /* Visit beyond end of list */ - - v = visitor_input_test_init(data, "0"); - - visit_start_list(v, NULL, NULL, 0, &error_abort); - visit_type_int64(v, NULL, &val, &err); - g_assert_cmpint(val, ==, 0); - visit_type_int64(v, NULL, &val, &err); - error_free_or_abort(&err); - - visit_check_list(v, &error_abort); - visit_end_list(v, NULL); -} - -static void test_visitor_in_uintList(TestInputVisitorData *data, - const void *unused) -{ - uint64_t expect1[] = { 1, 2, 0, 2, 3, 4, 20, 5, 6, 7, - 8, 9, 1, 2, 3, 4, 5, 6, 7, 8 }; - uint64_t expect2[] = { 32767, -32768, -32767 }; - uint64_t expect3[] = { INT64_MIN, INT64_MAX }; - uint64_t expect4[] = { 1 }; - uint64_t expect5[] = { UINT64_MAX }; - uint64_t expect6[] = { UINT64_MAX - 2, UINT64_MAX - 1, UINT64_MAX }; - Error *err = NULL; - uint64List *res = NULL; - Visitor *v; - uint64_t val; - - /* Valid lists */ - - v = visitor_input_test_init(data, "1,2,0,2-4,20,5-9,1-8"); - check_ulist(v, expect1, ARRAY_SIZE(expect1)); - - v = visitor_input_test_init(data, "32767,-32768--32767"); - check_ulist(v, expect2, ARRAY_SIZE(expect2)); - - v = visitor_input_test_init(data, - "-9223372036854775808,9223372036854775807"); - check_ulist(v, expect3, ARRAY_SIZE(expect3)); - - v = visitor_input_test_init(data, "1-1"); - check_ulist(v, expect4, ARRAY_SIZE(expect4)); - - v = visitor_input_test_init(data, "18446744073709551615"); - check_ulist(v, expect5, ARRAY_SIZE(expect5)); - - v = visitor_input_test_init(data, - "18446744073709551613-18446744073709551615"); - check_ulist(v, expect6, ARRAY_SIZE(expect6)); - - /* Value too large */ - - v = visitor_input_test_init(data, "18446744073709551616"); - visit_type_uint64List(v, NULL, &res, &err); - error_free_or_abort(&err); - g_assert(!res); - - /* Value too small */ - - v = visitor_input_test_init(data, "-18446744073709551616"); - visit_type_uint64List(v, NULL, &res, &err); - error_free_or_abort(&err); - g_assert(!res); - - /* Range not ascending */ - - v = visitor_input_test_init(data, "3-1"); - visit_type_uint64List(v, NULL, &res, &err); - error_free_or_abort(&err); - g_assert(!res); - - v = visitor_input_test_init(data, "18446744073709551615-0"); - visit_type_uint64List(v, NULL, &res, &err); - error_free_or_abort(&err); - g_assert(!res); - - /* Range too big (65536 is the limit against DOS attacks) */ - - v = visitor_input_test_init(data, "0-65536"); - visit_type_uint64List(v, NULL, &res, &err); - error_free_or_abort(&err); - g_assert(!res); - - /* Empty list */ - - v = visitor_input_test_init(data, ""); - visit_type_uint64List(v, NULL, &res, &error_abort); - g_assert(!res); - - /* Not a list */ - - v = visitor_input_test_init(data, "not an uint list"); - - visit_type_uint64List(v, NULL, &res, &err); - error_free_or_abort(&err); - g_assert(!res); - - /* Unvisited list tail */ - - v = visitor_input_test_init(data, "0,2-3"); - - visit_start_list(v, NULL, NULL, 0, &error_abort); - visit_type_uint64(v, NULL, &val, &error_abort); - g_assert_cmpuint(val, ==, 0); - visit_type_uint64(v, NULL, &val, &error_abort); - g_assert_cmpuint(val, ==, 2); - - visit_check_list(v, &err); - error_free_or_abort(&err); - visit_end_list(v, NULL); - - /* Visit beyond end of list */ - - v = visitor_input_test_init(data, "0"); - - visit_start_list(v, NULL, NULL, 0, &error_abort); - visit_type_uint64(v, NULL, &val, &err); - g_assert_cmpuint(val, ==, 0); - visit_type_uint64(v, NULL, &val, &err); - error_free_or_abort(&err); - - visit_check_list(v, &error_abort); - visit_end_list(v, NULL); -} - -static void test_visitor_in_bool(TestInputVisitorData *data, - const void *unused) -{ - bool res = false; - Visitor *v; - - v = visitor_input_test_init(data, "true"); - - visit_type_bool(v, NULL, &res, &error_abort); - g_assert_cmpint(res, ==, true); - - v = visitor_input_test_init(data, "yes"); - - visit_type_bool(v, NULL, &res, &error_abort); - g_assert_cmpint(res, ==, true); - - v = visitor_input_test_init(data, "on"); - - visit_type_bool(v, NULL, &res, &error_abort); - g_assert_cmpint(res, ==, true); - - v = visitor_input_test_init(data, "false"); - - visit_type_bool(v, NULL, &res, &error_abort); - g_assert_cmpint(res, ==, false); - - v = visitor_input_test_init(data, "no"); - - visit_type_bool(v, NULL, &res, &error_abort); - g_assert_cmpint(res, ==, false); - - v = visitor_input_test_init(data, "off"); - - visit_type_bool(v, NULL, &res, &error_abort); - g_assert_cmpint(res, ==, false); -} - -static void test_visitor_in_number(TestInputVisitorData *data, - const void *unused) -{ - double res = 0, value = 3.14; - Error *err = NULL; - Visitor *v; - - v = visitor_input_test_init(data, "3.14"); - - visit_type_number(v, NULL, &res, &error_abort); - g_assert_cmpfloat(res, ==, value); - - /* NaN and infinity has to be rejected */ - - v = visitor_input_test_init(data, "NaN"); - - visit_type_number(v, NULL, &res, &err); - error_free_or_abort(&err); - - v = visitor_input_test_init(data, "inf"); - - visit_type_number(v, NULL, &res, &err); - error_free_or_abort(&err); - -} - -static void test_visitor_in_string(TestInputVisitorData *data, - const void *unused) -{ - char *res = NULL, *value = (char *) "Q E M U"; - Visitor *v; - - v = visitor_input_test_init(data, value); - - visit_type_str(v, NULL, &res, &error_abort); - g_assert_cmpstr(res, ==, value); - - g_free(res); -} - -static void test_visitor_in_enum(TestInputVisitorData *data, - const void *unused) -{ - Visitor *v; - EnumOne i; - - for (i = 0; i < ENUM_ONE__MAX; i++) { - EnumOne res = -1; - - v = visitor_input_test_init(data, EnumOne_str(i)); - - visit_type_EnumOne(v, NULL, &res, &error_abort); - g_assert_cmpint(i, ==, res); - } -} - -/* Try to crash the visitors */ -static void test_visitor_in_fuzz(TestInputVisitorData *data, - const void *unused) -{ - int64_t ires; - intList *ilres; - bool bres; - double nres; - char *sres; - EnumOne eres; - Visitor *v; - unsigned int i; - char buf[10000]; - - for (i = 0; i < 100; i++) { - unsigned int j, k; - - j = g_test_rand_int_range(0, sizeof(buf) - 1); - - buf[j] = '\0'; - - for (k = 0; k != j; k++) { - buf[k] = (char)g_test_rand_int_range(0, 256); - } - - v = visitor_input_test_init(data, buf); - visit_type_int(v, NULL, &ires, NULL); - - v = visitor_input_test_init(data, buf); - visit_type_intList(v, NULL, &ilres, NULL); - qapi_free_intList(ilres); - - v = visitor_input_test_init(data, buf); - visit_type_bool(v, NULL, &bres, NULL); - - v = visitor_input_test_init(data, buf); - visit_type_number(v, NULL, &nres, NULL); - - v = visitor_input_test_init(data, buf); - sres = NULL; - visit_type_str(v, NULL, &sres, NULL); - g_free(sres); - - v = visitor_input_test_init(data, buf); - visit_type_EnumOne(v, NULL, &eres, NULL); - } -} - -static void input_visitor_test_add(const char *testpath, - TestInputVisitorData *data, - void (*test_func)(TestInputVisitorData *data, const void *user_data)) -{ - g_test_add(testpath, TestInputVisitorData, data, NULL, test_func, - visitor_input_teardown); -} - -int main(int argc, char **argv) -{ - TestInputVisitorData in_visitor_data; - - g_test_init(&argc, &argv, NULL); - - input_visitor_test_add("/string-visitor/input/int", - &in_visitor_data, test_visitor_in_int); - input_visitor_test_add("/string-visitor/input/intList", - &in_visitor_data, test_visitor_in_intList); - input_visitor_test_add("/string-visitor/input/uintList", - &in_visitor_data, test_visitor_in_uintList); - input_visitor_test_add("/string-visitor/input/bool", - &in_visitor_data, test_visitor_in_bool); - input_visitor_test_add("/string-visitor/input/number", - &in_visitor_data, test_visitor_in_number); - input_visitor_test_add("/string-visitor/input/string", - &in_visitor_data, test_visitor_in_string); - input_visitor_test_add("/string-visitor/input/enum", - &in_visitor_data, test_visitor_in_enum); - input_visitor_test_add("/string-visitor/input/fuzz", - &in_visitor_data, test_visitor_in_fuzz); - - g_test_run(); - - return 0; -} diff --git a/tests/test-string-output-visitor.c b/tests/test-string-output-visitor.c deleted file mode 100644 index e2bedc5c7c..0000000000 --- a/tests/test-string-output-visitor.c +++ /dev/null @@ -1,248 +0,0 @@ -/* - * String Output Visitor unit-tests. - * - * Copyright (C) 2012 Red Hat Inc. - * - * Authors: - * Paolo Bonzini (based on test-qobject-output-visitor) - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" - -#include "qemu-common.h" -#include "qapi/error.h" -#include "qapi/string-output-visitor.h" -#include "test-qapi-visit.h" - -typedef struct TestOutputVisitorData { - Visitor *ov; - char *str; - bool human; -} TestOutputVisitorData; - -static void visitor_output_setup_internal(TestOutputVisitorData *data, - bool human) -{ - data->human = human; - data->ov = string_output_visitor_new(human, &data->str); - g_assert(data->ov); -} - -static void visitor_output_setup(TestOutputVisitorData *data, - const void *unused) -{ - return visitor_output_setup_internal(data, false); -} - -static void visitor_output_setup_human(TestOutputVisitorData *data, - const void *unused) -{ - return visitor_output_setup_internal(data, true); -} - -static void visitor_output_teardown(TestOutputVisitorData *data, - const void *unused) -{ - visit_free(data->ov); - data->ov = NULL; - g_free(data->str); - data->str = NULL; -} - -static char *visitor_get(TestOutputVisitorData *data) -{ - visit_complete(data->ov, &data->str); - g_assert(data->str); - return data->str; -} - -static void visitor_reset(TestOutputVisitorData *data) -{ - bool human = data->human; - - visitor_output_teardown(data, NULL); - visitor_output_setup_internal(data, human); -} - -static void test_visitor_out_int(TestOutputVisitorData *data, - const void *unused) -{ - int64_t value = 42; - char *str; - - visit_type_int(data->ov, NULL, &value, &error_abort); - - str = visitor_get(data); - if (data->human) { - g_assert_cmpstr(str, ==, "42 (0x2a)"); - } else { - g_assert_cmpstr(str, ==, "42"); - } -} - -static void test_visitor_out_intList(TestOutputVisitorData *data, - const void *unused) -{ - int64_t value[] = {0, 1, 9, 10, 16, 15, 14, - 3, 4, 5, 6, 11, 12, 13, 21, 22, INT64_MAX - 1, INT64_MAX}; - intList *list = NULL, **tail = &list; - int i; - Error *err = NULL; - char *str; - - for (i = 0; i < ARRAY_SIZE(value); i++) { - QAPI_LIST_APPEND(tail, value[i]); - } - - visit_type_intList(data->ov, NULL, &list, &err); - g_assert(err == NULL); - - str = visitor_get(data); - if (data->human) { - g_assert_cmpstr(str, ==, - "0-1,3-6,9-16,21-22,9223372036854775806-9223372036854775807 " - "(0x0-0x1,0x3-0x6,0x9-0x10,0x15-0x16," - "0x7ffffffffffffffe-0x7fffffffffffffff)"); - } else { - g_assert_cmpstr(str, ==, - "0-1,3-6,9-16,21-22,9223372036854775806-9223372036854775807"); - } - qapi_free_intList(list); -} - -static void test_visitor_out_bool(TestOutputVisitorData *data, - const void *unused) -{ - bool value = true; - char *str; - - visit_type_bool(data->ov, NULL, &value, &error_abort); - - str = visitor_get(data); - g_assert_cmpstr(str, ==, "true"); -} - -static void test_visitor_out_number(TestOutputVisitorData *data, - const void *unused) -{ - double value = 3.1415926535897932; - char *str; - - visit_type_number(data->ov, NULL, &value, &error_abort); - - str = visitor_get(data); - g_assert_cmpstr(str, ==, "3.1415926535897931"); -} - -static void test_visitor_out_string(TestOutputVisitorData *data, - const void *unused) -{ - char *string = (char *) "Q E M U"; - const char *string_human = "\"Q E M U\""; - char *str; - - visit_type_str(data->ov, NULL, &string, &error_abort); - - str = visitor_get(data); - if (data->human) { - g_assert_cmpstr(str, ==, string_human); - } else { - g_assert_cmpstr(str, ==, string); - } -} - -static void test_visitor_out_no_string(TestOutputVisitorData *data, - const void *unused) -{ - char *string = NULL; - char *str; - - /* A null string should return "" */ - visit_type_str(data->ov, NULL, &string, &error_abort); - - str = visitor_get(data); - if (data->human) { - g_assert_cmpstr(str, ==, ""); - } else { - g_assert_cmpstr(str, ==, ""); - } -} - -static void test_visitor_out_enum(TestOutputVisitorData *data, - const void *unused) -{ - char *str; - EnumOne i; - - for (i = 0; i < ENUM_ONE__MAX; i++) { - visit_type_EnumOne(data->ov, "unused", &i, &error_abort); - - str = visitor_get(data); - if (data->human) { - char *str_human = g_strdup_printf("\"%s\"", EnumOne_str(i)); - - g_assert_cmpstr(str, ==, str_human); - g_free(str_human); - } else { - g_assert_cmpstr(str, ==, EnumOne_str(i)); - } - visitor_reset(data); - } -} - -static void -output_visitor_test_add(const char *testpath, - TestOutputVisitorData *data, - void (*test_func)(TestOutputVisitorData *data, - const void *user_data), - bool human) -{ - g_test_add(testpath, TestOutputVisitorData, data, - human ? visitor_output_setup_human : visitor_output_setup, - test_func, visitor_output_teardown); -} - -int main(int argc, char **argv) -{ - TestOutputVisitorData out_visitor_data; - - g_test_init(&argc, &argv, NULL); - - output_visitor_test_add("/string-visitor/output/int", - &out_visitor_data, test_visitor_out_int, false); - output_visitor_test_add("/string-visitor/output/int-human", - &out_visitor_data, test_visitor_out_int, true); - output_visitor_test_add("/string-visitor/output/bool", - &out_visitor_data, test_visitor_out_bool, false); - output_visitor_test_add("/string-visitor/output/bool-human", - &out_visitor_data, test_visitor_out_bool, true); - output_visitor_test_add("/string-visitor/output/number", - &out_visitor_data, test_visitor_out_number, false); - output_visitor_test_add("/string-visitor/output/number-human", - &out_visitor_data, test_visitor_out_number, true); - output_visitor_test_add("/string-visitor/output/string", - &out_visitor_data, test_visitor_out_string, false); - output_visitor_test_add("/string-visitor/output/string-human", - &out_visitor_data, test_visitor_out_string, true); - output_visitor_test_add("/string-visitor/output/no-string", - &out_visitor_data, test_visitor_out_no_string, - false); - output_visitor_test_add("/string-visitor/output/no-string-human", - &out_visitor_data, test_visitor_out_no_string, - true); - output_visitor_test_add("/string-visitor/output/enum", - &out_visitor_data, test_visitor_out_enum, false); - output_visitor_test_add("/string-visitor/output/enum-human", - &out_visitor_data, test_visitor_out_enum, true); - output_visitor_test_add("/string-visitor/output/intList", - &out_visitor_data, test_visitor_out_intList, false); - output_visitor_test_add("/string-visitor/output/intList-human", - &out_visitor_data, test_visitor_out_intList, true); - - g_test_run(); - - return 0; -} diff --git a/tests/test-thread-pool.c b/tests/test-thread-pool.c deleted file mode 100644 index 70dc6314a1..0000000000 --- a/tests/test-thread-pool.c +++ /dev/null @@ -1,250 +0,0 @@ -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "block/aio.h" -#include "block/thread-pool.h" -#include "block/block.h" -#include "qapi/error.h" -#include "qemu/timer.h" -#include "qemu/error-report.h" -#include "qemu/main-loop.h" - -static AioContext *ctx; -static ThreadPool *pool; -static int active; - -typedef struct { - BlockAIOCB *aiocb; - int n; - int ret; -} WorkerTestData; - -static int worker_cb(void *opaque) -{ - WorkerTestData *data = opaque; - return qatomic_fetch_inc(&data->n); -} - -static int long_cb(void *opaque) -{ - WorkerTestData *data = opaque; - if (qatomic_cmpxchg(&data->n, 0, 1) == 0) { - g_usleep(2000000); - qatomic_or(&data->n, 2); - } - return 0; -} - -static void done_cb(void *opaque, int ret) -{ - WorkerTestData *data = opaque; - g_assert(data->ret == -EINPROGRESS || data->ret == -ECANCELED); - data->ret = ret; - data->aiocb = NULL; - - /* Callbacks are serialized, so no need to use atomic ops. */ - active--; -} - -static void test_submit(void) -{ - WorkerTestData data = { .n = 0 }; - thread_pool_submit(pool, worker_cb, &data); - while (data.n == 0) { - aio_poll(ctx, true); - } - g_assert_cmpint(data.n, ==, 1); -} - -static void test_submit_aio(void) -{ - WorkerTestData data = { .n = 0, .ret = -EINPROGRESS }; - data.aiocb = thread_pool_submit_aio(pool, worker_cb, &data, - done_cb, &data); - - /* The callbacks are not called until after the first wait. */ - active = 1; - g_assert_cmpint(data.ret, ==, -EINPROGRESS); - while (data.ret == -EINPROGRESS) { - aio_poll(ctx, true); - } - g_assert_cmpint(active, ==, 0); - g_assert_cmpint(data.n, ==, 1); - g_assert_cmpint(data.ret, ==, 0); -} - -static void co_test_cb(void *opaque) -{ - WorkerTestData *data = opaque; - - active = 1; - data->n = 0; - data->ret = -EINPROGRESS; - thread_pool_submit_co(pool, worker_cb, data); - - /* The test continues in test_submit_co, after qemu_coroutine_enter... */ - - g_assert_cmpint(data->n, ==, 1); - data->ret = 0; - active--; - - /* The test continues in test_submit_co, after aio_poll... */ -} - -static void test_submit_co(void) -{ - WorkerTestData data; - Coroutine *co = qemu_coroutine_create(co_test_cb, &data); - - qemu_coroutine_enter(co); - - /* Back here once the worker has started. */ - - g_assert_cmpint(active, ==, 1); - g_assert_cmpint(data.ret, ==, -EINPROGRESS); - - /* aio_poll will execute the rest of the coroutine. */ - - while (data.ret == -EINPROGRESS) { - aio_poll(ctx, true); - } - - /* Back here after the coroutine has finished. */ - - g_assert_cmpint(active, ==, 0); - g_assert_cmpint(data.ret, ==, 0); -} - -static void test_submit_many(void) -{ - WorkerTestData data[100]; - int i; - - /* Start more work items than there will be threads. */ - for (i = 0; i < 100; i++) { - data[i].n = 0; - data[i].ret = -EINPROGRESS; - thread_pool_submit_aio(pool, worker_cb, &data[i], done_cb, &data[i]); - } - - active = 100; - while (active > 0) { - aio_poll(ctx, true); - } - for (i = 0; i < 100; i++) { - g_assert_cmpint(data[i].n, ==, 1); - g_assert_cmpint(data[i].ret, ==, 0); - } -} - -static void do_test_cancel(bool sync) -{ - WorkerTestData data[100]; - int num_canceled; - int i; - - /* Start more work items than there will be threads, to ensure - * the pool is full. - */ - test_submit_many(); - - /* Start long running jobs, to ensure we can cancel some. */ - for (i = 0; i < 100; i++) { - data[i].n = 0; - data[i].ret = -EINPROGRESS; - data[i].aiocb = thread_pool_submit_aio(pool, long_cb, &data[i], - done_cb, &data[i]); - } - - /* Starting the threads may be left to a bottom half. Let it - * run, but do not waste too much time... - */ - active = 100; - aio_notify(ctx); - aio_poll(ctx, false); - - /* Wait some time for the threads to start, with some sanity - * testing on the behavior of the scheduler... - */ - g_assert_cmpint(active, ==, 100); - g_usleep(1000000); - g_assert_cmpint(active, >, 50); - - /* Cancel the jobs that haven't been started yet. */ - num_canceled = 0; - for (i = 0; i < 100; i++) { - if (qatomic_cmpxchg(&data[i].n, 0, 4) == 0) { - data[i].ret = -ECANCELED; - if (sync) { - bdrv_aio_cancel(data[i].aiocb); - } else { - bdrv_aio_cancel_async(data[i].aiocb); - } - num_canceled++; - } - } - g_assert_cmpint(active, >, 0); - g_assert_cmpint(num_canceled, <, 100); - - for (i = 0; i < 100; i++) { - if (data[i].aiocb && qatomic_read(&data[i].n) < 4) { - if (sync) { - /* Canceling the others will be a blocking operation. */ - bdrv_aio_cancel(data[i].aiocb); - } else { - bdrv_aio_cancel_async(data[i].aiocb); - } - } - } - - /* Finish execution and execute any remaining callbacks. */ - while (active > 0) { - aio_poll(ctx, true); - } - g_assert_cmpint(active, ==, 0); - for (i = 0; i < 100; i++) { - g_assert(data[i].aiocb == NULL); - switch (data[i].n) { - case 0: - fprintf(stderr, "Callback not canceled but never started?\n"); - abort(); - case 3: - /* Couldn't be canceled asynchronously, must have completed. */ - g_assert_cmpint(data[i].ret, ==, 0); - break; - case 4: - /* Could be canceled asynchronously, never started. */ - g_assert_cmpint(data[i].ret, ==, -ECANCELED); - break; - default: - fprintf(stderr, "Callback aborted while running?\n"); - abort(); - } - } -} - -static void test_cancel(void) -{ - do_test_cancel(true); -} - -static void test_cancel_async(void) -{ - do_test_cancel(false); -} - -int main(int argc, char **argv) -{ - qemu_init_main_loop(&error_abort); - ctx = qemu_get_current_aio_context(); - pool = aio_get_thread_pool(ctx); - - g_test_init(&argc, &argv, NULL); - g_test_add_func("/thread-pool/submit", test_submit); - g_test_add_func("/thread-pool/submit-aio", test_submit_aio); - g_test_add_func("/thread-pool/submit-co", test_submit_co); - g_test_add_func("/thread-pool/submit-many", test_submit_many); - g_test_add_func("/thread-pool/cancel", test_cancel); - g_test_add_func("/thread-pool/cancel-async", test_cancel_async); - - return g_test_run(); -} diff --git a/tests/test-throttle.c b/tests/test-throttle.c deleted file mode 100644 index 7adb5e6652..0000000000 --- a/tests/test-throttle.c +++ /dev/null @@ -1,770 +0,0 @@ -/* - * Throttle infrastructure tests - * - * Copyright Nodalink, EURL. 2013-2014 - * Copyright Igalia, S.L. 2015 - * - * Authors: - * Benoît Canet - * Alberto Garcia - * - * This work is licensed under the terms of the GNU LGPL, version 2 or later. - * See the COPYING.LIB file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include -#include "block/aio.h" -#include "qapi/error.h" -#include "qemu/throttle.h" -#include "qemu/error-report.h" -#include "qemu/main-loop.h" -#include "qemu/module.h" -#include "block/throttle-groups.h" -#include "sysemu/block-backend.h" - -static AioContext *ctx; -static LeakyBucket bkt; -static ThrottleConfig cfg; -static ThrottleGroupMember tgm; -static ThrottleState ts; -static ThrottleTimers *tt; - -/* useful function */ -static bool double_cmp(double x, double y) -{ - return fabsl(x - y) < 1e-6; -} - -/* tests for single bucket operations */ -static void test_leak_bucket(void) -{ - throttle_config_init(&cfg); - bkt = cfg.buckets[THROTTLE_BPS_TOTAL]; - - /* set initial value */ - bkt.avg = 150; - bkt.max = 15; - bkt.level = 1.5; - - /* leak an op work of time */ - throttle_leak_bucket(&bkt, NANOSECONDS_PER_SECOND / 150); - g_assert(bkt.avg == 150); - g_assert(bkt.max == 15); - g_assert(double_cmp(bkt.level, 0.5)); - - /* leak again emptying the bucket */ - throttle_leak_bucket(&bkt, NANOSECONDS_PER_SECOND / 150); - g_assert(bkt.avg == 150); - g_assert(bkt.max == 15); - g_assert(double_cmp(bkt.level, 0)); - - /* check that the bucket level won't go lower */ - throttle_leak_bucket(&bkt, NANOSECONDS_PER_SECOND / 150); - g_assert(bkt.avg == 150); - g_assert(bkt.max == 15); - g_assert(double_cmp(bkt.level, 0)); - - /* check that burst_level leaks correctly */ - bkt.burst_level = 6; - bkt.max = 250; - bkt.burst_length = 2; /* otherwise burst_level will not leak */ - throttle_leak_bucket(&bkt, NANOSECONDS_PER_SECOND / 100); - g_assert(double_cmp(bkt.burst_level, 3.5)); - - throttle_leak_bucket(&bkt, NANOSECONDS_PER_SECOND / 100); - g_assert(double_cmp(bkt.burst_level, 1)); - - throttle_leak_bucket(&bkt, NANOSECONDS_PER_SECOND / 100); - g_assert(double_cmp(bkt.burst_level, 0)); - - throttle_leak_bucket(&bkt, NANOSECONDS_PER_SECOND / 100); - g_assert(double_cmp(bkt.burst_level, 0)); -} - -static void test_compute_wait(void) -{ - unsigned i; - int64_t wait; - int64_t result; - - throttle_config_init(&cfg); - bkt = cfg.buckets[THROTTLE_BPS_TOTAL]; - - /* no operation limit set */ - bkt.avg = 0; - bkt.max = 15; - bkt.level = 1.5; - wait = throttle_compute_wait(&bkt); - g_assert(!wait); - - /* zero delta */ - bkt.avg = 150; - bkt.max = 15; - bkt.level = 15; - wait = throttle_compute_wait(&bkt); - g_assert(!wait); - - /* below zero delta */ - bkt.avg = 150; - bkt.max = 15; - bkt.level = 9; - wait = throttle_compute_wait(&bkt); - g_assert(!wait); - - /* half an operation above max */ - bkt.avg = 150; - bkt.max = 15; - bkt.level = 15.5; - wait = throttle_compute_wait(&bkt); - /* time required to do half an operation */ - result = (int64_t) NANOSECONDS_PER_SECOND / 150 / 2; - g_assert(wait == result); - - /* Perform I/O for 2.2 seconds at a rate of bkt.max */ - bkt.burst_length = 2; - bkt.level = 0; - bkt.avg = 10; - bkt.max = 200; - for (i = 0; i < 22; i++) { - double units = bkt.max / 10; - bkt.level += units; - bkt.burst_level += units; - throttle_leak_bucket(&bkt, NANOSECONDS_PER_SECOND / 10); - wait = throttle_compute_wait(&bkt); - g_assert(double_cmp(bkt.burst_level, 0)); - g_assert(double_cmp(bkt.level, (i + 1) * (bkt.max - bkt.avg) / 10)); - /* We can do bursts for the 2 seconds we have configured in - * burst_length. We have 100 extra miliseconds of burst - * because bkt.level has been leaking during this time. - * After that, we have to wait. */ - result = i < 21 ? 0 : 1.8 * NANOSECONDS_PER_SECOND; - g_assert(wait == result); - } -} - -/* functions to test ThrottleState initialization/destroy methods */ -static void read_timer_cb(void *opaque) -{ -} - -static void write_timer_cb(void *opaque) -{ -} - -static void test_init(void) -{ - int i; - - tt = &tgm.throttle_timers; - - /* fill the structures with crap */ - memset(&ts, 1, sizeof(ts)); - memset(tt, 1, sizeof(*tt)); - - /* init structures */ - throttle_init(&ts); - throttle_timers_init(tt, ctx, QEMU_CLOCK_VIRTUAL, - read_timer_cb, write_timer_cb, &ts); - - /* check initialized fields */ - g_assert(tt->clock_type == QEMU_CLOCK_VIRTUAL); - g_assert(tt->timers[0]); - g_assert(tt->timers[1]); - - /* check other fields where cleared */ - g_assert(!ts.previous_leak); - g_assert(!ts.cfg.op_size); - for (i = 0; i < BUCKETS_COUNT; i++) { - g_assert(!ts.cfg.buckets[i].avg); - g_assert(!ts.cfg.buckets[i].max); - g_assert(!ts.cfg.buckets[i].level); - } - - throttle_timers_destroy(tt); -} - -static void test_destroy(void) -{ - int i; - throttle_init(&ts); - throttle_timers_init(tt, ctx, QEMU_CLOCK_VIRTUAL, - read_timer_cb, write_timer_cb, &ts); - throttle_timers_destroy(tt); - for (i = 0; i < 2; i++) { - g_assert(!tt->timers[i]); - } -} - -/* function to test throttle_config and throttle_get_config */ -static void test_config_functions(void) -{ - int i; - ThrottleConfig orig_cfg, final_cfg; - - orig_cfg.buckets[THROTTLE_BPS_TOTAL].avg = 153; - orig_cfg.buckets[THROTTLE_BPS_READ].avg = 56; - orig_cfg.buckets[THROTTLE_BPS_WRITE].avg = 1; - - orig_cfg.buckets[THROTTLE_OPS_TOTAL].avg = 150; - orig_cfg.buckets[THROTTLE_OPS_READ].avg = 69; - orig_cfg.buckets[THROTTLE_OPS_WRITE].avg = 23; - - orig_cfg.buckets[THROTTLE_BPS_TOTAL].max = 0; - orig_cfg.buckets[THROTTLE_BPS_READ].max = 56; - orig_cfg.buckets[THROTTLE_BPS_WRITE].max = 120; - - orig_cfg.buckets[THROTTLE_OPS_TOTAL].max = 150; - orig_cfg.buckets[THROTTLE_OPS_READ].max = 400; - orig_cfg.buckets[THROTTLE_OPS_WRITE].max = 500; - - orig_cfg.buckets[THROTTLE_BPS_TOTAL].level = 45; - orig_cfg.buckets[THROTTLE_BPS_READ].level = 65; - orig_cfg.buckets[THROTTLE_BPS_WRITE].level = 23; - - orig_cfg.buckets[THROTTLE_OPS_TOTAL].level = 1; - orig_cfg.buckets[THROTTLE_OPS_READ].level = 90; - orig_cfg.buckets[THROTTLE_OPS_WRITE].level = 75; - - orig_cfg.op_size = 1; - - throttle_init(&ts); - throttle_timers_init(tt, ctx, QEMU_CLOCK_VIRTUAL, - read_timer_cb, write_timer_cb, &ts); - /* structure reset by throttle_init previous_leak should be null */ - g_assert(!ts.previous_leak); - throttle_config(&ts, QEMU_CLOCK_VIRTUAL, &orig_cfg); - - /* has previous leak been initialized by throttle_config ? */ - g_assert(ts.previous_leak); - - /* get back the fixed configuration */ - throttle_get_config(&ts, &final_cfg); - - throttle_timers_destroy(tt); - - g_assert(final_cfg.buckets[THROTTLE_BPS_TOTAL].avg == 153); - g_assert(final_cfg.buckets[THROTTLE_BPS_READ].avg == 56); - g_assert(final_cfg.buckets[THROTTLE_BPS_WRITE].avg == 1); - - g_assert(final_cfg.buckets[THROTTLE_OPS_TOTAL].avg == 150); - g_assert(final_cfg.buckets[THROTTLE_OPS_READ].avg == 69); - g_assert(final_cfg.buckets[THROTTLE_OPS_WRITE].avg == 23); - - g_assert(final_cfg.buckets[THROTTLE_BPS_TOTAL].max == 0); - g_assert(final_cfg.buckets[THROTTLE_BPS_READ].max == 56); - g_assert(final_cfg.buckets[THROTTLE_BPS_WRITE].max == 120); - - g_assert(final_cfg.buckets[THROTTLE_OPS_TOTAL].max == 150); - g_assert(final_cfg.buckets[THROTTLE_OPS_READ].max == 400); - g_assert(final_cfg.buckets[THROTTLE_OPS_WRITE].max == 500); - - g_assert(final_cfg.op_size == 1); - - /* check bucket have been cleared */ - for (i = 0; i < BUCKETS_COUNT; i++) { - g_assert(!final_cfg.buckets[i].level); - } -} - -/* functions to test is throttle is enabled by a config */ -static void set_cfg_value(bool is_max, int index, int value) -{ - if (is_max) { - cfg.buckets[index].max = value; - /* If max is set, avg should never be 0 */ - cfg.buckets[index].avg = MAX(cfg.buckets[index].avg, 1); - } else { - cfg.buckets[index].avg = value; - } -} - -static void test_enabled(void) -{ - int i; - - throttle_config_init(&cfg); - g_assert(!throttle_enabled(&cfg)); - - for (i = 0; i < BUCKETS_COUNT; i++) { - throttle_config_init(&cfg); - set_cfg_value(false, i, 150); - g_assert(throttle_is_valid(&cfg, NULL)); - g_assert(throttle_enabled(&cfg)); - } - - for (i = 0; i < BUCKETS_COUNT; i++) { - throttle_config_init(&cfg); - set_cfg_value(false, i, -150); - g_assert(!throttle_is_valid(&cfg, NULL)); - } -} - -/* tests functions for throttle_conflicting */ - -static void test_conflicts_for_one_set(bool is_max, - int total, - int read, - int write) -{ - throttle_config_init(&cfg); - g_assert(throttle_is_valid(&cfg, NULL)); - - set_cfg_value(is_max, total, 1); - set_cfg_value(is_max, read, 1); - g_assert(!throttle_is_valid(&cfg, NULL)); - - throttle_config_init(&cfg); - set_cfg_value(is_max, total, 1); - set_cfg_value(is_max, write, 1); - g_assert(!throttle_is_valid(&cfg, NULL)); - - throttle_config_init(&cfg); - set_cfg_value(is_max, total, 1); - set_cfg_value(is_max, read, 1); - set_cfg_value(is_max, write, 1); - g_assert(!throttle_is_valid(&cfg, NULL)); - - throttle_config_init(&cfg); - set_cfg_value(is_max, total, 1); - g_assert(throttle_is_valid(&cfg, NULL)); - - throttle_config_init(&cfg); - set_cfg_value(is_max, read, 1); - set_cfg_value(is_max, write, 1); - g_assert(throttle_is_valid(&cfg, NULL)); -} - -static void test_conflicting_config(void) -{ - /* bps average conflicts */ - test_conflicts_for_one_set(false, - THROTTLE_BPS_TOTAL, - THROTTLE_BPS_READ, - THROTTLE_BPS_WRITE); - - /* ops average conflicts */ - test_conflicts_for_one_set(false, - THROTTLE_OPS_TOTAL, - THROTTLE_OPS_READ, - THROTTLE_OPS_WRITE); - - /* bps average conflicts */ - test_conflicts_for_one_set(true, - THROTTLE_BPS_TOTAL, - THROTTLE_BPS_READ, - THROTTLE_BPS_WRITE); - /* ops average conflicts */ - test_conflicts_for_one_set(true, - THROTTLE_OPS_TOTAL, - THROTTLE_OPS_READ, - THROTTLE_OPS_WRITE); -} -/* functions to test the throttle_is_valid function */ -static void test_is_valid_for_value(int value, bool should_be_valid) -{ - int is_max, index; - for (is_max = 0; is_max < 2; is_max++) { - for (index = 0; index < BUCKETS_COUNT; index++) { - throttle_config_init(&cfg); - set_cfg_value(is_max, index, value); - g_assert(throttle_is_valid(&cfg, NULL) == should_be_valid); - } - } -} - -static void test_is_valid(void) -{ - /* negative number are invalid */ - test_is_valid_for_value(-1, false); - /* zero are valids */ - test_is_valid_for_value(0, true); - /* positives numers are valids */ - test_is_valid_for_value(1, true); -} - -static void test_ranges(void) -{ - int i; - - for (i = 0; i < BUCKETS_COUNT; i++) { - LeakyBucket *b = &cfg.buckets[i]; - throttle_config_init(&cfg); - - /* avg = 0 means throttling is disabled, but the config is valid */ - b->avg = 0; - g_assert(throttle_is_valid(&cfg, NULL)); - g_assert(!throttle_enabled(&cfg)); - - /* These are valid configurations (values <= THROTTLE_VALUE_MAX) */ - b->avg = 1; - g_assert(throttle_is_valid(&cfg, NULL)); - - b->avg = THROTTLE_VALUE_MAX; - g_assert(throttle_is_valid(&cfg, NULL)); - - b->avg = THROTTLE_VALUE_MAX; - b->max = THROTTLE_VALUE_MAX; - g_assert(throttle_is_valid(&cfg, NULL)); - - /* Values over THROTTLE_VALUE_MAX are not allowed */ - b->avg = THROTTLE_VALUE_MAX + 1; - g_assert(!throttle_is_valid(&cfg, NULL)); - - b->avg = THROTTLE_VALUE_MAX; - b->max = THROTTLE_VALUE_MAX + 1; - g_assert(!throttle_is_valid(&cfg, NULL)); - - /* burst_length must be between 1 and THROTTLE_VALUE_MAX */ - b->avg = 1; - b->max = 1; - b->burst_length = 0; - g_assert(!throttle_is_valid(&cfg, NULL)); - - b->avg = 1; - b->max = 1; - b->burst_length = 1; - g_assert(throttle_is_valid(&cfg, NULL)); - - b->avg = 1; - b->max = 1; - b->burst_length = THROTTLE_VALUE_MAX; - g_assert(throttle_is_valid(&cfg, NULL)); - - b->avg = 1; - b->max = 1; - b->burst_length = THROTTLE_VALUE_MAX + 1; - g_assert(!throttle_is_valid(&cfg, NULL)); - - /* burst_length * max cannot exceed THROTTLE_VALUE_MAX */ - b->avg = 1; - b->max = 2; - b->burst_length = THROTTLE_VALUE_MAX / 2; - g_assert(throttle_is_valid(&cfg, NULL)); - - b->avg = 1; - b->max = 3; - b->burst_length = THROTTLE_VALUE_MAX / 2; - g_assert(!throttle_is_valid(&cfg, NULL)); - - b->avg = 1; - b->max = THROTTLE_VALUE_MAX; - b->burst_length = 1; - g_assert(throttle_is_valid(&cfg, NULL)); - - b->avg = 1; - b->max = THROTTLE_VALUE_MAX; - b->burst_length = 2; - g_assert(!throttle_is_valid(&cfg, NULL)); - } -} - -static void test_max_is_missing_limit(void) -{ - int i; - - for (i = 0; i < BUCKETS_COUNT; i++) { - throttle_config_init(&cfg); - cfg.buckets[i].max = 100; - cfg.buckets[i].avg = 0; - g_assert(!throttle_is_valid(&cfg, NULL)); - - cfg.buckets[i].max = 0; - cfg.buckets[i].avg = 0; - g_assert(throttle_is_valid(&cfg, NULL)); - - cfg.buckets[i].max = 0; - cfg.buckets[i].avg = 100; - g_assert(throttle_is_valid(&cfg, NULL)); - - cfg.buckets[i].max = 30; - cfg.buckets[i].avg = 100; - g_assert(!throttle_is_valid(&cfg, NULL)); - - cfg.buckets[i].max = 100; - cfg.buckets[i].avg = 100; - g_assert(throttle_is_valid(&cfg, NULL)); - } -} - -static void test_iops_size_is_missing_limit(void) -{ - /* A total/read/write iops limit is required */ - throttle_config_init(&cfg); - cfg.op_size = 4096; - g_assert(!throttle_is_valid(&cfg, NULL)); -} - -static void test_have_timer(void) -{ - /* zero structures */ - memset(&ts, 0, sizeof(ts)); - memset(tt, 0, sizeof(*tt)); - - /* no timer set should return false */ - g_assert(!throttle_timers_are_initialized(tt)); - - /* init structures */ - throttle_init(&ts); - throttle_timers_init(tt, ctx, QEMU_CLOCK_VIRTUAL, - read_timer_cb, write_timer_cb, &ts); - - /* timer set by init should return true */ - g_assert(throttle_timers_are_initialized(tt)); - - throttle_timers_destroy(tt); -} - -static void test_detach_attach(void) -{ - /* zero structures */ - memset(&ts, 0, sizeof(ts)); - memset(tt, 0, sizeof(*tt)); - - /* init the structure */ - throttle_init(&ts); - throttle_timers_init(tt, ctx, QEMU_CLOCK_VIRTUAL, - read_timer_cb, write_timer_cb, &ts); - - /* timer set by init should return true */ - g_assert(throttle_timers_are_initialized(tt)); - - /* timer should no longer exist after detaching */ - throttle_timers_detach_aio_context(tt); - g_assert(!throttle_timers_are_initialized(tt)); - - /* timer should exist again after attaching */ - throttle_timers_attach_aio_context(tt, ctx); - g_assert(throttle_timers_are_initialized(tt)); - - throttle_timers_destroy(tt); -} - -static bool do_test_accounting(bool is_ops, /* are we testing bps or ops */ - int size, /* size of the operation to do */ - double avg, /* io limit */ - uint64_t op_size, /* ideal size of an io */ - double total_result, - double read_result, - double write_result) -{ - BucketType to_test[2][3] = { { THROTTLE_BPS_TOTAL, - THROTTLE_BPS_READ, - THROTTLE_BPS_WRITE, }, - { THROTTLE_OPS_TOTAL, - THROTTLE_OPS_READ, - THROTTLE_OPS_WRITE, } }; - ThrottleConfig cfg; - BucketType index; - int i; - - throttle_config_init(&cfg); - - for (i = 0; i < 3; i++) { - BucketType index = to_test[is_ops][i]; - cfg.buckets[index].avg = avg; - } - - cfg.op_size = op_size; - - throttle_init(&ts); - throttle_timers_init(tt, ctx, QEMU_CLOCK_VIRTUAL, - read_timer_cb, write_timer_cb, &ts); - throttle_config(&ts, QEMU_CLOCK_VIRTUAL, &cfg); - - /* account a read */ - throttle_account(&ts, false, size); - /* account a write */ - throttle_account(&ts, true, size); - - /* check total result */ - index = to_test[is_ops][0]; - if (!double_cmp(ts.cfg.buckets[index].level, total_result)) { - return false; - } - - /* check read result */ - index = to_test[is_ops][1]; - if (!double_cmp(ts.cfg.buckets[index].level, read_result)) { - return false; - } - - /* check write result */ - index = to_test[is_ops][2]; - if (!double_cmp(ts.cfg.buckets[index].level, write_result)) { - return false; - } - - throttle_timers_destroy(tt); - - return true; -} - -static void test_accounting(void) -{ - /* tests for bps */ - - /* op of size 1 */ - g_assert(do_test_accounting(false, - 1 * 512, - 150, - 0, - 1024, - 512, - 512)); - - /* op of size 2 */ - g_assert(do_test_accounting(false, - 2 * 512, - 150, - 0, - 2048, - 1024, - 1024)); - - /* op of size 2 and orthogonal parameter change */ - g_assert(do_test_accounting(false, - 2 * 512, - 150, - 17, - 2048, - 1024, - 1024)); - - - /* tests for ops */ - - /* op of size 1 */ - g_assert(do_test_accounting(true, - 1 * 512, - 150, - 0, - 2, - 1, - 1)); - - /* op of size 2 */ - g_assert(do_test_accounting(true, - 2 * 512, - 150, - 0, - 2, - 1, - 1)); - - /* jumbo op accounting fragmentation : size 64 with op size of 13 units */ - g_assert(do_test_accounting(true, - 64 * 512, - 150, - 13 * 512, - (64.0 * 2) / 13, - (64.0 / 13), - (64.0 / 13))); - - /* same with orthogonal parameters changes */ - g_assert(do_test_accounting(true, - 64 * 512, - 300, - 13 * 512, - (64.0 * 2) / 13, - (64.0 / 13), - (64.0 / 13))); -} - -static void test_groups(void) -{ - ThrottleConfig cfg1, cfg2; - BlockBackend *blk1, *blk2, *blk3; - BlockBackendPublic *blkp1, *blkp2, *blkp3; - ThrottleGroupMember *tgm1, *tgm2, *tgm3; - - /* No actual I/O is performed on these devices */ - blk1 = blk_new(qemu_get_aio_context(), 0, BLK_PERM_ALL); - blk2 = blk_new(qemu_get_aio_context(), 0, BLK_PERM_ALL); - blk3 = blk_new(qemu_get_aio_context(), 0, BLK_PERM_ALL); - - blkp1 = blk_get_public(blk1); - blkp2 = blk_get_public(blk2); - blkp3 = blk_get_public(blk3); - - tgm1 = &blkp1->throttle_group_member; - tgm2 = &blkp2->throttle_group_member; - tgm3 = &blkp3->throttle_group_member; - - g_assert(tgm1->throttle_state == NULL); - g_assert(tgm2->throttle_state == NULL); - g_assert(tgm3->throttle_state == NULL); - - throttle_group_register_tgm(tgm1, "bar", blk_get_aio_context(blk1)); - throttle_group_register_tgm(tgm2, "foo", blk_get_aio_context(blk2)); - throttle_group_register_tgm(tgm3, "bar", blk_get_aio_context(blk3)); - - g_assert(tgm1->throttle_state != NULL); - g_assert(tgm2->throttle_state != NULL); - g_assert(tgm3->throttle_state != NULL); - - g_assert(!strcmp(throttle_group_get_name(tgm1), "bar")); - g_assert(!strcmp(throttle_group_get_name(tgm2), "foo")); - g_assert(tgm1->throttle_state == tgm3->throttle_state); - - /* Setting the config of a group member affects the whole group */ - throttle_config_init(&cfg1); - cfg1.buckets[THROTTLE_BPS_READ].avg = 500000; - cfg1.buckets[THROTTLE_BPS_WRITE].avg = 285000; - cfg1.buckets[THROTTLE_OPS_READ].avg = 20000; - cfg1.buckets[THROTTLE_OPS_WRITE].avg = 12000; - throttle_group_config(tgm1, &cfg1); - - throttle_group_get_config(tgm1, &cfg1); - throttle_group_get_config(tgm3, &cfg2); - g_assert(!memcmp(&cfg1, &cfg2, sizeof(cfg1))); - - cfg2.buckets[THROTTLE_BPS_READ].avg = 4547; - cfg2.buckets[THROTTLE_BPS_WRITE].avg = 1349; - cfg2.buckets[THROTTLE_OPS_READ].avg = 123; - cfg2.buckets[THROTTLE_OPS_WRITE].avg = 86; - throttle_group_config(tgm3, &cfg1); - - throttle_group_get_config(tgm1, &cfg1); - throttle_group_get_config(tgm3, &cfg2); - g_assert(!memcmp(&cfg1, &cfg2, sizeof(cfg1))); - - throttle_group_unregister_tgm(tgm1); - throttle_group_unregister_tgm(tgm2); - throttle_group_unregister_tgm(tgm3); - - g_assert(tgm1->throttle_state == NULL); - g_assert(tgm2->throttle_state == NULL); - g_assert(tgm3->throttle_state == NULL); -} - -int main(int argc, char **argv) -{ - qemu_init_main_loop(&error_fatal); - ctx = qemu_get_aio_context(); - bdrv_init(); - module_call_init(MODULE_INIT_QOM); - - do {} while (g_main_context_iteration(NULL, false)); - - /* tests in the same order as the header function declarations */ - g_test_init(&argc, &argv, NULL); - g_test_add_func("/throttle/leak_bucket", test_leak_bucket); - g_test_add_func("/throttle/compute_wait", test_compute_wait); - g_test_add_func("/throttle/init", test_init); - g_test_add_func("/throttle/destroy", test_destroy); - g_test_add_func("/throttle/have_timer", test_have_timer); - g_test_add_func("/throttle/detach_attach", test_detach_attach); - g_test_add_func("/throttle/config/enabled", test_enabled); - g_test_add_func("/throttle/config/conflicting", test_conflicting_config); - g_test_add_func("/throttle/config/is_valid", test_is_valid); - g_test_add_func("/throttle/config/ranges", test_ranges); - g_test_add_func("/throttle/config/max", test_max_is_missing_limit); - g_test_add_func("/throttle/config/iops_size", - test_iops_size_is_missing_limit); - g_test_add_func("/throttle/config_functions", test_config_functions); - g_test_add_func("/throttle/accounting", test_accounting); - g_test_add_func("/throttle/groups", test_groups); - return g_test_run(); -} - diff --git a/tests/test-timed-average.c b/tests/test-timed-average.c deleted file mode 100644 index 82c92500df..0000000000 --- a/tests/test-timed-average.c +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Timed average computation tests - * - * Copyright Nodalink, EURL. 2014 - * - * Authors: - * Benoît Canet - * - * This work is licensed under the terms of the GNU LGPL, version 2 or later. - * See the COPYING.LIB file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "sysemu/cpu-timers.h" -#include "qemu/timed-average.h" - -/* This is the clock for QEMU_CLOCK_VIRTUAL */ -static int64_t my_clock_value; - -int64_t cpu_get_clock(void) -{ - return my_clock_value; -} - -static void account(TimedAverage *ta) -{ - timed_average_account(ta, 1); - timed_average_account(ta, 5); - timed_average_account(ta, 2); - timed_average_account(ta, 4); - timed_average_account(ta, 3); -} - -static void test_average(void) -{ - TimedAverage ta; - uint64_t result; - int i; - - /* we will compute some average on a period of 1 second */ - timed_average_init(&ta, QEMU_CLOCK_VIRTUAL, NANOSECONDS_PER_SECOND); - - result = timed_average_min(&ta); - g_assert(result == 0); - result = timed_average_avg(&ta); - g_assert(result == 0); - result = timed_average_max(&ta); - g_assert(result == 0); - - for (i = 0; i < 100; i++) { - account(&ta); - result = timed_average_min(&ta); - g_assert(result == 1); - result = timed_average_avg(&ta); - g_assert(result == 3); - result = timed_average_max(&ta); - g_assert(result == 5); - my_clock_value += NANOSECONDS_PER_SECOND / 10; - } - - my_clock_value += NANOSECONDS_PER_SECOND * 100; - - result = timed_average_min(&ta); - g_assert(result == 0); - result = timed_average_avg(&ta); - g_assert(result == 0); - result = timed_average_max(&ta); - g_assert(result == 0); - - for (i = 0; i < 100; i++) { - account(&ta); - result = timed_average_min(&ta); - g_assert(result == 1); - result = timed_average_avg(&ta); - g_assert(result == 3); - result = timed_average_max(&ta); - g_assert(result == 5); - my_clock_value += NANOSECONDS_PER_SECOND / 10; - } -} - -int main(int argc, char **argv) -{ - /* tests in the same order as the header function declarations */ - g_test_init(&argc, &argv, NULL); - g_test_add_func("/timed-average/average", test_average); - return g_test_run(); -} - diff --git a/tests/test-util-filemonitor.c b/tests/test-util-filemonitor.c deleted file mode 100644 index b629e10857..0000000000 --- a/tests/test-util-filemonitor.c +++ /dev/null @@ -1,720 +0,0 @@ -/* - * Tests for util/filemonitor-*.c - * - * Copyright 2018 Red Hat, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this library; if not, see . - * - */ - -#include "qemu/osdep.h" -#include "qemu/main-loop.h" -#include "qapi/error.h" -#include "qemu/filemonitor.h" - -#include - -#include - -enum { - QFILE_MONITOR_TEST_OP_ADD_WATCH, - QFILE_MONITOR_TEST_OP_DEL_WATCH, - QFILE_MONITOR_TEST_OP_EVENT, - QFILE_MONITOR_TEST_OP_CREATE, - QFILE_MONITOR_TEST_OP_APPEND, - QFILE_MONITOR_TEST_OP_TRUNC, - QFILE_MONITOR_TEST_OP_RENAME, - QFILE_MONITOR_TEST_OP_TOUCH, - QFILE_MONITOR_TEST_OP_UNLINK, - QFILE_MONITOR_TEST_OP_MKDIR, - QFILE_MONITOR_TEST_OP_RMDIR, -}; - -typedef struct { - int type; - const char *filesrc; - const char *filedst; - int64_t *watchid; - int eventid; - /* - * Only valid with OP_EVENT - this event might be - * swapped with the next OP_EVENT - */ - bool swapnext; -} QFileMonitorTestOp; - -typedef struct { - int64_t id; - QFileMonitorEvent event; - char *filename; -} QFileMonitorTestRecord; - - -typedef struct { - QemuMutex lock; - GList *records; -} QFileMonitorTestData; - -static QemuMutex evlock; -static bool evstopping; -static bool evrunning; -static bool debug; - -/* - * Main function for a background thread that is - * running the event loop during the test - */ -static void * -qemu_file_monitor_test_event_loop(void *opaque G_GNUC_UNUSED) -{ - qemu_mutex_lock(&evlock); - - while (!evstopping) { - qemu_mutex_unlock(&evlock); - main_loop_wait(true); - qemu_mutex_lock(&evlock); - } - - evrunning = false; - qemu_mutex_unlock(&evlock); - return NULL; -} - - -/* - * File monitor event handler which simply maintains - * an ordered list of all events that it receives - */ -static void -qemu_file_monitor_test_handler(int64_t id, - QFileMonitorEvent event, - const char *filename, - void *opaque) -{ - QFileMonitorTestData *data = opaque; - QFileMonitorTestRecord *rec = g_new0(QFileMonitorTestRecord, 1); - - if (debug) { - g_printerr("Queue event id %" PRIx64 " event %d file %s\n", - id, event, filename); - } - rec->id = id; - rec->event = event; - rec->filename = g_strdup(filename); - - qemu_mutex_lock(&data->lock); - data->records = g_list_append(data->records, rec); - qemu_mutex_unlock(&data->lock); -} - - -static void -qemu_file_monitor_test_record_free(QFileMonitorTestRecord *rec) -{ - g_free(rec->filename); - g_free(rec); -} - - -/* - * Get the next event record that has been received by - * the file monitor event handler. Since events are - * emitted in the background thread running the event - * loop, we can't assume there is a record available - * immediately. Thus we will sleep for upto 5 seconds - * to wait for the event to be queued for us. - */ -static QFileMonitorTestRecord * -qemu_file_monitor_test_next_record(QFileMonitorTestData *data, - QFileMonitorTestRecord *pushback) -{ - GTimer *timer = g_timer_new(); - QFileMonitorTestRecord *record = NULL; - GList *tmp; - - qemu_mutex_lock(&data->lock); - while (!data->records && g_timer_elapsed(timer, NULL) < 5) { - qemu_mutex_unlock(&data->lock); - usleep(10 * 1000); - qemu_mutex_lock(&data->lock); - } - if (data->records) { - record = data->records->data; - if (pushback) { - data->records->data = pushback; - } else { - tmp = data->records; - data->records = g_list_remove_link(data->records, tmp); - g_list_free(tmp); - } - } else if (pushback) { - qemu_file_monitor_test_record_free(pushback); - } - qemu_mutex_unlock(&data->lock); - - g_timer_destroy(timer); - return record; -} - - -/* - * Check whether the event record we retrieved matches - * data we were expecting to see for the event - */ -static bool -qemu_file_monitor_test_expect(QFileMonitorTestData *data, - int64_t id, - QFileMonitorEvent event, - const char *filename, - bool swapnext) -{ - QFileMonitorTestRecord *rec; - bool ret = false; - - rec = qemu_file_monitor_test_next_record(data, NULL); - - retry: - if (!rec) { - g_printerr("Missing event watch id %" PRIx64 " event %d file %s\n", - id, event, filename); - return false; - } - - if (id != rec->id) { - if (swapnext) { - rec = qemu_file_monitor_test_next_record(data, rec); - swapnext = false; - goto retry; - } - g_printerr("Expected watch id %" PRIx64 " but got %" PRIx64 "\n", - id, rec->id); - goto cleanup; - } - - if (event != rec->event) { - g_printerr("Expected event %d but got %d\n", event, rec->event); - goto cleanup; - } - - if (!g_str_equal(filename, rec->filename)) { - g_printerr("Expected filename %s but got %s\n", - filename, rec->filename); - goto cleanup; - } - - ret = true; - - cleanup: - qemu_file_monitor_test_record_free(rec); - return ret; -} - - -static void -test_file_monitor_events(void) -{ - int64_t watch0 = 0; - int64_t watch1 = 0; - int64_t watch2 = 0; - int64_t watch3 = 0; - int64_t watch4 = 0; - int64_t watch5 = 0; - QFileMonitorTestOp ops[] = { - { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH, - .filesrc = NULL, .watchid = &watch0 }, - { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH, - .filesrc = "one.txt", .watchid = &watch1 }, - { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH, - .filesrc = "two.txt", .watchid = &watch2 }, - - - { .type = QFILE_MONITOR_TEST_OP_CREATE, - .filesrc = "one.txt", }, - { .type = QFILE_MONITOR_TEST_OP_EVENT, - .filesrc = "one.txt", .watchid = &watch0, - .eventid = QFILE_MONITOR_EVENT_CREATED }, - { .type = QFILE_MONITOR_TEST_OP_EVENT, - .filesrc = "one.txt", .watchid = &watch1, - .eventid = QFILE_MONITOR_EVENT_CREATED }, - - - { .type = QFILE_MONITOR_TEST_OP_CREATE, - .filesrc = "two.txt", }, - { .type = QFILE_MONITOR_TEST_OP_EVENT, - .filesrc = "two.txt", .watchid = &watch0, - .eventid = QFILE_MONITOR_EVENT_CREATED }, - { .type = QFILE_MONITOR_TEST_OP_EVENT, - .filesrc = "two.txt", .watchid = &watch2, - .eventid = QFILE_MONITOR_EVENT_CREATED }, - - - { .type = QFILE_MONITOR_TEST_OP_CREATE, - .filesrc = "three.txt", }, - { .type = QFILE_MONITOR_TEST_OP_EVENT, - .filesrc = "three.txt", .watchid = &watch0, - .eventid = QFILE_MONITOR_EVENT_CREATED }, - - - { .type = QFILE_MONITOR_TEST_OP_UNLINK, - .filesrc = "three.txt", }, - { .type = QFILE_MONITOR_TEST_OP_EVENT, - .filesrc = "three.txt", .watchid = &watch0, - .eventid = QFILE_MONITOR_EVENT_DELETED }, - - - { .type = QFILE_MONITOR_TEST_OP_RENAME, - .filesrc = "one.txt", .filedst = "two.txt" }, - { .type = QFILE_MONITOR_TEST_OP_EVENT, - .filesrc = "one.txt", .watchid = &watch0, - .eventid = QFILE_MONITOR_EVENT_DELETED }, - { .type = QFILE_MONITOR_TEST_OP_EVENT, - .filesrc = "one.txt", .watchid = &watch1, - .eventid = QFILE_MONITOR_EVENT_DELETED }, - { .type = QFILE_MONITOR_TEST_OP_EVENT, - .filesrc = "two.txt", .watchid = &watch0, - .eventid = QFILE_MONITOR_EVENT_CREATED }, - { .type = QFILE_MONITOR_TEST_OP_EVENT, - .filesrc = "two.txt", .watchid = &watch2, - .eventid = QFILE_MONITOR_EVENT_CREATED }, - - - { .type = QFILE_MONITOR_TEST_OP_APPEND, - .filesrc = "two.txt", }, - { .type = QFILE_MONITOR_TEST_OP_EVENT, - .filesrc = "two.txt", .watchid = &watch0, - .eventid = QFILE_MONITOR_EVENT_MODIFIED }, - { .type = QFILE_MONITOR_TEST_OP_EVENT, - .filesrc = "two.txt", .watchid = &watch2, - .eventid = QFILE_MONITOR_EVENT_MODIFIED }, - - - { .type = QFILE_MONITOR_TEST_OP_TOUCH, - .filesrc = "two.txt", }, - { .type = QFILE_MONITOR_TEST_OP_EVENT, - .filesrc = "two.txt", .watchid = &watch0, - .eventid = QFILE_MONITOR_EVENT_ATTRIBUTES }, - { .type = QFILE_MONITOR_TEST_OP_EVENT, - .filesrc = "two.txt", .watchid = &watch2, - .eventid = QFILE_MONITOR_EVENT_ATTRIBUTES }, - - - { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH, - .filesrc = "one.txt", .watchid = &watch1 }, - { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH, - .filesrc = "one.txt", .watchid = &watch3 }, - { .type = QFILE_MONITOR_TEST_OP_CREATE, - .filesrc = "one.txt", }, - { .type = QFILE_MONITOR_TEST_OP_EVENT, - .filesrc = "one.txt", .watchid = &watch0, - .eventid = QFILE_MONITOR_EVENT_CREATED }, - { .type = QFILE_MONITOR_TEST_OP_EVENT, - .filesrc = "one.txt", .watchid = &watch3, - .eventid = QFILE_MONITOR_EVENT_CREATED }, - - - { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH, - .filesrc = "one.txt", .watchid = &watch3 }, - { .type = QFILE_MONITOR_TEST_OP_UNLINK, - .filesrc = "one.txt", }, - { .type = QFILE_MONITOR_TEST_OP_EVENT, - .filesrc = "one.txt", .watchid = &watch0, - .eventid = QFILE_MONITOR_EVENT_DELETED }, - - - { .type = QFILE_MONITOR_TEST_OP_MKDIR, - .filesrc = "fish", }, - { .type = QFILE_MONITOR_TEST_OP_EVENT, - .filesrc = "fish", .watchid = &watch0, - .eventid = QFILE_MONITOR_EVENT_CREATED }, - - - { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH, - .filesrc = "fish/", .watchid = &watch4 }, - { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH, - .filesrc = "fish/one.txt", .watchid = &watch5 }, - { .type = QFILE_MONITOR_TEST_OP_CREATE, - .filesrc = "fish/one.txt", }, - { .type = QFILE_MONITOR_TEST_OP_EVENT, - .filesrc = "one.txt", .watchid = &watch4, - .eventid = QFILE_MONITOR_EVENT_CREATED }, - { .type = QFILE_MONITOR_TEST_OP_EVENT, - .filesrc = "one.txt", .watchid = &watch5, - .eventid = QFILE_MONITOR_EVENT_CREATED }, - - - { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH, - .filesrc = "fish/one.txt", .watchid = &watch5 }, - { .type = QFILE_MONITOR_TEST_OP_RENAME, - .filesrc = "fish/one.txt", .filedst = "two.txt", }, - { .type = QFILE_MONITOR_TEST_OP_EVENT, - .filesrc = "one.txt", .watchid = &watch4, - .eventid = QFILE_MONITOR_EVENT_DELETED }, - { .type = QFILE_MONITOR_TEST_OP_EVENT, - .filesrc = "two.txt", .watchid = &watch0, - .eventid = QFILE_MONITOR_EVENT_CREATED }, - { .type = QFILE_MONITOR_TEST_OP_EVENT, - .filesrc = "two.txt", .watchid = &watch2, - .eventid = QFILE_MONITOR_EVENT_CREATED }, - - - { .type = QFILE_MONITOR_TEST_OP_RMDIR, - .filesrc = "fish", }, - { .type = QFILE_MONITOR_TEST_OP_EVENT, - .filesrc = "", .watchid = &watch4, - .eventid = QFILE_MONITOR_EVENT_IGNORED, - .swapnext = true }, - { .type = QFILE_MONITOR_TEST_OP_EVENT, - .filesrc = "fish", .watchid = &watch0, - .eventid = QFILE_MONITOR_EVENT_DELETED }, - { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH, - .filesrc = "fish", .watchid = &watch4 }, - - - { .type = QFILE_MONITOR_TEST_OP_UNLINK, - .filesrc = "two.txt", }, - { .type = QFILE_MONITOR_TEST_OP_EVENT, - .filesrc = "two.txt", .watchid = &watch0, - .eventid = QFILE_MONITOR_EVENT_DELETED }, - { .type = QFILE_MONITOR_TEST_OP_EVENT, - .filesrc = "two.txt", .watchid = &watch2, - .eventid = QFILE_MONITOR_EVENT_DELETED }, - - - { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH, - .filesrc = "two.txt", .watchid = &watch2 }, - { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH, - .filesrc = NULL, .watchid = &watch0 }, - }; - Error *local_err = NULL; - GError *gerr = NULL; - QFileMonitor *mon = qemu_file_monitor_new(&local_err); - QemuThread th; - GTimer *timer; - gchar *dir = NULL; - int err = -1; - gsize i; - char *pathsrc = NULL; - char *pathdst = NULL; - QFileMonitorTestData data; - GHashTable *ids = g_hash_table_new(g_int64_hash, g_int64_equal); - char *travis_arch; - - qemu_mutex_init(&data.lock); - data.records = NULL; - - /* - * This test does not work on Travis LXD containers since some - * syscalls are blocked in that environment. - */ - travis_arch = getenv("TRAVIS_ARCH"); - if (travis_arch && !g_str_equal(travis_arch, "x86_64")) { - g_test_skip("Test does not work on non-x86 Travis containers."); - return; - } - - /* - * The file monitor needs the main loop running in - * order to receive events from inotify. We must - * thus spawn a background thread to run an event - * loop impl, while this thread triggers the - * actual file operations we're testing - */ - evrunning = 1; - evstopping = 0; - qemu_thread_create(&th, "event-loop", - qemu_file_monitor_test_event_loop, NULL, - QEMU_THREAD_JOINABLE); - - if (local_err) { - g_printerr("File monitoring not available: %s", - error_get_pretty(local_err)); - error_free(local_err); - return; - } - - dir = g_dir_make_tmp("test-util-filemonitor-XXXXXX", - &gerr); - if (!dir) { - g_printerr("Unable to create tmp dir %s", - gerr->message); - g_error_free(gerr); - abort(); - } - - /* - * Run through the operation sequence validating events - * as we go - */ - for (i = 0; i < G_N_ELEMENTS(ops); i++) { - const QFileMonitorTestOp *op = &(ops[i]); - int fd; - struct utimbuf ubuf; - char *watchdir; - const char *watchfile; - - pathsrc = g_strdup_printf("%s/%s", dir, op->filesrc); - if (op->filedst) { - pathdst = g_strdup_printf("%s/%s", dir, op->filedst); - } - - switch (op->type) { - case QFILE_MONITOR_TEST_OP_ADD_WATCH: - if (debug) { - g_printerr("Add watch %s %s\n", - dir, op->filesrc); - } - if (op->filesrc && strchr(op->filesrc, '/')) { - watchdir = g_strdup_printf("%s/%s", dir, op->filesrc); - watchfile = strrchr(watchdir, '/'); - *(char *)watchfile = '\0'; - watchfile++; - if (*watchfile == '\0') { - watchfile = NULL; - } - } else { - watchdir = g_strdup(dir); - watchfile = op->filesrc; - } - *op->watchid = - qemu_file_monitor_add_watch(mon, - watchdir, - watchfile, - qemu_file_monitor_test_handler, - &data, - &local_err); - g_free(watchdir); - if (*op->watchid < 0) { - g_printerr("Unable to add watch %s", - error_get_pretty(local_err)); - error_free(local_err); - goto cleanup; - } - if (debug) { - g_printerr("Watch ID %" PRIx64 "\n", *op->watchid); - } - if (g_hash_table_contains(ids, op->watchid)) { - g_printerr("Watch ID %" PRIx64 "already exists", *op->watchid); - goto cleanup; - } - g_hash_table_add(ids, op->watchid); - break; - case QFILE_MONITOR_TEST_OP_DEL_WATCH: - if (debug) { - g_printerr("Del watch %s %" PRIx64 "\n", dir, *op->watchid); - } - if (op->filesrc && strchr(op->filesrc, '/')) { - watchdir = g_strdup_printf("%s/%s", dir, op->filesrc); - watchfile = strrchr(watchdir, '/'); - *(char *)watchfile = '\0'; - } else { - watchdir = g_strdup(dir); - } - g_hash_table_remove(ids, op->watchid); - qemu_file_monitor_remove_watch(mon, - watchdir, - *op->watchid); - g_free(watchdir); - break; - case QFILE_MONITOR_TEST_OP_EVENT: - if (debug) { - g_printerr("Event id=%" PRIx64 " event=%d file=%s\n", - *op->watchid, op->eventid, op->filesrc); - } - if (!qemu_file_monitor_test_expect(&data, *op->watchid, - op->eventid, op->filesrc, - op->swapnext)) - goto cleanup; - break; - case QFILE_MONITOR_TEST_OP_CREATE: - if (debug) { - g_printerr("Create %s\n", pathsrc); - } - fd = open(pathsrc, O_WRONLY | O_CREAT, 0700); - if (fd < 0) { - g_printerr("Unable to create %s: %s", - pathsrc, strerror(errno)); - goto cleanup; - } - close(fd); - break; - - case QFILE_MONITOR_TEST_OP_APPEND: - if (debug) { - g_printerr("Append %s\n", pathsrc); - } - fd = open(pathsrc, O_WRONLY | O_APPEND, 0700); - if (fd < 0) { - g_printerr("Unable to open %s: %s", - pathsrc, strerror(errno)); - goto cleanup; - } - - if (write(fd, "Hello World", 10) != 10) { - g_printerr("Unable to write %s: %s", - pathsrc, strerror(errno)); - close(fd); - goto cleanup; - } - close(fd); - break; - - case QFILE_MONITOR_TEST_OP_TRUNC: - if (debug) { - g_printerr("Truncate %s\n", pathsrc); - } - if (truncate(pathsrc, 4) < 0) { - g_printerr("Unable to truncate %s: %s", - pathsrc, strerror(errno)); - goto cleanup; - } - break; - - case QFILE_MONITOR_TEST_OP_RENAME: - if (debug) { - g_printerr("Rename %s -> %s\n", pathsrc, pathdst); - } - if (rename(pathsrc, pathdst) < 0) { - g_printerr("Unable to rename %s to %s: %s", - pathsrc, pathdst, strerror(errno)); - goto cleanup; - } - break; - - case QFILE_MONITOR_TEST_OP_UNLINK: - if (debug) { - g_printerr("Unlink %s\n", pathsrc); - } - if (unlink(pathsrc) < 0) { - g_printerr("Unable to unlink %s: %s", - pathsrc, strerror(errno)); - goto cleanup; - } - break; - - case QFILE_MONITOR_TEST_OP_TOUCH: - if (debug) { - g_printerr("Touch %s\n", pathsrc); - } - ubuf.actime = 1024; - ubuf.modtime = 1025; - if (utime(pathsrc, &ubuf) < 0) { - g_printerr("Unable to touch %s: %s", - pathsrc, strerror(errno)); - goto cleanup; - } - break; - - case QFILE_MONITOR_TEST_OP_MKDIR: - if (debug) { - g_printerr("Mkdir %s\n", pathsrc); - } - if (g_mkdir_with_parents(pathsrc, 0700) < 0) { - g_printerr("Unable to mkdir %s: %s", - pathsrc, strerror(errno)); - goto cleanup; - } - break; - - case QFILE_MONITOR_TEST_OP_RMDIR: - if (debug) { - g_printerr("Rmdir %s\n", pathsrc); - } - if (rmdir(pathsrc) < 0) { - g_printerr("Unable to rmdir %s: %s", - pathsrc, strerror(errno)); - goto cleanup; - } - break; - - default: - g_assert_not_reached(); - } - - g_free(pathsrc); - g_free(pathdst); - pathsrc = pathdst = NULL; - } - - g_assert_cmpint(g_hash_table_size(ids), ==, 0); - - err = 0; - - cleanup: - g_free(pathsrc); - g_free(pathdst); - - qemu_mutex_lock(&evlock); - evstopping = 1; - timer = g_timer_new(); - while (evrunning && g_timer_elapsed(timer, NULL) < 5) { - qemu_mutex_unlock(&evlock); - usleep(10 * 1000); - qemu_mutex_lock(&evlock); - } - qemu_mutex_unlock(&evlock); - - if (g_timer_elapsed(timer, NULL) >= 5) { - g_printerr("Event loop failed to quit after 5 seconds\n"); - } - g_timer_destroy(timer); - - qemu_file_monitor_free(mon); - g_list_foreach(data.records, - (GFunc)qemu_file_monitor_test_record_free, NULL); - g_list_free(data.records); - qemu_mutex_destroy(&data.lock); - if (dir) { - for (i = 0; i < G_N_ELEMENTS(ops); i++) { - const QFileMonitorTestOp *op = &(ops[i]); - char *path = g_strdup_printf("%s/%s", - dir, op->filesrc); - if (op->type == QFILE_MONITOR_TEST_OP_MKDIR) { - rmdir(path); - g_free(path); - } else { - unlink(path); - g_free(path); - if (op->filedst) { - path = g_strdup_printf("%s/%s", - dir, op->filedst); - unlink(path); - g_free(path); - } - } - } - if (rmdir(dir) < 0) { - g_printerr("Failed to remove %s: %s\n", - dir, strerror(errno)); - abort(); - } - } - g_hash_table_unref(ids); - g_free(dir); - g_assert(err == 0); -} - - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - - qemu_init_main_loop(&error_abort); - - qemu_mutex_init(&evlock); - - debug = getenv("FILEMONITOR_DEBUG") != NULL; - g_test_add_func("/util/filemonitor", test_file_monitor_events); - - return g_test_run(); -} diff --git a/tests/test-util-sockets.c b/tests/test-util-sockets.c deleted file mode 100644 index 67486055ed..0000000000 --- a/tests/test-util-sockets.c +++ /dev/null @@ -1,379 +0,0 @@ -/* - * Tests for util/qemu-sockets.c - * - * Copyright 2018 Red Hat, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this library; if not, see . - * - */ - -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "qemu/sockets.h" -#include "qapi/error.h" -#include "socket-helpers.h" -#include "monitor/monitor.h" - -static void test_fd_is_socket_bad(void) -{ - char *tmp = g_strdup("qemu-test-util-sockets-XXXXXX"); - int fd = mkstemp(tmp); - if (fd != 0) { - unlink(tmp); - } - g_free(tmp); - - g_assert(fd >= 0); - - g_assert(!fd_is_socket(fd)); - close(fd); -} - -static void test_fd_is_socket_good(void) -{ - int fd = qemu_socket(PF_INET, SOCK_STREAM, 0); - - g_assert(fd >= 0); - - g_assert(fd_is_socket(fd)); - close(fd); -} - -static int mon_fd = -1; -static const char *mon_fdname; -__thread Monitor *cur_mon; - -int monitor_get_fd(Monitor *mon, const char *fdname, Error **errp) -{ - g_assert(cur_mon); - g_assert(mon == cur_mon); - if (mon_fd == -1 || !g_str_equal(mon_fdname, fdname)) { - error_setg(errp, "No fd named %s", fdname); - return -1; - } - return dup(mon_fd); -} - -/* - * Syms of stubs in libqemuutil.a are discarded at .o file - * granularity. To replace monitor_get_fd() and monitor_cur(), we - * must ensure that we also replace any other symbol that is used in - * the binary and would be taken from the same stub object file, - * otherwise we get duplicate syms at link time. - */ -Monitor *monitor_cur(void) { return cur_mon; } -int monitor_vprintf(Monitor *mon, const char *fmt, va_list ap) { abort(); } - -#ifndef _WIN32 -static void test_socket_fd_pass_name_good(void) -{ - SocketAddress addr; - int fd; - - cur_mon = g_malloc(1); /* Fake a monitor */ - mon_fdname = "myfd"; - mon_fd = qemu_socket(AF_INET, SOCK_STREAM, 0); - g_assert_cmpint(mon_fd, >, STDERR_FILENO); - - addr.type = SOCKET_ADDRESS_TYPE_FD; - addr.u.fd.str = g_strdup(mon_fdname); - - fd = socket_connect(&addr, &error_abort); - g_assert_cmpint(fd, !=, -1); - g_assert_cmpint(fd, !=, mon_fd); - close(fd); - - fd = socket_listen(&addr, 1, &error_abort); - g_assert_cmpint(fd, !=, -1); - g_assert_cmpint(fd, !=, mon_fd); - close(fd); - - g_free(addr.u.fd.str); - mon_fdname = NULL; - close(mon_fd); - mon_fd = -1; - g_free(cur_mon); - cur_mon = NULL; -} - -static void test_socket_fd_pass_name_bad(void) -{ - SocketAddress addr; - Error *err = NULL; - int fd; - - cur_mon = g_malloc(1); /* Fake a monitor */ - mon_fdname = "myfd"; - mon_fd = dup(STDOUT_FILENO); - g_assert_cmpint(mon_fd, >, STDERR_FILENO); - - addr.type = SOCKET_ADDRESS_TYPE_FD; - addr.u.fd.str = g_strdup(mon_fdname); - - fd = socket_connect(&addr, &err); - g_assert_cmpint(fd, ==, -1); - error_free_or_abort(&err); - - fd = socket_listen(&addr, 1, &err); - g_assert_cmpint(fd, ==, -1); - error_free_or_abort(&err); - - g_free(addr.u.fd.str); - mon_fdname = NULL; - close(mon_fd); - mon_fd = -1; - g_free(cur_mon); - cur_mon = NULL; -} - -static void test_socket_fd_pass_name_nomon(void) -{ - SocketAddress addr; - Error *err = NULL; - int fd; - - g_assert(cur_mon == NULL); - - addr.type = SOCKET_ADDRESS_TYPE_FD; - addr.u.fd.str = g_strdup("myfd"); - - fd = socket_connect(&addr, &err); - g_assert_cmpint(fd, ==, -1); - error_free_or_abort(&err); - - fd = socket_listen(&addr, 1, &err); - g_assert_cmpint(fd, ==, -1); - error_free_or_abort(&err); - - g_free(addr.u.fd.str); -} - - -static void test_socket_fd_pass_num_good(void) -{ - SocketAddress addr; - int fd, sfd; - - g_assert(cur_mon == NULL); - sfd = qemu_socket(AF_INET, SOCK_STREAM, 0); - g_assert_cmpint(sfd, >, STDERR_FILENO); - - addr.type = SOCKET_ADDRESS_TYPE_FD; - addr.u.fd.str = g_strdup_printf("%d", sfd); - - fd = socket_connect(&addr, &error_abort); - g_assert_cmpint(fd, ==, sfd); - - fd = socket_listen(&addr, 1, &error_abort); - g_assert_cmpint(fd, ==, sfd); - - g_free(addr.u.fd.str); - close(sfd); -} - -static void test_socket_fd_pass_num_bad(void) -{ - SocketAddress addr; - Error *err = NULL; - int fd, sfd; - - g_assert(cur_mon == NULL); - sfd = dup(STDOUT_FILENO); - - addr.type = SOCKET_ADDRESS_TYPE_FD; - addr.u.fd.str = g_strdup_printf("%d", sfd); - - fd = socket_connect(&addr, &err); - g_assert_cmpint(fd, ==, -1); - error_free_or_abort(&err); - - fd = socket_listen(&addr, 1, &err); - g_assert_cmpint(fd, ==, -1); - error_free_or_abort(&err); - - g_free(addr.u.fd.str); - close(sfd); -} - -static void test_socket_fd_pass_num_nocli(void) -{ - SocketAddress addr; - Error *err = NULL; - int fd; - - cur_mon = g_malloc(1); /* Fake a monitor */ - - addr.type = SOCKET_ADDRESS_TYPE_FD; - addr.u.fd.str = g_strdup_printf("%d", STDOUT_FILENO); - - fd = socket_connect(&addr, &err); - g_assert_cmpint(fd, ==, -1); - error_free_or_abort(&err); - - fd = socket_listen(&addr, 1, &err); - g_assert_cmpint(fd, ==, -1); - error_free_or_abort(&err); - - g_free(addr.u.fd.str); -} -#endif - -#ifdef CONFIG_LINUX - -#define ABSTRACT_SOCKET_VARIANTS 3 - -typedef struct { - SocketAddress *server, *client[ABSTRACT_SOCKET_VARIANTS]; - bool expect_connect[ABSTRACT_SOCKET_VARIANTS]; -} abstract_socket_matrix_row; - -static gpointer unix_client_thread_func(gpointer user_data) -{ - abstract_socket_matrix_row *row = user_data; - Error *err = NULL; - int i, fd; - - for (i = 0; i < ABSTRACT_SOCKET_VARIANTS; i++) { - if (row->expect_connect[i]) { - fd = socket_connect(row->client[i], &error_abort); - g_assert_cmpint(fd, >=, 0); - } else { - fd = socket_connect(row->client[i], &err); - g_assert_cmpint(fd, ==, -1); - error_free_or_abort(&err); - } - close(fd); - } - return NULL; -} - -static void test_socket_unix_abstract_row(abstract_socket_matrix_row *test) -{ - int fd, connfd, i; - GThread *cli; - struct sockaddr_un un; - socklen_t len = sizeof(un); - - /* Last one must connect, or else accept() below hangs */ - assert(test->expect_connect[ABSTRACT_SOCKET_VARIANTS - 1]); - - fd = socket_listen(test->server, 1, &error_abort); - g_assert_cmpint(fd, >=, 0); - g_assert(fd_is_socket(fd)); - - cli = g_thread_new("abstract_unix_client", - unix_client_thread_func, - test); - - for (i = 0; i < ABSTRACT_SOCKET_VARIANTS; i++) { - if (test->expect_connect[i]) { - connfd = accept(fd, (struct sockaddr *)&un, &len); - g_assert_cmpint(connfd, !=, -1); - close(connfd); - } - } - - close(fd); - g_thread_join(cli); -} - -static void test_socket_unix_abstract(void) -{ - SocketAddress addr, addr_tight, addr_padded; - abstract_socket_matrix_row matrix[ABSTRACT_SOCKET_VARIANTS] = { - { &addr, - { &addr_tight, &addr_padded, &addr }, - { true, false, true } }, - { &addr_tight, - { &addr_padded, &addr, &addr_tight }, - { false, true, true } }, - { &addr_padded, - { &addr, &addr_tight, &addr_padded }, - { false, false, true } } - }; - int i; - - addr.type = SOCKET_ADDRESS_TYPE_UNIX; - addr.u.q_unix.path = g_strdup_printf("unix-%d-%u", - getpid(), g_random_int()); - addr.u.q_unix.has_abstract = true; - addr.u.q_unix.abstract = true; - addr.u.q_unix.has_tight = false; - addr.u.q_unix.tight = false; - - addr_tight = addr; - addr_tight.u.q_unix.has_tight = true; - addr_tight.u.q_unix.tight = true; - - addr_padded = addr; - addr_padded.u.q_unix.has_tight = true; - addr_padded.u.q_unix.tight = false; - - for (i = 0; i < ABSTRACT_SOCKET_VARIANTS; i++) { - test_socket_unix_abstract_row(&matrix[i]); - } - - g_free(addr.u.q_unix.path); -} - -#endif /* CONFIG_LINUX */ - -int main(int argc, char **argv) -{ - bool has_ipv4, has_ipv6; - - qemu_init_main_loop(&error_abort); - socket_init(); - - g_test_init(&argc, &argv, NULL); - - /* We're creating actual IPv4/6 sockets, so we should - * check if the host running tests actually supports - * each protocol to avoid breaking tests on machines - * with either IPv4 or IPv6 disabled. - */ - if (socket_check_protocol_support(&has_ipv4, &has_ipv6) < 0) { - g_printerr("socket_check_protocol_support() failed\n"); - goto end; - } - - if (has_ipv4) { - g_test_add_func("/util/socket/is-socket/bad", - test_fd_is_socket_bad); - g_test_add_func("/util/socket/is-socket/good", - test_fd_is_socket_good); -#ifndef _WIN32 - g_test_add_func("/socket/fd-pass/name/good", - test_socket_fd_pass_name_good); - g_test_add_func("/socket/fd-pass/name/bad", - test_socket_fd_pass_name_bad); - g_test_add_func("/socket/fd-pass/name/nomon", - test_socket_fd_pass_name_nomon); - g_test_add_func("/socket/fd-pass/num/good", - test_socket_fd_pass_num_good); - g_test_add_func("/socket/fd-pass/num/bad", - test_socket_fd_pass_num_bad); - g_test_add_func("/socket/fd-pass/num/nocli", - test_socket_fd_pass_num_nocli); -#endif - } - -#ifdef CONFIG_LINUX - g_test_add_func("/util/socket/unix-abstract", - test_socket_unix_abstract); -#endif - -end: - return g_test_run(); -} diff --git a/tests/test-uuid.c b/tests/test-uuid.c deleted file mode 100644 index c111de5fc1..0000000000 --- a/tests/test-uuid.c +++ /dev/null @@ -1,184 +0,0 @@ -/* - * QEMU UUID Library - * - * Copyright (c) 2016 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - * - */ - -#include "qemu/osdep.h" -#include "qemu/uuid.h" - -struct { - const char *uuidstr; - QemuUUID uuid; - bool uuidstr_is_valid; - bool check_unparse; -} uuid_test_data[] = { - { /* Normal */ - "586ece27-7f09-41e0-9e74-e901317e9d42", - { { { - 0x58, 0x6e, 0xce, 0x27, 0x7f, 0x09, 0x41, 0xe0, - 0x9e, 0x74, 0xe9, 0x01, 0x31, 0x7e, 0x9d, 0x42, - } } }, - true, true, - }, { /* NULL */ - "00000000-0000-0000-0000-000000000000", - { }, - true, true, - }, { /* Upper case */ - "0CC6C752-3961-4028-A286-C05CC616D396", - { { { - 0x0c, 0xc6, 0xc7, 0x52, 0x39, 0x61, 0x40, 0x28, - 0xa2, 0x86, 0xc0, 0x5c, 0xc6, 0x16, 0xd3, 0x96, - } } }, - true, false, - }, { /* Mixed case */ - "0CC6C752-3961-4028-a286-c05cc616D396", - { { { - 0x0c, 0xc6, 0xc7, 0x52, 0x39, 0x61, 0x40, 0x28, - 0xa2, 0x86, 0xc0, 0x5c, 0xc6, 0x16, 0xd3, 0x96, - } } }, - true, false, - }, { /* Empty */ - "" - }, { /* Too short */ - "abc", - }, { /* Non-hex */ - "abcdefgh-0000-0000-0000-000000000000", - }, { /* No '-' */ - "0cc6c75239614028a286c05cc616d396", - }, { /* '-' in wrong position */ - "0cc6c-7523961-4028-a286-c05cc616d396", - }, { /* Double '-' */ - "0cc6c752--3961-4028-a286-c05cc616d396", - }, { /* Too long */ - "0000000000000000000000000000000000000000000000", - }, { /* Invalid char in the beginning */ - ")cc6c752-3961-4028-a286-c05cc616d396", - }, { /* Invalid char in the beginning, in extra */ - ")0cc6c752-3961-4028-a286-c05cc616d396", - }, { /* Invalid char in the middle */ - "0cc6c752-39*1-4028-a286-c05cc616d396", - }, { /* Invalid char in the middle, in extra */ - "0cc6c752-39*61-4028-a286-c05cc616d396", - }, { /* Invalid char in the end */ - "0cc6c752-3961-4028-a286-c05cc616d39&", - }, { /* Invalid char in the end, in extra */ - "0cc6c752-3961-4028-a286-c05cc616d396&", - }, { /* Short end and trailing space */ - "0cc6c752-3961-4028-a286-c05cc616d39 ", - }, { /* Leading space and short end */ - " 0cc6c752-3961-4028-a286-c05cc616d39", - }, -}; - -static inline bool uuid_is_valid(QemuUUID *uuid) -{ - return qemu_uuid_is_null(uuid) || - ((uuid->data[6] & 0xf0) == 0x40 && (uuid->data[8] & 0xc0) == 0x80); -} - -static void test_uuid_generate(void) -{ - QemuUUID uuid_not_null = { { { - 0x58, 0x6e, 0xce, 0x27, 0x7f, 0x09, 0x41, 0xe0, - 0x9e, 0x74, 0xe9, 0x01, 0x31, 0x7e, 0x9d, 0x42 - } } }; - QemuUUID uuid; - int i; - - for (i = 0; i < 100; ++i) { - qemu_uuid_generate(&uuid); - g_assert(uuid_is_valid(&uuid)); - g_assert_false(qemu_uuid_is_null(&uuid)); - g_assert_false(qemu_uuid_is_equal(&uuid_not_null, &uuid)); - } -} - -static void test_uuid_is_null(void) -{ - QemuUUID uuid_null = { }; - QemuUUID uuid_not_null = { { { - 0x58, 0x6e, 0xce, 0x27, 0x7f, 0x09, 0x41, 0xe0, - 0x9e, 0x74, 0xe9, 0x01, 0x31, 0x7e, 0x9d, 0x42 - } } }; - QemuUUID uuid_not_null_2 = { { { 1 } } }; - - g_assert(qemu_uuid_is_null(&uuid_null)); - g_assert_false(qemu_uuid_is_null(&uuid_not_null)); - g_assert_false(qemu_uuid_is_null(&uuid_not_null_2)); -} - -static void test_uuid_parse(void) -{ - int i, r; - - for (i = 0; i < ARRAY_SIZE(uuid_test_data); i++) { - QemuUUID uuid; - bool is_valid = uuid_test_data[i].uuidstr_is_valid; - - r = qemu_uuid_parse(uuid_test_data[i].uuidstr, &uuid); - g_assert_cmpint(!r, ==, is_valid); - if (is_valid) { - g_assert_cmpint(is_valid, ==, uuid_is_valid(&uuid)); - g_assert_cmpmem(&uuid_test_data[i].uuid, sizeof(uuid), - &uuid, sizeof(uuid)); - } - } -} - -static void test_uuid_unparse(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(uuid_test_data); i++) { - char out[37]; - - if (!uuid_test_data[i].check_unparse) { - continue; - } - qemu_uuid_unparse(&uuid_test_data[i].uuid, out); - g_assert_cmpstr(uuid_test_data[i].uuidstr, ==, out); - } -} - -static void test_uuid_unparse_strdup(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(uuid_test_data); i++) { - char *out; - - if (!uuid_test_data[i].check_unparse) { - continue; - } - out = qemu_uuid_unparse_strdup(&uuid_test_data[i].uuid); - g_assert_cmpstr(uuid_test_data[i].uuidstr, ==, out); - g_free(out); - } -} - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - g_test_add_func("/uuid/is_null", test_uuid_is_null); - g_test_add_func("/uuid/generate", test_uuid_generate); - g_test_add_func("/uuid/parse", test_uuid_parse); - g_test_add_func("/uuid/unparse", test_uuid_unparse); - g_test_add_func("/uuid/unparse_strdup", test_uuid_unparse_strdup); - - return g_test_run(); -} diff --git a/tests/test-visitor-serialization.c b/tests/test-visitor-serialization.c deleted file mode 100644 index 4629958647..0000000000 --- a/tests/test-visitor-serialization.c +++ /dev/null @@ -1,1116 +0,0 @@ -/* - * Unit-tests for visitor-based serialization - * - * Copyright (C) 2014-2015 Red Hat, Inc. - * Copyright IBM, Corp. 2012 - * - * Authors: - * Michael Roth - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include - -#include "qemu-common.h" -#include "test-qapi-visit.h" -#include "qapi/error.h" -#include "qapi/qmp/qjson.h" -#include "qapi/qmp/qstring.h" -#include "qapi/qobject-input-visitor.h" -#include "qapi/qobject-output-visitor.h" -#include "qapi/string-input-visitor.h" -#include "qapi/string-output-visitor.h" -#include "qapi/dealloc-visitor.h" - -enum PrimitiveTypeKind { - PTYPE_STRING = 0, - PTYPE_BOOLEAN, - PTYPE_NUMBER, - PTYPE_INTEGER, - PTYPE_U8, - PTYPE_U16, - PTYPE_U32, - PTYPE_U64, - PTYPE_S8, - PTYPE_S16, - PTYPE_S32, - PTYPE_S64, - PTYPE_EOL, -}; - -typedef struct PrimitiveType { - union { - const char *string; - bool boolean; - double number; - int64_t integer; - uint8_t u8; - uint16_t u16; - uint32_t u32; - uint64_t u64; - int8_t s8; - int16_t s16; - int32_t s32; - int64_t s64; - } value; - enum PrimitiveTypeKind type; - const char *description; -} PrimitiveType; - -typedef struct PrimitiveList { - union { - strList *strings; - boolList *booleans; - numberList *numbers; - intList *integers; - int8List *s8_integers; - int16List *s16_integers; - int32List *s32_integers; - int64List *s64_integers; - uint8List *u8_integers; - uint16List *u16_integers; - uint32List *u32_integers; - uint64List *u64_integers; - } value; - enum PrimitiveTypeKind type; - const char *description; -} PrimitiveList; - -/* test helpers */ - -typedef void (*VisitorFunc)(Visitor *v, void **native, Error **errp); - -static void dealloc_helper(void *native_in, VisitorFunc visit, Error **errp) -{ - Visitor *v = qapi_dealloc_visitor_new(); - - visit(v, &native_in, errp); - - visit_free(v); -} - -static void visit_primitive_type(Visitor *v, void **native, Error **errp) -{ - PrimitiveType *pt = *native; - switch(pt->type) { - case PTYPE_STRING: - visit_type_str(v, NULL, (char **)&pt->value.string, errp); - break; - case PTYPE_BOOLEAN: - visit_type_bool(v, NULL, &pt->value.boolean, errp); - break; - case PTYPE_NUMBER: - visit_type_number(v, NULL, &pt->value.number, errp); - break; - case PTYPE_INTEGER: - visit_type_int(v, NULL, &pt->value.integer, errp); - break; - case PTYPE_U8: - visit_type_uint8(v, NULL, &pt->value.u8, errp); - break; - case PTYPE_U16: - visit_type_uint16(v, NULL, &pt->value.u16, errp); - break; - case PTYPE_U32: - visit_type_uint32(v, NULL, &pt->value.u32, errp); - break; - case PTYPE_U64: - visit_type_uint64(v, NULL, &pt->value.u64, errp); - break; - case PTYPE_S8: - visit_type_int8(v, NULL, &pt->value.s8, errp); - break; - case PTYPE_S16: - visit_type_int16(v, NULL, &pt->value.s16, errp); - break; - case PTYPE_S32: - visit_type_int32(v, NULL, &pt->value.s32, errp); - break; - case PTYPE_S64: - visit_type_int64(v, NULL, &pt->value.s64, errp); - break; - case PTYPE_EOL: - g_assert_not_reached(); - } -} - -static void visit_primitive_list(Visitor *v, void **native, Error **errp) -{ - PrimitiveList *pl = *native; - switch (pl->type) { - case PTYPE_STRING: - visit_type_strList(v, NULL, &pl->value.strings, errp); - break; - case PTYPE_BOOLEAN: - visit_type_boolList(v, NULL, &pl->value.booleans, errp); - break; - case PTYPE_NUMBER: - visit_type_numberList(v, NULL, &pl->value.numbers, errp); - break; - case PTYPE_INTEGER: - visit_type_intList(v, NULL, &pl->value.integers, errp); - break; - case PTYPE_S8: - visit_type_int8List(v, NULL, &pl->value.s8_integers, errp); - break; - case PTYPE_S16: - visit_type_int16List(v, NULL, &pl->value.s16_integers, errp); - break; - case PTYPE_S32: - visit_type_int32List(v, NULL, &pl->value.s32_integers, errp); - break; - case PTYPE_S64: - visit_type_int64List(v, NULL, &pl->value.s64_integers, errp); - break; - case PTYPE_U8: - visit_type_uint8List(v, NULL, &pl->value.u8_integers, errp); - break; - case PTYPE_U16: - visit_type_uint16List(v, NULL, &pl->value.u16_integers, errp); - break; - case PTYPE_U32: - visit_type_uint32List(v, NULL, &pl->value.u32_integers, errp); - break; - case PTYPE_U64: - visit_type_uint64List(v, NULL, &pl->value.u64_integers, errp); - break; - default: - g_assert_not_reached(); - } -} - - -static TestStruct *struct_create(void) -{ - TestStruct *ts = g_malloc0(sizeof(*ts)); - ts->integer = -42; - ts->boolean = true; - ts->string = strdup("test string"); - return ts; -} - -static void struct_compare(TestStruct *ts1, TestStruct *ts2) -{ - g_assert(ts1); - g_assert(ts2); - g_assert_cmpint(ts1->integer, ==, ts2->integer); - g_assert(ts1->boolean == ts2->boolean); - g_assert_cmpstr(ts1->string, ==, ts2->string); -} - -static void struct_cleanup(TestStruct *ts) -{ - g_free(ts->string); - g_free(ts); -} - -static void visit_struct(Visitor *v, void **native, Error **errp) -{ - visit_type_TestStruct(v, NULL, (TestStruct **)native, errp); -} - -static UserDefTwo *nested_struct_create(void) -{ - UserDefTwo *udnp = g_malloc0(sizeof(*udnp)); - udnp->string0 = strdup("test_string0"); - udnp->dict1 = g_malloc0(sizeof(*udnp->dict1)); - udnp->dict1->string1 = strdup("test_string1"); - udnp->dict1->dict2 = g_malloc0(sizeof(*udnp->dict1->dict2)); - udnp->dict1->dict2->userdef = g_new0(UserDefOne, 1); - udnp->dict1->dict2->userdef->integer = 42; - udnp->dict1->dict2->userdef->string = strdup("test_string"); - udnp->dict1->dict2->string = strdup("test_string2"); - udnp->dict1->dict3 = g_malloc0(sizeof(*udnp->dict1->dict3)); - udnp->dict1->has_dict3 = true; - udnp->dict1->dict3->userdef = g_new0(UserDefOne, 1); - udnp->dict1->dict3->userdef->integer = 43; - udnp->dict1->dict3->userdef->string = strdup("test_string"); - udnp->dict1->dict3->string = strdup("test_string3"); - return udnp; -} - -static void nested_struct_compare(UserDefTwo *udnp1, UserDefTwo *udnp2) -{ - g_assert(udnp1); - g_assert(udnp2); - g_assert_cmpstr(udnp1->string0, ==, udnp2->string0); - g_assert_cmpstr(udnp1->dict1->string1, ==, udnp2->dict1->string1); - g_assert_cmpint(udnp1->dict1->dict2->userdef->integer, ==, - udnp2->dict1->dict2->userdef->integer); - g_assert_cmpstr(udnp1->dict1->dict2->userdef->string, ==, - udnp2->dict1->dict2->userdef->string); - g_assert_cmpstr(udnp1->dict1->dict2->string, ==, - udnp2->dict1->dict2->string); - g_assert(udnp1->dict1->has_dict3 == udnp2->dict1->has_dict3); - g_assert_cmpint(udnp1->dict1->dict3->userdef->integer, ==, - udnp2->dict1->dict3->userdef->integer); - g_assert_cmpstr(udnp1->dict1->dict3->userdef->string, ==, - udnp2->dict1->dict3->userdef->string); - g_assert_cmpstr(udnp1->dict1->dict3->string, ==, - udnp2->dict1->dict3->string); -} - -static void nested_struct_cleanup(UserDefTwo *udnp) -{ - qapi_free_UserDefTwo(udnp); -} - -static void visit_nested_struct(Visitor *v, void **native, Error **errp) -{ - visit_type_UserDefTwo(v, NULL, (UserDefTwo **)native, errp); -} - -static void visit_nested_struct_list(Visitor *v, void **native, Error **errp) -{ - visit_type_UserDefTwoList(v, NULL, (UserDefTwoList **)native, errp); -} - -/* test cases */ - -typedef enum VisitorCapabilities { - VCAP_PRIMITIVES = 1, - VCAP_STRUCTURES = 2, - VCAP_LISTS = 4, - VCAP_PRIMITIVE_LISTS = 8, -} VisitorCapabilities; - -typedef struct SerializeOps { - void (*serialize)(void *native_in, void **datap, - VisitorFunc visit, Error **errp); - void (*deserialize)(void **native_out, void *datap, - VisitorFunc visit, Error **errp); - void (*cleanup)(void *datap); - const char *type; - VisitorCapabilities caps; -} SerializeOps; - -typedef struct TestArgs { - const SerializeOps *ops; - void *test_data; -} TestArgs; - -static void test_primitives(gconstpointer opaque) -{ - TestArgs *args = (TestArgs *) opaque; - const SerializeOps *ops = args->ops; - PrimitiveType *pt = args->test_data; - PrimitiveType *pt_copy = g_malloc0(sizeof(*pt_copy)); - void *serialize_data; - - pt_copy->type = pt->type; - ops->serialize(pt, &serialize_data, visit_primitive_type, &error_abort); - ops->deserialize((void **)&pt_copy, serialize_data, visit_primitive_type, - &error_abort); - - g_assert(pt_copy != NULL); - switch (pt->type) { - case PTYPE_STRING: - g_assert_cmpstr(pt->value.string, ==, pt_copy->value.string); - g_free((char *)pt_copy->value.string); - break; - case PTYPE_BOOLEAN: - g_assert_cmpint(pt->value.boolean, ==, pt->value.boolean); - break; - case PTYPE_NUMBER: - g_assert_cmpfloat(pt->value.number, ==, pt_copy->value.number); - break; - case PTYPE_INTEGER: - g_assert_cmpint(pt->value.integer, ==, pt_copy->value.integer); - break; - case PTYPE_U8: - g_assert_cmpuint(pt->value.u8, ==, pt_copy->value.u8); - break; - case PTYPE_U16: - g_assert_cmpuint(pt->value.u16, ==, pt_copy->value.u16); - break; - case PTYPE_U32: - g_assert_cmpuint(pt->value.u32, ==, pt_copy->value.u32); - break; - case PTYPE_U64: - g_assert_cmpuint(pt->value.u64, ==, pt_copy->value.u64); - break; - case PTYPE_S8: - g_assert_cmpint(pt->value.s8, ==, pt_copy->value.s8); - break; - case PTYPE_S16: - g_assert_cmpint(pt->value.s16, ==, pt_copy->value.s16); - break; - case PTYPE_S32: - g_assert_cmpint(pt->value.s32, ==, pt_copy->value.s32); - break; - case PTYPE_S64: - g_assert_cmpint(pt->value.s64, ==, pt_copy->value.s64); - break; - case PTYPE_EOL: - g_assert_not_reached(); - } - - ops->cleanup(serialize_data); - g_free(args); - g_free(pt_copy); -} - -static void test_primitive_lists(gconstpointer opaque) -{ - TestArgs *args = (TestArgs *) opaque; - const SerializeOps *ops = args->ops; - PrimitiveType *pt = args->test_data; - PrimitiveList pl = { .value = { NULL } }; - PrimitiveList pl_copy = { .value = { NULL } }; - PrimitiveList *pl_copy_ptr = &pl_copy; - void *serialize_data; - void *cur_head = NULL; - int i; - - pl.type = pl_copy.type = pt->type; - - /* build up our list of primitive types */ - for (i = 0; i < 32; i++) { - switch (pl.type) { - case PTYPE_STRING: { - QAPI_LIST_PREPEND(pl.value.strings, g_strdup(pt->value.string)); - break; - } - case PTYPE_INTEGER: { - QAPI_LIST_PREPEND(pl.value.integers, pt->value.integer); - break; - } - case PTYPE_S8: { - QAPI_LIST_PREPEND(pl.value.s8_integers, pt->value.s8); - break; - } - case PTYPE_S16: { - QAPI_LIST_PREPEND(pl.value.s16_integers, pt->value.s16); - break; - } - case PTYPE_S32: { - QAPI_LIST_PREPEND(pl.value.s32_integers, pt->value.s32); - break; - } - case PTYPE_S64: { - QAPI_LIST_PREPEND(pl.value.s64_integers, pt->value.s64); - break; - } - case PTYPE_U8: { - QAPI_LIST_PREPEND(pl.value.u8_integers, pt->value.u8); - break; - } - case PTYPE_U16: { - QAPI_LIST_PREPEND(pl.value.u16_integers, pt->value.u16); - break; - } - case PTYPE_U32: { - QAPI_LIST_PREPEND(pl.value.u32_integers, pt->value.u32); - break; - } - case PTYPE_U64: { - QAPI_LIST_PREPEND(pl.value.u64_integers, pt->value.u64); - break; - } - case PTYPE_NUMBER: { - QAPI_LIST_PREPEND(pl.value.numbers, pt->value.number); - break; - } - case PTYPE_BOOLEAN: { - QAPI_LIST_PREPEND(pl.value.booleans, pt->value.boolean); - break; - } - default: - g_assert_not_reached(); - } - } - - ops->serialize((void **)&pl, &serialize_data, visit_primitive_list, - &error_abort); - ops->deserialize((void **)&pl_copy_ptr, serialize_data, - visit_primitive_list, &error_abort); - - i = 0; - - /* compare our deserialized list of primitives to the original */ - do { - switch (pl_copy.type) { - case PTYPE_STRING: { - strList *ptr; - if (cur_head) { - ptr = cur_head; - cur_head = ptr->next; - } else { - cur_head = ptr = pl_copy.value.strings; - } - g_assert_cmpstr(pt->value.string, ==, ptr->value); - break; - } - case PTYPE_INTEGER: { - intList *ptr; - if (cur_head) { - ptr = cur_head; - cur_head = ptr->next; - } else { - cur_head = ptr = pl_copy.value.integers; - } - g_assert_cmpint(pt->value.integer, ==, ptr->value); - break; - } - case PTYPE_S8: { - int8List *ptr; - if (cur_head) { - ptr = cur_head; - cur_head = ptr->next; - } else { - cur_head = ptr = pl_copy.value.s8_integers; - } - g_assert_cmpint(pt->value.s8, ==, ptr->value); - break; - } - case PTYPE_S16: { - int16List *ptr; - if (cur_head) { - ptr = cur_head; - cur_head = ptr->next; - } else { - cur_head = ptr = pl_copy.value.s16_integers; - } - g_assert_cmpint(pt->value.s16, ==, ptr->value); - break; - } - case PTYPE_S32: { - int32List *ptr; - if (cur_head) { - ptr = cur_head; - cur_head = ptr->next; - } else { - cur_head = ptr = pl_copy.value.s32_integers; - } - g_assert_cmpint(pt->value.s32, ==, ptr->value); - break; - } - case PTYPE_S64: { - int64List *ptr; - if (cur_head) { - ptr = cur_head; - cur_head = ptr->next; - } else { - cur_head = ptr = pl_copy.value.s64_integers; - } - g_assert_cmpint(pt->value.s64, ==, ptr->value); - break; - } - case PTYPE_U8: { - uint8List *ptr; - if (cur_head) { - ptr = cur_head; - cur_head = ptr->next; - } else { - cur_head = ptr = pl_copy.value.u8_integers; - } - g_assert_cmpint(pt->value.u8, ==, ptr->value); - break; - } - case PTYPE_U16: { - uint16List *ptr; - if (cur_head) { - ptr = cur_head; - cur_head = ptr->next; - } else { - cur_head = ptr = pl_copy.value.u16_integers; - } - g_assert_cmpint(pt->value.u16, ==, ptr->value); - break; - } - case PTYPE_U32: { - uint32List *ptr; - if (cur_head) { - ptr = cur_head; - cur_head = ptr->next; - } else { - cur_head = ptr = pl_copy.value.u32_integers; - } - g_assert_cmpint(pt->value.u32, ==, ptr->value); - break; - } - case PTYPE_U64: { - uint64List *ptr; - if (cur_head) { - ptr = cur_head; - cur_head = ptr->next; - } else { - cur_head = ptr = pl_copy.value.u64_integers; - } - g_assert_cmpint(pt->value.u64, ==, ptr->value); - break; - } - case PTYPE_NUMBER: { - numberList *ptr; - GString *double_expected = g_string_new(""); - GString *double_actual = g_string_new(""); - if (cur_head) { - ptr = cur_head; - cur_head = ptr->next; - } else { - cur_head = ptr = pl_copy.value.numbers; - } - /* we serialize with %f for our reference visitors, so rather than - * fuzzy floating math to test "equality", just compare the - * formatted values - */ - g_string_printf(double_expected, "%.6f", pt->value.number); - g_string_printf(double_actual, "%.6f", ptr->value); - g_assert_cmpstr(double_actual->str, ==, double_expected->str); - g_string_free(double_expected, true); - g_string_free(double_actual, true); - break; - } - case PTYPE_BOOLEAN: { - boolList *ptr; - if (cur_head) { - ptr = cur_head; - cur_head = ptr->next; - } else { - cur_head = ptr = pl_copy.value.booleans; - } - g_assert_cmpint(!!pt->value.boolean, ==, !!ptr->value); - break; - } - default: - g_assert_not_reached(); - } - i++; - } while (cur_head); - - g_assert_cmpint(i, ==, 33); - - ops->cleanup(serialize_data); - dealloc_helper(&pl, visit_primitive_list, &error_abort); - dealloc_helper(&pl_copy, visit_primitive_list, &error_abort); - g_free(args); -} - -static void test_struct(gconstpointer opaque) -{ - TestArgs *args = (TestArgs *) opaque; - const SerializeOps *ops = args->ops; - TestStruct *ts = struct_create(); - TestStruct *ts_copy = NULL; - void *serialize_data; - - ops->serialize(ts, &serialize_data, visit_struct, &error_abort); - ops->deserialize((void **)&ts_copy, serialize_data, visit_struct, - &error_abort); - - struct_compare(ts, ts_copy); - - struct_cleanup(ts); - struct_cleanup(ts_copy); - - ops->cleanup(serialize_data); - g_free(args); -} - -static void test_nested_struct(gconstpointer opaque) -{ - TestArgs *args = (TestArgs *) opaque; - const SerializeOps *ops = args->ops; - UserDefTwo *udnp = nested_struct_create(); - UserDefTwo *udnp_copy = NULL; - void *serialize_data; - - ops->serialize(udnp, &serialize_data, visit_nested_struct, &error_abort); - ops->deserialize((void **)&udnp_copy, serialize_data, visit_nested_struct, - &error_abort); - - nested_struct_compare(udnp, udnp_copy); - - nested_struct_cleanup(udnp); - nested_struct_cleanup(udnp_copy); - - ops->cleanup(serialize_data); - g_free(args); -} - -static void test_nested_struct_list(gconstpointer opaque) -{ - TestArgs *args = (TestArgs *) opaque; - const SerializeOps *ops = args->ops; - UserDefTwoList *listp = NULL, *tmp, *tmp_copy, *listp_copy = NULL; - void *serialize_data; - int i = 0; - - for (i = 0; i < 8; i++) { - QAPI_LIST_PREPEND(listp, nested_struct_create()); - } - - ops->serialize(listp, &serialize_data, visit_nested_struct_list, - &error_abort); - ops->deserialize((void **)&listp_copy, serialize_data, - visit_nested_struct_list, &error_abort); - - tmp = listp; - tmp_copy = listp_copy; - while (listp_copy) { - g_assert(listp); - nested_struct_compare(listp->value, listp_copy->value); - listp = listp->next; - listp_copy = listp_copy->next; - } - - qapi_free_UserDefTwoList(tmp); - qapi_free_UserDefTwoList(tmp_copy); - - ops->cleanup(serialize_data); - g_free(args); -} - -static PrimitiveType pt_values[] = { - /* string tests */ - { - .description = "string_empty", - .type = PTYPE_STRING, - .value.string = "", - }, - { - .description = "string_whitespace", - .type = PTYPE_STRING, - .value.string = "a b c\td", - }, - { - .description = "string_newlines", - .type = PTYPE_STRING, - .value.string = "a\nb\n", - }, - { - .description = "string_commas", - .type = PTYPE_STRING, - .value.string = "a,b, c,d", - }, - { - .description = "string_single_quoted", - .type = PTYPE_STRING, - .value.string = "'a b',cd", - }, - { - .description = "string_double_quoted", - .type = PTYPE_STRING, - .value.string = "\"a b\",cd", - }, - /* boolean tests */ - { - .description = "boolean_true1", - .type = PTYPE_BOOLEAN, - .value.boolean = true, - }, - { - .description = "boolean_true2", - .type = PTYPE_BOOLEAN, - .value.boolean = 8, - }, - { - .description = "boolean_true3", - .type = PTYPE_BOOLEAN, - .value.boolean = -1, - }, - { - .description = "boolean_false1", - .type = PTYPE_BOOLEAN, - .value.boolean = false, - }, - { - .description = "boolean_false2", - .type = PTYPE_BOOLEAN, - .value.boolean = 0, - }, - /* number tests (double) */ - { - .description = "number_sanity1", - .type = PTYPE_NUMBER, - .value.number = -1, - }, - { - .description = "number_sanity2", - .type = PTYPE_NUMBER, - .value.number = 3.141593, - }, - { - .description = "number_min", - .type = PTYPE_NUMBER, - .value.number = DBL_MIN, - }, - { - .description = "number_max", - .type = PTYPE_NUMBER, - .value.number = DBL_MAX, - }, - /* integer tests (int64) */ - { - .description = "integer_sanity1", - .type = PTYPE_INTEGER, - .value.integer = -1, - }, - { - .description = "integer_sanity2", - .type = PTYPE_INTEGER, - .value.integer = INT64_MAX / 2 + 1, - }, - { - .description = "integer_min", - .type = PTYPE_INTEGER, - .value.integer = INT64_MIN, - }, - { - .description = "integer_max", - .type = PTYPE_INTEGER, - .value.integer = INT64_MAX, - }, - /* uint8 tests */ - { - .description = "uint8_sanity1", - .type = PTYPE_U8, - .value.u8 = 1, - }, - { - .description = "uint8_sanity2", - .type = PTYPE_U8, - .value.u8 = UINT8_MAX / 2 + 1, - }, - { - .description = "uint8_min", - .type = PTYPE_U8, - .value.u8 = 0, - }, - { - .description = "uint8_max", - .type = PTYPE_U8, - .value.u8 = UINT8_MAX, - }, - /* uint16 tests */ - { - .description = "uint16_sanity1", - .type = PTYPE_U16, - .value.u16 = 1, - }, - { - .description = "uint16_sanity2", - .type = PTYPE_U16, - .value.u16 = UINT16_MAX / 2 + 1, - }, - { - .description = "uint16_min", - .type = PTYPE_U16, - .value.u16 = 0, - }, - { - .description = "uint16_max", - .type = PTYPE_U16, - .value.u16 = UINT16_MAX, - }, - /* uint32 tests */ - { - .description = "uint32_sanity1", - .type = PTYPE_U32, - .value.u32 = 1, - }, - { - .description = "uint32_sanity2", - .type = PTYPE_U32, - .value.u32 = UINT32_MAX / 2 + 1, - }, - { - .description = "uint32_min", - .type = PTYPE_U32, - .value.u32 = 0, - }, - { - .description = "uint32_max", - .type = PTYPE_U32, - .value.u32 = UINT32_MAX, - }, - /* uint64 tests */ - { - .description = "uint64_sanity1", - .type = PTYPE_U64, - .value.u64 = 1, - }, - { - .description = "uint64_sanity2", - .type = PTYPE_U64, - .value.u64 = UINT64_MAX / 2 + 1, - }, - { - .description = "uint64_min", - .type = PTYPE_U64, - .value.u64 = 0, - }, - { - .description = "uint64_max", - .type = PTYPE_U64, - .value.u64 = UINT64_MAX, - }, - /* int8 tests */ - { - .description = "int8_sanity1", - .type = PTYPE_S8, - .value.s8 = -1, - }, - { - .description = "int8_sanity2", - .type = PTYPE_S8, - .value.s8 = INT8_MAX / 2 + 1, - }, - { - .description = "int8_min", - .type = PTYPE_S8, - .value.s8 = INT8_MIN, - }, - { - .description = "int8_max", - .type = PTYPE_S8, - .value.s8 = INT8_MAX, - }, - /* int16 tests */ - { - .description = "int16_sanity1", - .type = PTYPE_S16, - .value.s16 = -1, - }, - { - .description = "int16_sanity2", - .type = PTYPE_S16, - .value.s16 = INT16_MAX / 2 + 1, - }, - { - .description = "int16_min", - .type = PTYPE_S16, - .value.s16 = INT16_MIN, - }, - { - .description = "int16_max", - .type = PTYPE_S16, - .value.s16 = INT16_MAX, - }, - /* int32 tests */ - { - .description = "int32_sanity1", - .type = PTYPE_S32, - .value.s32 = -1, - }, - { - .description = "int32_sanity2", - .type = PTYPE_S32, - .value.s32 = INT32_MAX / 2 + 1, - }, - { - .description = "int32_min", - .type = PTYPE_S32, - .value.s32 = INT32_MIN, - }, - { - .description = "int32_max", - .type = PTYPE_S32, - .value.s32 = INT32_MAX, - }, - /* int64 tests */ - { - .description = "int64_sanity1", - .type = PTYPE_S64, - .value.s64 = -1, - }, - { - .description = "int64_sanity2", - .type = PTYPE_S64, - .value.s64 = INT64_MAX / 2 + 1, - }, - { - .description = "int64_min", - .type = PTYPE_S64, - .value.s64 = INT64_MIN, - }, - { - .description = "int64_max", - .type = PTYPE_S64, - .value.s64 = INT64_MAX, - }, - { .type = PTYPE_EOL } -}; - -/* visitor-specific op implementations */ - -typedef struct QmpSerializeData { - Visitor *qov; - QObject *obj; - Visitor *qiv; -} QmpSerializeData; - -static void qmp_serialize(void *native_in, void **datap, - VisitorFunc visit, Error **errp) -{ - QmpSerializeData *d = g_malloc0(sizeof(*d)); - - d->qov = qobject_output_visitor_new(&d->obj); - visit(d->qov, &native_in, errp); - *datap = d; -} - -static void qmp_deserialize(void **native_out, void *datap, - VisitorFunc visit, Error **errp) -{ - QmpSerializeData *d = datap; - GString *output_json; - QObject *obj_orig, *obj; - - visit_complete(d->qov, &d->obj); - obj_orig = d->obj; - output_json = qobject_to_json(obj_orig); - obj = qobject_from_json(output_json->str, &error_abort); - - g_string_free(output_json, true); - d->qiv = qobject_input_visitor_new(obj); - qobject_unref(obj_orig); - qobject_unref(obj); - visit(d->qiv, native_out, errp); -} - -static void qmp_cleanup(void *datap) -{ - QmpSerializeData *d = datap; - visit_free(d->qov); - visit_free(d->qiv); - - g_free(d); -} - -typedef struct StringSerializeData { - char *string; - Visitor *sov; - Visitor *siv; -} StringSerializeData; - -static void string_serialize(void *native_in, void **datap, - VisitorFunc visit, Error **errp) -{ - StringSerializeData *d = g_malloc0(sizeof(*d)); - - d->sov = string_output_visitor_new(false, &d->string); - visit(d->sov, &native_in, errp); - *datap = d; -} - -static void string_deserialize(void **native_out, void *datap, - VisitorFunc visit, Error **errp) -{ - StringSerializeData *d = datap; - - visit_complete(d->sov, &d->string); - d->siv = string_input_visitor_new(d->string); - visit(d->siv, native_out, errp); -} - -static void string_cleanup(void *datap) -{ - StringSerializeData *d = datap; - - visit_free(d->sov); - visit_free(d->siv); - g_free(d->string); - g_free(d); -} - -/* visitor registration, test harness */ - -/* note: to function interchangeably as a serialization mechanism your - * visitor test implementation should pass the test cases for all visitor - * capabilities: primitives, structures, and lists - */ -static const SerializeOps visitors[] = { - { - .type = "QMP", - .serialize = qmp_serialize, - .deserialize = qmp_deserialize, - .cleanup = qmp_cleanup, - .caps = VCAP_PRIMITIVES | VCAP_STRUCTURES | VCAP_LISTS | - VCAP_PRIMITIVE_LISTS - }, - { - .type = "String", - .serialize = string_serialize, - .deserialize = string_deserialize, - .cleanup = string_cleanup, - .caps = VCAP_PRIMITIVES - }, - { NULL } -}; - -static void add_visitor_type(const SerializeOps *ops) -{ - char testname_prefix[32]; - char testname[128]; - TestArgs *args; - int i = 0; - - sprintf(testname_prefix, "/visitor/serialization/%s", ops->type); - - if (ops->caps & VCAP_PRIMITIVES) { - while (pt_values[i].type != PTYPE_EOL) { - sprintf(testname, "%s/primitives/%s", testname_prefix, - pt_values[i].description); - args = g_malloc0(sizeof(*args)); - args->ops = ops; - args->test_data = &pt_values[i]; - g_test_add_data_func(testname, args, test_primitives); - i++; - } - } - - if (ops->caps & VCAP_STRUCTURES) { - sprintf(testname, "%s/struct", testname_prefix); - args = g_malloc0(sizeof(*args)); - args->ops = ops; - args->test_data = NULL; - g_test_add_data_func(testname, args, test_struct); - - sprintf(testname, "%s/nested_struct", testname_prefix); - args = g_malloc0(sizeof(*args)); - args->ops = ops; - args->test_data = NULL; - g_test_add_data_func(testname, args, test_nested_struct); - } - - if (ops->caps & VCAP_LISTS) { - sprintf(testname, "%s/nested_struct_list", testname_prefix); - args = g_malloc0(sizeof(*args)); - args->ops = ops; - args->test_data = NULL; - g_test_add_data_func(testname, args, test_nested_struct_list); - } - - if (ops->caps & VCAP_PRIMITIVE_LISTS) { - i = 0; - while (pt_values[i].type != PTYPE_EOL) { - sprintf(testname, "%s/primitive_list/%s", testname_prefix, - pt_values[i].description); - args = g_malloc0(sizeof(*args)); - args->ops = ops; - args->test_data = &pt_values[i]; - g_test_add_data_func(testname, args, test_primitive_lists); - i++; - } - } -} - -int main(int argc, char **argv) -{ - int i = 0; - - g_test_init(&argc, &argv, NULL); - - while (visitors[i].type != NULL) { - add_visitor_type(&visitors[i]); - i++; - } - - g_test_run(); - - return 0; -} diff --git a/tests/test-vmstate.c b/tests/test-vmstate.c deleted file mode 100644 index a001879585..0000000000 --- a/tests/test-vmstate.c +++ /dev/null @@ -1,1529 +0,0 @@ -/* - * Test code for VMState - * - * Copyright (c) 2013 Red Hat Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" - -#include "../migration/migration.h" -#include "migration/vmstate.h" -#include "migration/qemu-file-types.h" -#include "../migration/qemu-file.h" -#include "../migration/qemu-file-channel.h" -#include "../migration/savevm.h" -#include "qemu/coroutine.h" -#include "qemu/module.h" -#include "io/channel-file.h" - -static int temp_fd; - - -/* Duplicate temp_fd and seek to the beginning of the file */ -static QEMUFile *open_test_file(bool write) -{ - int fd = dup(temp_fd); - QIOChannel *ioc; - QEMUFile *f; - - lseek(fd, 0, SEEK_SET); - if (write) { - g_assert_cmpint(ftruncate(fd, 0), ==, 0); - } - ioc = QIO_CHANNEL(qio_channel_file_new_fd(fd)); - if (write) { - f = qemu_fopen_channel_output(ioc); - } else { - f = qemu_fopen_channel_input(ioc); - } - object_unref(OBJECT(ioc)); - return f; -} - -#define SUCCESS(val) \ - g_assert_cmpint((val), ==, 0) - -#define FAILURE(val) \ - g_assert_cmpint((val), !=, 0) - -static void save_vmstate(const VMStateDescription *desc, void *obj) -{ - QEMUFile *f = open_test_file(true); - - /* Save file with vmstate */ - int ret = vmstate_save_state(f, desc, obj, NULL); - g_assert(!ret); - qemu_put_byte(f, QEMU_VM_EOF); - g_assert(!qemu_file_get_error(f)); - qemu_fclose(f); -} - -static void save_buffer(const uint8_t *buf, size_t buf_size) -{ - QEMUFile *fsave = open_test_file(true); - qemu_put_buffer(fsave, buf, buf_size); - qemu_fclose(fsave); -} - -static void compare_vmstate(const uint8_t *wire, size_t size) -{ - QEMUFile *f = open_test_file(false); - uint8_t result[size]; - - /* read back as binary */ - - g_assert_cmpint(qemu_get_buffer(f, result, sizeof(result)), ==, - sizeof(result)); - g_assert(!qemu_file_get_error(f)); - - /* Compare that what is on the file is the same that what we - expected to be there */ - SUCCESS(memcmp(result, wire, sizeof(result))); - - /* Must reach EOF */ - qemu_get_byte(f); - g_assert_cmpint(qemu_file_get_error(f), ==, -EIO); - - qemu_fclose(f); -} - -static int load_vmstate_one(const VMStateDescription *desc, void *obj, - int version, const uint8_t *wire, size_t size) -{ - QEMUFile *f; - int ret; - - f = open_test_file(true); - qemu_put_buffer(f, wire, size); - qemu_fclose(f); - - f = open_test_file(false); - ret = vmstate_load_state(f, desc, obj, version); - if (ret) { - g_assert(qemu_file_get_error(f)); - } else{ - g_assert(!qemu_file_get_error(f)); - } - qemu_fclose(f); - return ret; -} - - -static int load_vmstate(const VMStateDescription *desc, - void *obj, void *obj_clone, - void (*obj_copy)(void *, void*), - int version, const uint8_t *wire, size_t size) -{ - /* We test with zero size */ - obj_copy(obj_clone, obj); - FAILURE(load_vmstate_one(desc, obj, version, wire, 0)); - - /* Stream ends with QEMU_EOF, so we need at least 3 bytes to be - * able to test in the middle */ - - if (size > 3) { - - /* We test with size - 2. We can't test size - 1 due to EOF tricks */ - obj_copy(obj, obj_clone); - FAILURE(load_vmstate_one(desc, obj, version, wire, size - 2)); - - /* Test with size/2, first half of real state */ - obj_copy(obj, obj_clone); - FAILURE(load_vmstate_one(desc, obj, version, wire, size/2)); - - /* Test with size/2, second half of real state */ - obj_copy(obj, obj_clone); - FAILURE(load_vmstate_one(desc, obj, version, wire + (size/2), size/2)); - - } - obj_copy(obj, obj_clone); - return load_vmstate_one(desc, obj, version, wire, size); -} - -/* Test struct that we are going to use for our tests */ - -typedef struct TestSimple { - bool b_1, b_2; - uint8_t u8_1; - uint16_t u16_1; - uint32_t u32_1; - uint64_t u64_1; - int8_t i8_1, i8_2; - int16_t i16_1, i16_2; - int32_t i32_1, i32_2; - int64_t i64_1, i64_2; -} TestSimple; - -/* Object instantiation, we are going to use it in more than one test */ - -TestSimple obj_simple = { - .b_1 = true, - .b_2 = false, - .u8_1 = 130, - .u16_1 = 512, - .u32_1 = 70000, - .u64_1 = 12121212, - .i8_1 = 65, - .i8_2 = -65, - .i16_1 = 512, - .i16_2 = -512, - .i32_1 = 70000, - .i32_2 = -70000, - .i64_1 = 12121212, - .i64_2 = -12121212, -}; - -/* Description of the values. If you add a primitive type - you are expected to add a test here */ - -static const VMStateDescription vmstate_simple_primitive = { - .name = "simple/primitive", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_BOOL(b_1, TestSimple), - VMSTATE_BOOL(b_2, TestSimple), - VMSTATE_UINT8(u8_1, TestSimple), - VMSTATE_UINT16(u16_1, TestSimple), - VMSTATE_UINT32(u32_1, TestSimple), - VMSTATE_UINT64(u64_1, TestSimple), - VMSTATE_INT8(i8_1, TestSimple), - VMSTATE_INT8(i8_2, TestSimple), - VMSTATE_INT16(i16_1, TestSimple), - VMSTATE_INT16(i16_2, TestSimple), - VMSTATE_INT32(i32_1, TestSimple), - VMSTATE_INT32(i32_2, TestSimple), - VMSTATE_INT64(i64_1, TestSimple), - VMSTATE_INT64(i64_2, TestSimple), - VMSTATE_END_OF_LIST() - } -}; - -/* It describes what goes through the wire. Our tests are basically: - - * save test - - save a struct a vmstate to a file - - read that file back (binary read, no vmstate) - - compare it with what we expect to be on the wire - * load test - - save to the file what we expect to be on the wire - - read struct back with vmstate in a different - - compare back with the original struct -*/ - -uint8_t wire_simple_primitive[] = { - /* b_1 */ 0x01, - /* b_2 */ 0x00, - /* u8_1 */ 0x82, - /* u16_1 */ 0x02, 0x00, - /* u32_1 */ 0x00, 0x01, 0x11, 0x70, - /* u64_1 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, 0xf4, 0x7c, - /* i8_1 */ 0x41, - /* i8_2 */ 0xbf, - /* i16_1 */ 0x02, 0x00, - /* i16_2 */ 0xfe, 0x0, - /* i32_1 */ 0x00, 0x01, 0x11, 0x70, - /* i32_2 */ 0xff, 0xfe, 0xee, 0x90, - /* i64_1 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, 0xf4, 0x7c, - /* i64_2 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0x47, 0x0b, 0x84, - QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */ -}; - -static void obj_simple_copy(void *target, void *source) -{ - memcpy(target, source, sizeof(TestSimple)); -} - -static void test_simple_primitive(void) -{ - TestSimple obj, obj_clone; - - memset(&obj, 0, sizeof(obj)); - save_vmstate(&vmstate_simple_primitive, &obj_simple); - - compare_vmstate(wire_simple_primitive, sizeof(wire_simple_primitive)); - - SUCCESS(load_vmstate(&vmstate_simple_primitive, &obj, &obj_clone, - obj_simple_copy, 1, wire_simple_primitive, - sizeof(wire_simple_primitive))); - -#define FIELD_EQUAL(name) g_assert_cmpint(obj.name, ==, obj_simple.name) - - FIELD_EQUAL(b_1); - FIELD_EQUAL(b_2); - FIELD_EQUAL(u8_1); - FIELD_EQUAL(u16_1); - FIELD_EQUAL(u32_1); - FIELD_EQUAL(u64_1); - FIELD_EQUAL(i8_1); - FIELD_EQUAL(i8_2); - FIELD_EQUAL(i16_1); - FIELD_EQUAL(i16_2); - FIELD_EQUAL(i32_1); - FIELD_EQUAL(i32_2); - FIELD_EQUAL(i64_1); - FIELD_EQUAL(i64_2); -} - -typedef struct TestSimpleArray { - uint16_t u16_1[3]; -} TestSimpleArray; - -/* Object instantiation, we are going to use it in more than one test */ - -TestSimpleArray obj_simple_arr = { - .u16_1 = { 0x42, 0x43, 0x44 }, -}; - -/* Description of the values. If you add a primitive type - you are expected to add a test here */ - -static const VMStateDescription vmstate_simple_arr = { - .name = "simple/array", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT16_ARRAY(u16_1, TestSimpleArray, 3), - VMSTATE_END_OF_LIST() - } -}; - -uint8_t wire_simple_arr[] = { - /* u16_1 */ 0x00, 0x42, - /* u16_1 */ 0x00, 0x43, - /* u16_1 */ 0x00, 0x44, - QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */ -}; - -static void obj_simple_arr_copy(void *target, void *source) -{ - memcpy(target, source, sizeof(TestSimpleArray)); -} - -static void test_simple_array(void) -{ - TestSimpleArray obj, obj_clone; - - memset(&obj, 0, sizeof(obj)); - save_vmstate(&vmstate_simple_arr, &obj_simple_arr); - - compare_vmstate(wire_simple_arr, sizeof(wire_simple_arr)); - - SUCCESS(load_vmstate(&vmstate_simple_arr, &obj, &obj_clone, - obj_simple_arr_copy, 1, wire_simple_arr, - sizeof(wire_simple_arr))); -} - -typedef struct TestStruct { - uint32_t a, b, c, e; - uint64_t d, f; - bool skip_c_e; -} TestStruct; - -static const VMStateDescription vmstate_versioned = { - .name = "test/versioned", - .version_id = 2, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(a, TestStruct), - VMSTATE_UINT32_V(b, TestStruct, 2), /* Versioned field in the middle, so - * we catch bugs more easily. - */ - VMSTATE_UINT32(c, TestStruct), - VMSTATE_UINT64(d, TestStruct), - VMSTATE_UINT32_V(e, TestStruct, 2), - VMSTATE_UINT64_V(f, TestStruct, 2), - VMSTATE_END_OF_LIST() - } -}; - -static void test_load_v1(void) -{ - uint8_t buf[] = { - 0, 0, 0, 10, /* a */ - 0, 0, 0, 30, /* c */ - 0, 0, 0, 0, 0, 0, 0, 40, /* d */ - QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */ - }; - save_buffer(buf, sizeof(buf)); - - QEMUFile *loading = open_test_file(false); - TestStruct obj = { .b = 200, .e = 500, .f = 600 }; - vmstate_load_state(loading, &vmstate_versioned, &obj, 1); - g_assert(!qemu_file_get_error(loading)); - g_assert_cmpint(obj.a, ==, 10); - g_assert_cmpint(obj.b, ==, 200); - g_assert_cmpint(obj.c, ==, 30); - g_assert_cmpint(obj.d, ==, 40); - g_assert_cmpint(obj.e, ==, 500); - g_assert_cmpint(obj.f, ==, 600); - qemu_fclose(loading); -} - -static void test_load_v2(void) -{ - uint8_t buf[] = { - 0, 0, 0, 10, /* a */ - 0, 0, 0, 20, /* b */ - 0, 0, 0, 30, /* c */ - 0, 0, 0, 0, 0, 0, 0, 40, /* d */ - 0, 0, 0, 50, /* e */ - 0, 0, 0, 0, 0, 0, 0, 60, /* f */ - QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */ - }; - save_buffer(buf, sizeof(buf)); - - QEMUFile *loading = open_test_file(false); - TestStruct obj; - vmstate_load_state(loading, &vmstate_versioned, &obj, 2); - g_assert_cmpint(obj.a, ==, 10); - g_assert_cmpint(obj.b, ==, 20); - g_assert_cmpint(obj.c, ==, 30); - g_assert_cmpint(obj.d, ==, 40); - g_assert_cmpint(obj.e, ==, 50); - g_assert_cmpint(obj.f, ==, 60); - qemu_fclose(loading); -} - -static bool test_skip(void *opaque, int version_id) -{ - TestStruct *t = (TestStruct *)opaque; - return !t->skip_c_e; -} - -static const VMStateDescription vmstate_skipping = { - .name = "test/skip", - .version_id = 2, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(a, TestStruct), - VMSTATE_UINT32(b, TestStruct), - VMSTATE_UINT32_TEST(c, TestStruct, test_skip), - VMSTATE_UINT64(d, TestStruct), - VMSTATE_UINT32_TEST(e, TestStruct, test_skip), - VMSTATE_UINT64_V(f, TestStruct, 2), - VMSTATE_END_OF_LIST() - } -}; - - -static void test_save_noskip(void) -{ - QEMUFile *fsave = open_test_file(true); - TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6, - .skip_c_e = false }; - int ret = vmstate_save_state(fsave, &vmstate_skipping, &obj, NULL); - g_assert(!ret); - g_assert(!qemu_file_get_error(fsave)); - - uint8_t expected[] = { - 0, 0, 0, 1, /* a */ - 0, 0, 0, 2, /* b */ - 0, 0, 0, 3, /* c */ - 0, 0, 0, 0, 0, 0, 0, 4, /* d */ - 0, 0, 0, 5, /* e */ - 0, 0, 0, 0, 0, 0, 0, 6, /* f */ - }; - - qemu_fclose(fsave); - compare_vmstate(expected, sizeof(expected)); -} - -static void test_save_skip(void) -{ - QEMUFile *fsave = open_test_file(true); - TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6, - .skip_c_e = true }; - int ret = vmstate_save_state(fsave, &vmstate_skipping, &obj, NULL); - g_assert(!ret); - g_assert(!qemu_file_get_error(fsave)); - - uint8_t expected[] = { - 0, 0, 0, 1, /* a */ - 0, 0, 0, 2, /* b */ - 0, 0, 0, 0, 0, 0, 0, 4, /* d */ - 0, 0, 0, 0, 0, 0, 0, 6, /* f */ - }; - - qemu_fclose(fsave); - compare_vmstate(expected, sizeof(expected)); -} - -static void test_load_noskip(void) -{ - uint8_t buf[] = { - 0, 0, 0, 10, /* a */ - 0, 0, 0, 20, /* b */ - 0, 0, 0, 30, /* c */ - 0, 0, 0, 0, 0, 0, 0, 40, /* d */ - 0, 0, 0, 50, /* e */ - 0, 0, 0, 0, 0, 0, 0, 60, /* f */ - QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */ - }; - save_buffer(buf, sizeof(buf)); - - QEMUFile *loading = open_test_file(false); - TestStruct obj = { .skip_c_e = false }; - vmstate_load_state(loading, &vmstate_skipping, &obj, 2); - g_assert(!qemu_file_get_error(loading)); - g_assert_cmpint(obj.a, ==, 10); - g_assert_cmpint(obj.b, ==, 20); - g_assert_cmpint(obj.c, ==, 30); - g_assert_cmpint(obj.d, ==, 40); - g_assert_cmpint(obj.e, ==, 50); - g_assert_cmpint(obj.f, ==, 60); - qemu_fclose(loading); -} - -static void test_load_skip(void) -{ - uint8_t buf[] = { - 0, 0, 0, 10, /* a */ - 0, 0, 0, 20, /* b */ - 0, 0, 0, 0, 0, 0, 0, 40, /* d */ - 0, 0, 0, 0, 0, 0, 0, 60, /* f */ - QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */ - }; - save_buffer(buf, sizeof(buf)); - - QEMUFile *loading = open_test_file(false); - TestStruct obj = { .skip_c_e = true, .c = 300, .e = 500 }; - vmstate_load_state(loading, &vmstate_skipping, &obj, 2); - g_assert(!qemu_file_get_error(loading)); - g_assert_cmpint(obj.a, ==, 10); - g_assert_cmpint(obj.b, ==, 20); - g_assert_cmpint(obj.c, ==, 300); - g_assert_cmpint(obj.d, ==, 40); - g_assert_cmpint(obj.e, ==, 500); - g_assert_cmpint(obj.f, ==, 60); - qemu_fclose(loading); -} - -typedef struct { - int32_t i; -} TestStructTriv; - -const VMStateDescription vmsd_tst = { - .name = "test/tst", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_INT32(i, TestStructTriv), - VMSTATE_END_OF_LIST() - } -}; - -/* test array migration */ - -#define AR_SIZE 4 - -typedef struct { - TestStructTriv *ar[AR_SIZE]; -} TestArrayOfPtrToStuct; - -const VMStateDescription vmsd_arps = { - .name = "test/arps", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_ARRAY_OF_POINTER_TO_STRUCT(ar, TestArrayOfPtrToStuct, - AR_SIZE, 0, vmsd_tst, TestStructTriv), - VMSTATE_END_OF_LIST() - } -}; - -static uint8_t wire_arr_ptr_no0[] = { - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x02, - 0x00, 0x00, 0x00, 0x03, - QEMU_VM_EOF -}; - -static void test_arr_ptr_str_no0_save(void) -{ - TestStructTriv ar[AR_SIZE] = {{.i = 0}, {.i = 1}, {.i = 2}, {.i = 3} }; - TestArrayOfPtrToStuct sample = {.ar = {&ar[0], &ar[1], &ar[2], &ar[3]} }; - - save_vmstate(&vmsd_arps, &sample); - compare_vmstate(wire_arr_ptr_no0, sizeof(wire_arr_ptr_no0)); -} - -static void test_arr_ptr_str_no0_load(void) -{ - TestStructTriv ar_gt[AR_SIZE] = {{.i = 0}, {.i = 1}, {.i = 2}, {.i = 3} }; - TestStructTriv ar[AR_SIZE] = {}; - TestArrayOfPtrToStuct obj = {.ar = {&ar[0], &ar[1], &ar[2], &ar[3]} }; - int idx; - - save_buffer(wire_arr_ptr_no0, sizeof(wire_arr_ptr_no0)); - SUCCESS(load_vmstate_one(&vmsd_arps, &obj, 1, - wire_arr_ptr_no0, sizeof(wire_arr_ptr_no0))); - for (idx = 0; idx < AR_SIZE; ++idx) { - /* compare the target array ar with the ground truth array ar_gt */ - g_assert_cmpint(ar_gt[idx].i, ==, ar[idx].i); - } -} - -static uint8_t wire_arr_ptr_0[] = { - 0x00, 0x00, 0x00, 0x00, - VMS_NULLPTR_MARKER, - 0x00, 0x00, 0x00, 0x02, - 0x00, 0x00, 0x00, 0x03, - QEMU_VM_EOF -}; - -static void test_arr_ptr_str_0_save(void) -{ - TestStructTriv ar[AR_SIZE] = {{.i = 0}, {.i = 1}, {.i = 2}, {.i = 3} }; - TestArrayOfPtrToStuct sample = {.ar = {&ar[0], NULL, &ar[2], &ar[3]} }; - - save_vmstate(&vmsd_arps, &sample); - compare_vmstate(wire_arr_ptr_0, sizeof(wire_arr_ptr_0)); -} - -static void test_arr_ptr_str_0_load(void) -{ - TestStructTriv ar_gt[AR_SIZE] = {{.i = 0}, {.i = 0}, {.i = 2}, {.i = 3} }; - TestStructTriv ar[AR_SIZE] = {}; - TestArrayOfPtrToStuct obj = {.ar = {&ar[0], NULL, &ar[2], &ar[3]} }; - int idx; - - save_buffer(wire_arr_ptr_0, sizeof(wire_arr_ptr_0)); - SUCCESS(load_vmstate_one(&vmsd_arps, &obj, 1, - wire_arr_ptr_0, sizeof(wire_arr_ptr_0))); - for (idx = 0; idx < AR_SIZE; ++idx) { - /* compare the target array ar with the ground truth array ar_gt */ - g_assert_cmpint(ar_gt[idx].i, ==, ar[idx].i); - } - for (idx = 0; idx < AR_SIZE; ++idx) { - if (idx == 1) { - g_assert_cmpint((uintptr_t)(obj.ar[idx]), ==, 0); - } else { - g_assert_cmpint((uintptr_t)(obj.ar[idx]), !=, 0); - } - } -} - -typedef struct TestArrayOfPtrToInt { - int32_t *ar[AR_SIZE]; -} TestArrayOfPtrToInt; - -const VMStateDescription vmsd_arpp = { - .name = "test/arps", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_ARRAY_OF_POINTER(ar, TestArrayOfPtrToInt, - AR_SIZE, 0, vmstate_info_int32, int32_t*), - VMSTATE_END_OF_LIST() - } -}; - -static void test_arr_ptr_prim_0_save(void) -{ - int32_t ar[AR_SIZE] = {0 , 1, 2, 3}; - TestArrayOfPtrToInt sample = {.ar = {&ar[0], NULL, &ar[2], &ar[3]} }; - - save_vmstate(&vmsd_arpp, &sample); - compare_vmstate(wire_arr_ptr_0, sizeof(wire_arr_ptr_0)); -} - -static void test_arr_ptr_prim_0_load(void) -{ - int32_t ar_gt[AR_SIZE] = {0, 1, 2, 3}; - int32_t ar[AR_SIZE] = {3 , 42, 1, 0}; - TestArrayOfPtrToInt obj = {.ar = {&ar[0], NULL, &ar[2], &ar[3]} }; - int idx; - - save_buffer(wire_arr_ptr_0, sizeof(wire_arr_ptr_0)); - SUCCESS(load_vmstate_one(&vmsd_arpp, &obj, 1, - wire_arr_ptr_0, sizeof(wire_arr_ptr_0))); - for (idx = 0; idx < AR_SIZE; ++idx) { - /* compare the target array ar with the ground truth array ar_gt */ - if (idx == 1) { - g_assert_cmpint(42, ==, ar[idx]); - } else { - g_assert_cmpint(ar_gt[idx], ==, ar[idx]); - } - } -} - -/* test QTAILQ migration */ -typedef struct TestQtailqElement TestQtailqElement; - -struct TestQtailqElement { - bool b; - uint8_t u8; - QTAILQ_ENTRY(TestQtailqElement) next; -}; - -typedef struct TestQtailq { - int16_t i16; - QTAILQ_HEAD(, TestQtailqElement) q; - int32_t i32; -} TestQtailq; - -static const VMStateDescription vmstate_q_element = { - .name = "test/queue-element", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_BOOL(b, TestQtailqElement), - VMSTATE_UINT8(u8, TestQtailqElement), - VMSTATE_END_OF_LIST() - }, -}; - -static const VMStateDescription vmstate_q = { - .name = "test/queue", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_INT16(i16, TestQtailq), - VMSTATE_QTAILQ_V(q, TestQtailq, 1, vmstate_q_element, TestQtailqElement, - next), - VMSTATE_INT32(i32, TestQtailq), - VMSTATE_END_OF_LIST() - } -}; - -uint8_t wire_q[] = { - /* i16 */ 0xfe, 0x0, - /* start of element 0 of q */ 0x01, - /* .b */ 0x01, - /* .u8 */ 0x82, - /* start of element 1 of q */ 0x01, - /* b */ 0x00, - /* u8 */ 0x41, - /* end of q */ 0x00, - /* i32 */ 0x00, 0x01, 0x11, 0x70, - QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */ -}; - -static void test_save_q(void) -{ - TestQtailq obj_q = { - .i16 = -512, - .i32 = 70000, - }; - - TestQtailqElement obj_qe1 = { - .b = true, - .u8 = 130, - }; - - TestQtailqElement obj_qe2 = { - .b = false, - .u8 = 65, - }; - - QTAILQ_INIT(&obj_q.q); - QTAILQ_INSERT_TAIL(&obj_q.q, &obj_qe1, next); - QTAILQ_INSERT_TAIL(&obj_q.q, &obj_qe2, next); - - save_vmstate(&vmstate_q, &obj_q); - compare_vmstate(wire_q, sizeof(wire_q)); -} - -static void test_load_q(void) -{ - TestQtailq obj_q = { - .i16 = -512, - .i32 = 70000, - }; - - TestQtailqElement obj_qe1 = { - .b = true, - .u8 = 130, - }; - - TestQtailqElement obj_qe2 = { - .b = false, - .u8 = 65, - }; - - QTAILQ_INIT(&obj_q.q); - QTAILQ_INSERT_TAIL(&obj_q.q, &obj_qe1, next); - QTAILQ_INSERT_TAIL(&obj_q.q, &obj_qe2, next); - - QEMUFile *fsave = open_test_file(true); - - qemu_put_buffer(fsave, wire_q, sizeof(wire_q)); - g_assert(!qemu_file_get_error(fsave)); - qemu_fclose(fsave); - - QEMUFile *fload = open_test_file(false); - TestQtailq tgt; - - QTAILQ_INIT(&tgt.q); - vmstate_load_state(fload, &vmstate_q, &tgt, 1); - char eof = qemu_get_byte(fload); - g_assert(!qemu_file_get_error(fload)); - g_assert_cmpint(tgt.i16, ==, obj_q.i16); - g_assert_cmpint(tgt.i32, ==, obj_q.i32); - g_assert_cmpint(eof, ==, QEMU_VM_EOF); - - TestQtailqElement *qele_from = QTAILQ_FIRST(&obj_q.q); - TestQtailqElement *qlast_from = QTAILQ_LAST(&obj_q.q); - TestQtailqElement *qele_to = QTAILQ_FIRST(&tgt.q); - TestQtailqElement *qlast_to = QTAILQ_LAST(&tgt.q); - - while (1) { - g_assert_cmpint(qele_to->b, ==, qele_from->b); - g_assert_cmpint(qele_to->u8, ==, qele_from->u8); - if ((qele_from == qlast_from) || (qele_to == qlast_to)) { - break; - } - qele_from = QTAILQ_NEXT(qele_from, next); - qele_to = QTAILQ_NEXT(qele_to, next); - } - - g_assert_cmpint((uintptr_t) qele_from, ==, (uintptr_t) qlast_from); - g_assert_cmpint((uintptr_t) qele_to, ==, (uintptr_t) qlast_to); - - /* clean up */ - TestQtailqElement *qele; - while (!QTAILQ_EMPTY(&tgt.q)) { - qele = QTAILQ_LAST(&tgt.q); - QTAILQ_REMOVE(&tgt.q, qele, next); - free(qele); - qele = NULL; - } - qemu_fclose(fload); -} - -/* interval (key) */ -typedef struct TestGTreeInterval { - uint64_t low; - uint64_t high; -} TestGTreeInterval; - -#define VMSTATE_INTERVAL \ -{ \ - .name = "interval", \ - .version_id = 1, \ - .minimum_version_id = 1, \ - .fields = (VMStateField[]) { \ - VMSTATE_UINT64(low, TestGTreeInterval), \ - VMSTATE_UINT64(high, TestGTreeInterval), \ - VMSTATE_END_OF_LIST() \ - } \ -} - -/* mapping (value) */ -typedef struct TestGTreeMapping { - uint64_t phys_addr; - uint32_t flags; -} TestGTreeMapping; - -#define VMSTATE_MAPPING \ -{ \ - .name = "mapping", \ - .version_id = 1, \ - .minimum_version_id = 1, \ - .fields = (VMStateField[]) { \ - VMSTATE_UINT64(phys_addr, TestGTreeMapping), \ - VMSTATE_UINT32(flags, TestGTreeMapping), \ - VMSTATE_END_OF_LIST() \ - }, \ -} - -static const VMStateDescription vmstate_interval_mapping[2] = { - VMSTATE_MAPPING, /* value */ - VMSTATE_INTERVAL /* key */ -}; - -typedef struct TestGTreeDomain { - int32_t id; - GTree *mappings; -} TestGTreeDomain; - -typedef struct TestGTreeIOMMU { - int32_t id; - GTree *domains; -} TestGTreeIOMMU; - -/* Interval comparison function */ -static gint interval_cmp(gconstpointer a, gconstpointer b, gpointer user_data) -{ - TestGTreeInterval *inta = (TestGTreeInterval *)a; - TestGTreeInterval *intb = (TestGTreeInterval *)b; - - if (inta->high < intb->low) { - return -1; - } else if (intb->high < inta->low) { - return 1; - } else { - return 0; - } -} - -/* ID comparison function */ -static gint int_cmp(gconstpointer a, gconstpointer b, gpointer user_data) -{ - guint ua = GPOINTER_TO_UINT(a); - guint ub = GPOINTER_TO_UINT(b); - return (ua > ub) - (ua < ub); -} - -static void destroy_domain(gpointer data) -{ - TestGTreeDomain *domain = (TestGTreeDomain *)data; - - g_tree_destroy(domain->mappings); - g_free(domain); -} - -static int domain_preload(void *opaque) -{ - TestGTreeDomain *domain = opaque; - - domain->mappings = g_tree_new_full((GCompareDataFunc)interval_cmp, - NULL, g_free, g_free); - return 0; -} - -static int iommu_preload(void *opaque) -{ - TestGTreeIOMMU *iommu = opaque; - - iommu->domains = g_tree_new_full((GCompareDataFunc)int_cmp, - NULL, NULL, destroy_domain); - return 0; -} - -static const VMStateDescription vmstate_domain = { - .name = "domain", - .version_id = 1, - .minimum_version_id = 1, - .pre_load = domain_preload, - .fields = (VMStateField[]) { - VMSTATE_INT32(id, TestGTreeDomain), - VMSTATE_GTREE_V(mappings, TestGTreeDomain, 1, - vmstate_interval_mapping, - TestGTreeInterval, TestGTreeMapping), - VMSTATE_END_OF_LIST() - } -}; - -/* test QLIST Migration */ - -typedef struct TestQListElement { - uint32_t id; - QLIST_ENTRY(TestQListElement) next; -} TestQListElement; - -typedef struct TestQListContainer { - uint32_t id; - QLIST_HEAD(, TestQListElement) list; -} TestQListContainer; - -static const VMStateDescription vmstate_qlist_element = { - .name = "test/queue list", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(id, TestQListElement), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_iommu = { - .name = "iommu", - .version_id = 1, - .minimum_version_id = 1, - .pre_load = iommu_preload, - .fields = (VMStateField[]) { - VMSTATE_INT32(id, TestGTreeIOMMU), - VMSTATE_GTREE_DIRECT_KEY_V(domains, TestGTreeIOMMU, 1, - &vmstate_domain, TestGTreeDomain), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_container = { - .name = "test/container/qlist", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(id, TestQListContainer), - VMSTATE_QLIST_V(list, TestQListContainer, 1, vmstate_qlist_element, - TestQListElement, next), - VMSTATE_END_OF_LIST() - } -}; - -uint8_t first_domain_dump[] = { - /* id */ - 0x00, 0x0, 0x0, 0x6, - 0x00, 0x0, 0x0, 0x2, /* 2 mappings */ - 0x1, /* start of a */ - /* a */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, - /* map_a */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x00, - 0x00, 0x00, 0x00, 0x01, - 0x1, /* start of b */ - /* b */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0xFF, - /* map_b */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x02, - 0x0, /* end of gtree */ - QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */ -}; - -static TestGTreeDomain *create_first_domain(void) -{ - TestGTreeDomain *domain; - TestGTreeMapping *map_a, *map_b; - TestGTreeInterval *a, *b; - - domain = g_malloc0(sizeof(TestGTreeDomain)); - domain->id = 6; - - a = g_malloc0(sizeof(TestGTreeInterval)); - a->low = 0x1000; - a->high = 0x1FFF; - - b = g_malloc0(sizeof(TestGTreeInterval)); - b->low = 0x4000; - b->high = 0x4FFF; - - map_a = g_malloc0(sizeof(TestGTreeMapping)); - map_a->phys_addr = 0xa000; - map_a->flags = 1; - - map_b = g_malloc0(sizeof(TestGTreeMapping)); - map_b->phys_addr = 0xe0000; - map_b->flags = 2; - - domain->mappings = g_tree_new_full((GCompareDataFunc)interval_cmp, NULL, - (GDestroyNotify)g_free, - (GDestroyNotify)g_free); - g_tree_insert(domain->mappings, a, map_a); - g_tree_insert(domain->mappings, b, map_b); - return domain; -} - -static void test_gtree_save_domain(void) -{ - TestGTreeDomain *first_domain = create_first_domain(); - - save_vmstate(&vmstate_domain, first_domain); - compare_vmstate(first_domain_dump, sizeof(first_domain_dump)); - destroy_domain(first_domain); -} - -struct match_node_data { - GTree *tree; - gpointer key; - gpointer value; -}; - -struct tree_cmp_data { - GTree *tree1; - GTree *tree2; - GTraverseFunc match_node; -}; - -static gboolean match_interval_mapping_node(gpointer key, - gpointer value, gpointer data) -{ - TestGTreeMapping *map_a, *map_b; - TestGTreeInterval *a, *b; - struct match_node_data *d = (struct match_node_data *)data; - a = (TestGTreeInterval *)key; - b = (TestGTreeInterval *)d->key; - - map_a = (TestGTreeMapping *)value; - map_b = (TestGTreeMapping *)d->value; - - assert(a->low == b->low); - assert(a->high == b->high); - assert(map_a->phys_addr == map_b->phys_addr); - assert(map_a->flags == map_b->flags); - g_tree_remove(d->tree, key); - return true; -} - -static gboolean diff_tree(gpointer key, gpointer value, gpointer data) -{ - struct tree_cmp_data *tp = (struct tree_cmp_data *)data; - struct match_node_data d = {tp->tree2, key, value}; - - g_tree_foreach(tp->tree2, tp->match_node, &d); - g_tree_remove(tp->tree1, key); - return false; -} - -static void compare_trees(GTree *tree1, GTree *tree2, - GTraverseFunc function) -{ - struct tree_cmp_data tp = {tree1, tree2, function}; - - g_tree_foreach(tree1, diff_tree, &tp); - assert(g_tree_nnodes(tree1) == 0); - assert(g_tree_nnodes(tree2) == 0); -} - -static void diff_domain(TestGTreeDomain *d1, TestGTreeDomain *d2) -{ - assert(d1->id == d2->id); - compare_trees(d1->mappings, d2->mappings, match_interval_mapping_node); -} - -static gboolean match_domain_node(gpointer key, gpointer value, gpointer data) -{ - uint64_t id1, id2; - TestGTreeDomain *d1, *d2; - struct match_node_data *d = (struct match_node_data *)data; - - id1 = (uint64_t)(uintptr_t)key; - id2 = (uint64_t)(uintptr_t)d->key; - d1 = (TestGTreeDomain *)value; - d2 = (TestGTreeDomain *)d->value; - assert(id1 == id2); - diff_domain(d1, d2); - g_tree_remove(d->tree, key); - return true; -} - -static void diff_iommu(TestGTreeIOMMU *iommu1, TestGTreeIOMMU *iommu2) -{ - assert(iommu1->id == iommu2->id); - compare_trees(iommu1->domains, iommu2->domains, match_domain_node); -} - -static void test_gtree_load_domain(void) -{ - TestGTreeDomain *dest_domain = g_malloc0(sizeof(TestGTreeDomain)); - TestGTreeDomain *orig_domain = create_first_domain(); - QEMUFile *fload, *fsave; - char eof; - - fsave = open_test_file(true); - qemu_put_buffer(fsave, first_domain_dump, sizeof(first_domain_dump)); - g_assert(!qemu_file_get_error(fsave)); - qemu_fclose(fsave); - - fload = open_test_file(false); - - vmstate_load_state(fload, &vmstate_domain, dest_domain, 1); - eof = qemu_get_byte(fload); - g_assert(!qemu_file_get_error(fload)); - g_assert_cmpint(orig_domain->id, ==, dest_domain->id); - g_assert_cmpint(eof, ==, QEMU_VM_EOF); - - diff_domain(orig_domain, dest_domain); - destroy_domain(orig_domain); - destroy_domain(dest_domain); - qemu_fclose(fload); -} - -uint8_t iommu_dump[] = { - /* iommu id */ - 0x00, 0x0, 0x0, 0x7, - 0x00, 0x0, 0x0, 0x2, /* 2 domains */ - 0x1,/* start of domain 5 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x0, 0x0, 0x5, /* key = 5 */ - 0x00, 0x0, 0x0, 0x5, /* domain1 id */ - 0x00, 0x0, 0x0, 0x1, /* 1 mapping */ - 0x1, /* start of mappings */ - /* c */ - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, - /* map_c */ - 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, - 0x00, 0x0, 0x0, 0x3, - 0x0, /* end of domain1 mappings*/ - 0x1,/* start of domain 6 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x0, 0x0, 0x6, /* key = 6 */ - 0x00, 0x0, 0x0, 0x6, /* domain6 id */ - 0x00, 0x0, 0x0, 0x2, /* 2 mappings */ - 0x1, /* start of a */ - /* a */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, - /* map_a */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x00, - 0x00, 0x00, 0x00, 0x01, - 0x1, /* start of b */ - /* b */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0xFF, - /* map_b */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x02, - 0x0, /* end of domain6 mappings*/ - 0x0, /* end of domains */ - QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */ -}; - -static TestGTreeIOMMU *create_iommu(void) -{ - TestGTreeIOMMU *iommu = g_malloc0(sizeof(TestGTreeIOMMU)); - TestGTreeDomain *first_domain = create_first_domain(); - TestGTreeDomain *second_domain; - TestGTreeMapping *map_c; - TestGTreeInterval *c; - - iommu->id = 7; - iommu->domains = g_tree_new_full((GCompareDataFunc)int_cmp, NULL, - NULL, - destroy_domain); - - second_domain = g_malloc0(sizeof(TestGTreeDomain)); - second_domain->id = 5; - second_domain->mappings = g_tree_new_full((GCompareDataFunc)interval_cmp, - NULL, - (GDestroyNotify)g_free, - (GDestroyNotify)g_free); - - g_tree_insert(iommu->domains, GUINT_TO_POINTER(6), first_domain); - g_tree_insert(iommu->domains, (gpointer)0x0000000000000005, second_domain); - - c = g_malloc0(sizeof(TestGTreeInterval)); - c->low = 0x1000000; - c->high = 0x1FFFFFF; - - map_c = g_malloc0(sizeof(TestGTreeMapping)); - map_c->phys_addr = 0xF000000; - map_c->flags = 0x3; - - g_tree_insert(second_domain->mappings, c, map_c); - return iommu; -} - -static void destroy_iommu(TestGTreeIOMMU *iommu) -{ - g_tree_destroy(iommu->domains); - g_free(iommu); -} - -static void test_gtree_save_iommu(void) -{ - TestGTreeIOMMU *iommu = create_iommu(); - - save_vmstate(&vmstate_iommu, iommu); - compare_vmstate(iommu_dump, sizeof(iommu_dump)); - destroy_iommu(iommu); -} - -static void test_gtree_load_iommu(void) -{ - TestGTreeIOMMU *dest_iommu = g_malloc0(sizeof(TestGTreeIOMMU)); - TestGTreeIOMMU *orig_iommu = create_iommu(); - QEMUFile *fsave, *fload; - char eof; - - fsave = open_test_file(true); - qemu_put_buffer(fsave, iommu_dump, sizeof(iommu_dump)); - g_assert(!qemu_file_get_error(fsave)); - qemu_fclose(fsave); - - fload = open_test_file(false); - vmstate_load_state(fload, &vmstate_iommu, dest_iommu, 1); - eof = qemu_get_byte(fload); - g_assert(!qemu_file_get_error(fload)); - g_assert_cmpint(orig_iommu->id, ==, dest_iommu->id); - g_assert_cmpint(eof, ==, QEMU_VM_EOF); - - diff_iommu(orig_iommu, dest_iommu); - destroy_iommu(orig_iommu); - destroy_iommu(dest_iommu); - qemu_fclose(fload); -} - -static uint8_t qlist_dump[] = { - 0x00, 0x00, 0x00, 0x01, /* container id */ - 0x1, /* start of a */ - 0x00, 0x00, 0x00, 0x0a, - 0x1, /* start of b */ - 0x00, 0x00, 0x0b, 0x00, - 0x1, /* start of c */ - 0x00, 0x0c, 0x00, 0x00, - 0x1, /* start of d */ - 0x0d, 0x00, 0x00, 0x00, - 0x0, /* end of list */ - QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */ -}; - -static TestQListContainer *alloc_container(void) -{ - TestQListElement *a = g_malloc(sizeof(TestQListElement)); - TestQListElement *b = g_malloc(sizeof(TestQListElement)); - TestQListElement *c = g_malloc(sizeof(TestQListElement)); - TestQListElement *d = g_malloc(sizeof(TestQListElement)); - TestQListContainer *container = g_malloc(sizeof(TestQListContainer)); - - a->id = 0x0a; - b->id = 0x0b00; - c->id = 0xc0000; - d->id = 0xd000000; - container->id = 1; - - QLIST_INIT(&container->list); - QLIST_INSERT_HEAD(&container->list, d, next); - QLIST_INSERT_HEAD(&container->list, c, next); - QLIST_INSERT_HEAD(&container->list, b, next); - QLIST_INSERT_HEAD(&container->list, a, next); - return container; -} - -static void free_container(TestQListContainer *container) -{ - TestQListElement *iter, *tmp; - - QLIST_FOREACH_SAFE(iter, &container->list, next, tmp) { - QLIST_REMOVE(iter, next); - g_free(iter); - } - g_free(container); -} - -static void compare_containers(TestQListContainer *c1, TestQListContainer *c2) -{ - TestQListElement *first_item_c1, *first_item_c2; - - while (!QLIST_EMPTY(&c1->list)) { - first_item_c1 = QLIST_FIRST(&c1->list); - first_item_c2 = QLIST_FIRST(&c2->list); - assert(first_item_c2); - assert(first_item_c1->id == first_item_c2->id); - QLIST_REMOVE(first_item_c1, next); - QLIST_REMOVE(first_item_c2, next); - g_free(first_item_c1); - g_free(first_item_c2); - } - assert(QLIST_EMPTY(&c2->list)); -} - -/* - * Check the prev & next fields are correct by doing list - * manipulations on the container. We will do that for both - * the source and the destination containers - */ -static void manipulate_container(TestQListContainer *c) -{ - TestQListElement *prev = NULL, *iter = QLIST_FIRST(&c->list); - TestQListElement *elem; - - elem = g_malloc(sizeof(TestQListElement)); - elem->id = 0x12; - QLIST_INSERT_AFTER(iter, elem, next); - - elem = g_malloc(sizeof(TestQListElement)); - elem->id = 0x13; - QLIST_INSERT_HEAD(&c->list, elem, next); - - while (iter) { - prev = iter; - iter = QLIST_NEXT(iter, next); - } - - elem = g_malloc(sizeof(TestQListElement)); - elem->id = 0x14; - QLIST_INSERT_BEFORE(prev, elem, next); - - elem = g_malloc(sizeof(TestQListElement)); - elem->id = 0x15; - QLIST_INSERT_AFTER(prev, elem, next); - - QLIST_REMOVE(prev, next); - g_free(prev); -} - -static void test_save_qlist(void) -{ - TestQListContainer *container = alloc_container(); - - save_vmstate(&vmstate_container, container); - compare_vmstate(qlist_dump, sizeof(qlist_dump)); - free_container(container); -} - -static void test_load_qlist(void) -{ - QEMUFile *fsave, *fload; - TestQListContainer *orig_container = alloc_container(); - TestQListContainer *dest_container = g_malloc0(sizeof(TestQListContainer)); - char eof; - - QLIST_INIT(&dest_container->list); - - fsave = open_test_file(true); - qemu_put_buffer(fsave, qlist_dump, sizeof(qlist_dump)); - g_assert(!qemu_file_get_error(fsave)); - qemu_fclose(fsave); - - fload = open_test_file(false); - vmstate_load_state(fload, &vmstate_container, dest_container, 1); - eof = qemu_get_byte(fload); - g_assert(!qemu_file_get_error(fload)); - g_assert_cmpint(eof, ==, QEMU_VM_EOF); - manipulate_container(orig_container); - manipulate_container(dest_container); - compare_containers(orig_container, dest_container); - free_container(orig_container); - free_container(dest_container); - qemu_fclose(fload); -} - -typedef struct TmpTestStruct { - TestStruct *parent; - int64_t diff; -} TmpTestStruct; - -static int tmp_child_pre_save(void *opaque) -{ - struct TmpTestStruct *tts = opaque; - - tts->diff = tts->parent->b - tts->parent->a; - - return 0; -} - -static int tmp_child_post_load(void *opaque, int version_id) -{ - struct TmpTestStruct *tts = opaque; - - tts->parent->b = tts->parent->a + tts->diff; - - return 0; -} - -static const VMStateDescription vmstate_tmp_back_to_parent = { - .name = "test/tmp_child_parent", - .fields = (VMStateField[]) { - VMSTATE_UINT64(f, TestStruct), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_tmp_child = { - .name = "test/tmp_child", - .pre_save = tmp_child_pre_save, - .post_load = tmp_child_post_load, - .fields = (VMStateField[]) { - VMSTATE_INT64(diff, TmpTestStruct), - VMSTATE_STRUCT_POINTER(parent, TmpTestStruct, - vmstate_tmp_back_to_parent, TestStruct), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_with_tmp = { - .name = "test/with_tmp", - .version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(a, TestStruct), - VMSTATE_UINT64(d, TestStruct), - VMSTATE_WITH_TMP(TestStruct, TmpTestStruct, vmstate_tmp_child), - VMSTATE_END_OF_LIST() - } -}; - -static void obj_tmp_copy(void *target, void *source) -{ - memcpy(target, source, sizeof(TestStruct)); -} - -static void test_tmp_struct(void) -{ - TestStruct obj, obj_clone; - - uint8_t const wire_with_tmp[] = { - /* u32 a */ 0x00, 0x00, 0x00, 0x02, - /* u64 d */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - /* diff */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, - /* u64 f */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, - QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */ - }; - - memset(&obj, 0, sizeof(obj)); - obj.a = 2; - obj.b = 4; - obj.d = 1; - obj.f = 8; - save_vmstate(&vmstate_with_tmp, &obj); - - compare_vmstate(wire_with_tmp, sizeof(wire_with_tmp)); - - memset(&obj, 0, sizeof(obj)); - SUCCESS(load_vmstate(&vmstate_with_tmp, &obj, &obj_clone, - obj_tmp_copy, 1, wire_with_tmp, - sizeof(wire_with_tmp))); - g_assert_cmpint(obj.a, ==, 2); /* From top level vmsd */ - g_assert_cmpint(obj.b, ==, 4); /* from the post_load */ - g_assert_cmpint(obj.d, ==, 1); /* From top level vmsd */ - g_assert_cmpint(obj.f, ==, 8); /* From the child->parent */ -} - -int main(int argc, char **argv) -{ - g_autofree char *temp_file = g_strdup_printf("%s/vmst.test.XXXXXX", - g_get_tmp_dir()); - temp_fd = mkstemp(temp_file); - - module_call_init(MODULE_INIT_QOM); - - g_setenv("QTEST_SILENT_ERRORS", "1", 1); - - g_test_init(&argc, &argv, NULL); - g_test_add_func("/vmstate/simple/primitive", test_simple_primitive); - g_test_add_func("/vmstate/simple/array", test_simple_array); - g_test_add_func("/vmstate/versioned/load/v1", test_load_v1); - g_test_add_func("/vmstate/versioned/load/v2", test_load_v2); - g_test_add_func("/vmstate/field_exists/load/noskip", test_load_noskip); - g_test_add_func("/vmstate/field_exists/load/skip", test_load_skip); - g_test_add_func("/vmstate/field_exists/save/noskip", test_save_noskip); - g_test_add_func("/vmstate/field_exists/save/skip", test_save_skip); - g_test_add_func("/vmstate/array/ptr/str/no0/save", - test_arr_ptr_str_no0_save); - g_test_add_func("/vmstate/array/ptr/str/no0/load", - test_arr_ptr_str_no0_load); - g_test_add_func("/vmstate/array/ptr/str/0/save", test_arr_ptr_str_0_save); - g_test_add_func("/vmstate/array/ptr/str/0/load", - test_arr_ptr_str_0_load); - g_test_add_func("/vmstate/array/ptr/prim/0/save", - test_arr_ptr_prim_0_save); - g_test_add_func("/vmstate/array/ptr/prim/0/load", - test_arr_ptr_prim_0_load); - g_test_add_func("/vmstate/qtailq/save/saveq", test_save_q); - g_test_add_func("/vmstate/qtailq/load/loadq", test_load_q); - g_test_add_func("/vmstate/gtree/save/savedomain", test_gtree_save_domain); - g_test_add_func("/vmstate/gtree/load/loaddomain", test_gtree_load_domain); - g_test_add_func("/vmstate/gtree/save/saveiommu", test_gtree_save_iommu); - g_test_add_func("/vmstate/gtree/load/loadiommu", test_gtree_load_iommu); - g_test_add_func("/vmstate/qlist/save/saveqlist", test_save_qlist); - g_test_add_func("/vmstate/qlist/load/loadqlist", test_load_qlist); - g_test_add_func("/vmstate/tmp_struct", test_tmp_struct); - g_test_run(); - - close(temp_fd); - unlink(temp_file); - - return 0; -} diff --git a/tests/test-write-threshold.c b/tests/test-write-threshold.c deleted file mode 100644 index fc1c45a2eb..0000000000 --- a/tests/test-write-threshold.c +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Test block device write threshold - * - * This work is licensed under the terms of the GNU LGPL, version 2 or later. - * See the COPYING.LIB file in the top-level directory. - * - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "block/block_int.h" -#include "block/write-threshold.h" - - -static void test_threshold_not_set_on_init(void) -{ - uint64_t res; - BlockDriverState bs; - memset(&bs, 0, sizeof(bs)); - - g_assert(!bdrv_write_threshold_is_set(&bs)); - - res = bdrv_write_threshold_get(&bs); - g_assert_cmpint(res, ==, 0); -} - -static void test_threshold_set_get(void) -{ - uint64_t threshold = 4 * 1024 * 1024; - uint64_t res; - BlockDriverState bs; - memset(&bs, 0, sizeof(bs)); - - bdrv_write_threshold_set(&bs, threshold); - - g_assert(bdrv_write_threshold_is_set(&bs)); - - res = bdrv_write_threshold_get(&bs); - g_assert_cmpint(res, ==, threshold); -} - -static void test_threshold_multi_set_get(void) -{ - uint64_t threshold1 = 4 * 1024 * 1024; - uint64_t threshold2 = 15 * 1024 * 1024; - uint64_t res; - BlockDriverState bs; - memset(&bs, 0, sizeof(bs)); - - bdrv_write_threshold_set(&bs, threshold1); - bdrv_write_threshold_set(&bs, threshold2); - res = bdrv_write_threshold_get(&bs); - g_assert_cmpint(res, ==, threshold2); -} - -static void test_threshold_not_trigger(void) -{ - uint64_t amount = 0; - uint64_t threshold = 4 * 1024 * 1024; - BlockDriverState bs; - BdrvTrackedRequest req; - - memset(&bs, 0, sizeof(bs)); - memset(&req, 0, sizeof(req)); - req.offset = 1024; - req.bytes = 1024; - - bdrv_check_request(req.offset, req.bytes, &error_abort); - - bdrv_write_threshold_set(&bs, threshold); - amount = bdrv_write_threshold_exceeded(&bs, &req); - g_assert_cmpuint(amount, ==, 0); -} - - -static void test_threshold_trigger(void) -{ - uint64_t amount = 0; - uint64_t threshold = 4 * 1024 * 1024; - BlockDriverState bs; - BdrvTrackedRequest req; - - memset(&bs, 0, sizeof(bs)); - memset(&req, 0, sizeof(req)); - req.offset = (4 * 1024 * 1024) - 1024; - req.bytes = 2 * 1024; - - bdrv_check_request(req.offset, req.bytes, &error_abort); - - bdrv_write_threshold_set(&bs, threshold); - amount = bdrv_write_threshold_exceeded(&bs, &req); - g_assert_cmpuint(amount, >=, 1024); -} - -typedef struct TestStruct { - const char *name; - void (*func)(void); -} TestStruct; - - -int main(int argc, char **argv) -{ - size_t i; - TestStruct tests[] = { - { "/write-threshold/not-set-on-init", - test_threshold_not_set_on_init }, - { "/write-threshold/set-get", - test_threshold_set_get }, - { "/write-threshold/multi-set-get", - test_threshold_multi_set_get }, - { "/write-threshold/not-trigger", - test_threshold_not_trigger }, - { "/write-threshold/trigger", - test_threshold_trigger }, - { NULL, NULL } - }; - - g_test_init(&argc, &argv, NULL); - for (i = 0; tests[i].name != NULL; i++) { - g_test_add_func(tests[i].name, tests[i].func); - } - return g_test_run(); -} diff --git a/tests/test-x86-cpuid.c b/tests/test-x86-cpuid.c deleted file mode 100644 index bfabc0403a..0000000000 --- a/tests/test-x86-cpuid.c +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Test code for x86 CPUID and Topology functions - * - * Copyright (c) 2012 Red Hat Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" - -#include "hw/i386/topology.h" - -static void test_topo_bits(void) -{ - X86CPUTopoInfo topo_info = {0}; - - /* simple tests for 1 thread per core, 1 core per die, 1 die per package */ - topo_info = (X86CPUTopoInfo) {1, 1, 1}; - g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 0); - g_assert_cmpuint(apicid_core_width(&topo_info), ==, 0); - g_assert_cmpuint(apicid_die_width(&topo_info), ==, 0); - - topo_info = (X86CPUTopoInfo) {1, 1, 1}; - g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 0), ==, 0); - g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 1), ==, 1); - g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 2), ==, 2); - g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 3), ==, 3); - - - /* Test field width calculation for multiple values - */ - topo_info = (X86CPUTopoInfo) {1, 1, 2}; - g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 1); - topo_info = (X86CPUTopoInfo) {1, 1, 3}; - g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 2); - topo_info = (X86CPUTopoInfo) {1, 1, 4}; - g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 2); - - topo_info = (X86CPUTopoInfo) {1, 1, 14}; - g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 4); - topo_info = (X86CPUTopoInfo) {1, 1, 15}; - g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 4); - topo_info = (X86CPUTopoInfo) {1, 1, 16}; - g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 4); - topo_info = (X86CPUTopoInfo) {1, 1, 17}; - g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 5); - - - topo_info = (X86CPUTopoInfo) {1, 30, 2}; - g_assert_cmpuint(apicid_core_width(&topo_info), ==, 5); - topo_info = (X86CPUTopoInfo) {1, 31, 2}; - g_assert_cmpuint(apicid_core_width(&topo_info), ==, 5); - topo_info = (X86CPUTopoInfo) {1, 32, 2}; - g_assert_cmpuint(apicid_core_width(&topo_info), ==, 5); - topo_info = (X86CPUTopoInfo) {1, 33, 2}; - g_assert_cmpuint(apicid_core_width(&topo_info), ==, 6); - - topo_info = (X86CPUTopoInfo) {1, 30, 2}; - g_assert_cmpuint(apicid_die_width(&topo_info), ==, 0); - topo_info = (X86CPUTopoInfo) {2, 30, 2}; - g_assert_cmpuint(apicid_die_width(&topo_info), ==, 1); - topo_info = (X86CPUTopoInfo) {3, 30, 2}; - g_assert_cmpuint(apicid_die_width(&topo_info), ==, 2); - topo_info = (X86CPUTopoInfo) {4, 30, 2}; - g_assert_cmpuint(apicid_die_width(&topo_info), ==, 2); - - /* build a weird topology and see if IDs are calculated correctly - */ - - /* This will use 2 bits for thread ID and 3 bits for core ID - */ - topo_info = (X86CPUTopoInfo) {1, 6, 3}; - g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 2); - g_assert_cmpuint(apicid_core_offset(&topo_info), ==, 2); - g_assert_cmpuint(apicid_die_offset(&topo_info), ==, 5); - g_assert_cmpuint(apicid_pkg_offset(&topo_info), ==, 5); - - topo_info = (X86CPUTopoInfo) {1, 6, 3}; - g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 0), ==, 0); - g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 1), ==, 1); - g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 2), ==, 2); - - topo_info = (X86CPUTopoInfo) {1, 6, 3}; - g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 1 * 3 + 0), ==, - (1 << 2) | 0); - g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 1 * 3 + 1), ==, - (1 << 2) | 1); - g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 1 * 3 + 2), ==, - (1 << 2) | 2); - - g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 2 * 3 + 0), ==, - (2 << 2) | 0); - g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 2 * 3 + 1), ==, - (2 << 2) | 1); - g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 2 * 3 + 2), ==, - (2 << 2) | 2); - - g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 5 * 3 + 0), ==, - (5 << 2) | 0); - g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 5 * 3 + 1), ==, - (5 << 2) | 1); - g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 5 * 3 + 2), ==, - (5 << 2) | 2); - - g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, - 1 * 6 * 3 + 0 * 3 + 0), ==, (1 << 5)); - g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, - 1 * 6 * 3 + 1 * 3 + 1), ==, (1 << 5) | (1 << 2) | 1); - g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, - 3 * 6 * 3 + 5 * 3 + 2), ==, (3 << 5) | (5 << 2) | 2); -} - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - - g_test_add_func("/cpuid/topology/basic", test_topo_bits); - - g_test_run(); - - return 0; -} diff --git a/tests/test-xbzrle.c b/tests/test-xbzrle.c deleted file mode 100644 index 795d6f1cba..0000000000 --- a/tests/test-xbzrle.c +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Xor Based Zero Run Length Encoding unit tests. - * - * Copyright 2013 Red Hat, Inc. and/or its affiliates - * - * Authors: - * Orit Wasserman - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "qemu/cutils.h" -#include "../migration/xbzrle.h" - -#define XBZRLE_PAGE_SIZE 4096 - -static void test_uleb(void) -{ - uint32_t i, val; - uint8_t buf[2]; - int encode_ret, decode_ret; - - for (i = 0; i <= 0x3fff; i++) { - encode_ret = uleb128_encode_small(&buf[0], i); - decode_ret = uleb128_decode_small(&buf[0], &val); - g_assert(encode_ret == decode_ret); - g_assert(i == val); - } - - /* decode invalid value */ - buf[0] = 0x80; - buf[1] = 0x80; - - decode_ret = uleb128_decode_small(&buf[0], &val); - g_assert(decode_ret == -1); - g_assert(val == 0); -} - -static void test_encode_decode_zero(void) -{ - uint8_t *buffer = g_malloc0(XBZRLE_PAGE_SIZE); - uint8_t *compressed = g_malloc0(XBZRLE_PAGE_SIZE); - int i = 0; - int dlen = 0; - int diff_len = g_test_rand_int_range(0, XBZRLE_PAGE_SIZE - 1006); - - for (i = diff_len; i > 0; i--) { - buffer[1000 + i] = i; - } - - buffer[1000 + diff_len + 3] = 103; - buffer[1000 + diff_len + 5] = 105; - - /* encode zero page */ - dlen = xbzrle_encode_buffer(buffer, buffer, XBZRLE_PAGE_SIZE, compressed, - XBZRLE_PAGE_SIZE); - g_assert(dlen == 0); - - g_free(buffer); - g_free(compressed); -} - -static void test_encode_decode_unchanged(void) -{ - uint8_t *compressed = g_malloc0(XBZRLE_PAGE_SIZE); - uint8_t *test = g_malloc0(XBZRLE_PAGE_SIZE); - int i = 0; - int dlen = 0; - int diff_len = g_test_rand_int_range(0, XBZRLE_PAGE_SIZE - 1006); - - for (i = diff_len; i > 0; i--) { - test[1000 + i] = i + 4; - } - - test[1000 + diff_len + 3] = 107; - test[1000 + diff_len + 5] = 109; - - /* test unchanged buffer */ - dlen = xbzrle_encode_buffer(test, test, XBZRLE_PAGE_SIZE, compressed, - XBZRLE_PAGE_SIZE); - g_assert(dlen == 0); - - g_free(test); - g_free(compressed); -} - -static void test_encode_decode_1_byte(void) -{ - uint8_t *buffer = g_malloc0(XBZRLE_PAGE_SIZE); - uint8_t *test = g_malloc0(XBZRLE_PAGE_SIZE); - uint8_t *compressed = g_malloc(XBZRLE_PAGE_SIZE); - int dlen = 0, rc = 0; - uint8_t buf[2]; - - test[XBZRLE_PAGE_SIZE - 1] = 1; - - dlen = xbzrle_encode_buffer(buffer, test, XBZRLE_PAGE_SIZE, compressed, - XBZRLE_PAGE_SIZE); - g_assert(dlen == (uleb128_encode_small(&buf[0], 4095) + 2)); - - rc = xbzrle_decode_buffer(compressed, dlen, buffer, XBZRLE_PAGE_SIZE); - g_assert(rc == XBZRLE_PAGE_SIZE); - g_assert(memcmp(test, buffer, XBZRLE_PAGE_SIZE) == 0); - - g_free(buffer); - g_free(compressed); - g_free(test); -} - -static void test_encode_decode_overflow(void) -{ - uint8_t *compressed = g_malloc0(XBZRLE_PAGE_SIZE); - uint8_t *test = g_malloc0(XBZRLE_PAGE_SIZE); - uint8_t *buffer = g_malloc0(XBZRLE_PAGE_SIZE); - int i = 0, rc = 0; - - for (i = 0; i < XBZRLE_PAGE_SIZE / 2 - 1; i++) { - test[i * 2] = 1; - } - - /* encode overflow */ - rc = xbzrle_encode_buffer(buffer, test, XBZRLE_PAGE_SIZE, compressed, - XBZRLE_PAGE_SIZE); - g_assert(rc == -1); - - g_free(buffer); - g_free(compressed); - g_free(test); -} - -static void encode_decode_range(void) -{ - uint8_t *buffer = g_malloc0(XBZRLE_PAGE_SIZE); - uint8_t *compressed = g_malloc(XBZRLE_PAGE_SIZE); - uint8_t *test = g_malloc0(XBZRLE_PAGE_SIZE); - int i = 0, rc = 0; - int dlen = 0; - - int diff_len = g_test_rand_int_range(0, XBZRLE_PAGE_SIZE - 1006); - - for (i = diff_len; i > 0; i--) { - buffer[1000 + i] = i; - test[1000 + i] = i + 4; - } - - buffer[1000 + diff_len + 3] = 103; - test[1000 + diff_len + 3] = 107; - - buffer[1000 + diff_len + 5] = 105; - test[1000 + diff_len + 5] = 109; - - /* test encode/decode */ - dlen = xbzrle_encode_buffer(test, buffer, XBZRLE_PAGE_SIZE, compressed, - XBZRLE_PAGE_SIZE); - - rc = xbzrle_decode_buffer(compressed, dlen, test, XBZRLE_PAGE_SIZE); - g_assert(rc < XBZRLE_PAGE_SIZE); - g_assert(memcmp(test, buffer, XBZRLE_PAGE_SIZE) == 0); - - g_free(buffer); - g_free(compressed); - g_free(test); -} - -static void test_encode_decode(void) -{ - int i; - - for (i = 0; i < 10000; i++) { - encode_decode_range(); - } -} - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - g_test_rand_int(); - g_test_add_func("/xbzrle/uleb", test_uleb); - g_test_add_func("/xbzrle/encode_decode_zero", test_encode_decode_zero); - g_test_add_func("/xbzrle/encode_decode_unchanged", - test_encode_decode_unchanged); - g_test_add_func("/xbzrle/encode_decode_1_byte", test_encode_decode_1_byte); - g_test_add_func("/xbzrle/encode_decode_overflow", - test_encode_decode_overflow); - g_test_add_func("/xbzrle/encode_decode", test_encode_decode); - - return g_test_run(); -} diff --git a/tests/unit/check-block-qdict.c b/tests/unit/check-block-qdict.c new file mode 100644 index 0000000000..5a25825093 --- /dev/null +++ b/tests/unit/check-block-qdict.c @@ -0,0 +1,712 @@ +/* + * Unit-tests for Block layer QDict extras + * + * Copyright (c) 2013-2018 Red Hat, Inc. + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "block/qdict.h" +#include "qapi/qmp/qlist.h" +#include "qapi/qmp/qnum.h" +#include "qapi/error.h" + +static void qdict_defaults_test(void) +{ + QDict *dict, *copy; + + dict = qdict_new(); + copy = qdict_new(); + + qdict_set_default_str(dict, "foo", "abc"); + qdict_set_default_str(dict, "foo", "def"); + g_assert_cmpstr(qdict_get_str(dict, "foo"), ==, "abc"); + qdict_set_default_str(dict, "bar", "ghi"); + + qdict_copy_default(copy, dict, "foo"); + g_assert_cmpstr(qdict_get_str(copy, "foo"), ==, "abc"); + qdict_set_default_str(copy, "bar", "xyz"); + qdict_copy_default(copy, dict, "bar"); + g_assert_cmpstr(qdict_get_str(copy, "bar"), ==, "xyz"); + + qobject_unref(copy); + qobject_unref(dict); +} + +static void qdict_flatten_test(void) +{ + QList *e_1 = qlist_new(); + QList *e = qlist_new(); + QDict *e_1_2 = qdict_new(); + QDict *f = qdict_new(); + QList *y = qlist_new(); + QDict *z = qdict_new(); + QDict *root = qdict_new(); + + /* + * Test the flattening of + * + * { + * "e": [ + * 42, + * [ + * 23, + * 66, + * { + * "a": 0, + * "b": 1 + * } + * ] + * ], + * "f": { + * "c": 2, + * "d": 3, + * }, + * "g": 4, + * "y": [{}], + * "z": {"a": []} + * } + * + * to + * + * { + * "e.0": 42, + * "e.1.0": 23, + * "e.1.1": 66, + * "e.1.2.a": 0, + * "e.1.2.b": 1, + * "f.c": 2, + * "f.d": 3, + * "g": 4, + * "y.0": {}, + * "z.a": [] + * } + */ + + qdict_put_int(e_1_2, "a", 0); + qdict_put_int(e_1_2, "b", 1); + + qlist_append_int(e_1, 23); + qlist_append_int(e_1, 66); + qlist_append(e_1, e_1_2); + qlist_append_int(e, 42); + qlist_append(e, e_1); + + qdict_put_int(f, "c", 2); + qdict_put_int(f, "d", 3); + + qlist_append(y, qdict_new()); + + qdict_put(z, "a", qlist_new()); + + qdict_put(root, "e", e); + qdict_put(root, "f", f); + qdict_put_int(root, "g", 4); + qdict_put(root, "y", y); + qdict_put(root, "z", z); + + qdict_flatten(root); + + g_assert(qdict_get_int(root, "e.0") == 42); + g_assert(qdict_get_int(root, "e.1.0") == 23); + g_assert(qdict_get_int(root, "e.1.1") == 66); + g_assert(qdict_get_int(root, "e.1.2.a") == 0); + g_assert(qdict_get_int(root, "e.1.2.b") == 1); + g_assert(qdict_get_int(root, "f.c") == 2); + g_assert(qdict_get_int(root, "f.d") == 3); + g_assert(qdict_get_int(root, "g") == 4); + g_assert(!qdict_size(qdict_get_qdict(root, "y.0"))); + g_assert(qlist_empty(qdict_get_qlist(root, "z.a"))); + + g_assert(qdict_size(root) == 10); + + qobject_unref(root); +} + +static void qdict_clone_flatten_test(void) +{ + QDict *dict1 = qdict_new(); + QDict *dict2 = qdict_new(); + QDict *cloned_dict1; + + /* + * Test that we can clone and flatten + * { "a": { "b": 42 } } + * without modifying the clone. + */ + + qdict_put_int(dict2, "b", 42); + qdict_put(dict1, "a", dict2); + + cloned_dict1 = qdict_clone_shallow(dict1); + + qdict_flatten(dict1); + + g_assert(qdict_size(dict1) == 1); + g_assert(qdict_get_int(dict1, "a.b") == 42); + + g_assert(qdict_size(cloned_dict1) == 1); + g_assert(qdict_get_qdict(cloned_dict1, "a") == dict2); + + g_assert(qdict_size(dict2) == 1); + g_assert(qdict_get_int(dict2, "b") == 42); + + qobject_unref(dict1); + qobject_unref(cloned_dict1); +} + +static void qdict_array_split_test(void) +{ + QDict *test_dict = qdict_new(); + QDict *dict1, *dict2; + QNum *int1; + QList *test_list; + + /* + * Test the split of + * + * { + * "1.x": 0, + * "4.y": 1, + * "0.a": 42, + * "o.o": 7, + * "0.b": 23, + * "2": 66 + * } + * + * to + * + * [ + * { + * "a": 42, + * "b": 23 + * }, + * { + * "x": 0 + * }, + * 66 + * ] + * + * and + * + * { + * "4.y": 1, + * "o.o": 7 + * } + * + * (remaining in the old QDict) + * + * This example is given in the comment of qdict_array_split(). + */ + + qdict_put_int(test_dict, "1.x", 0); + qdict_put_int(test_dict, "4.y", 1); + qdict_put_int(test_dict, "0.a", 42); + qdict_put_int(test_dict, "o.o", 7); + qdict_put_int(test_dict, "0.b", 23); + qdict_put_int(test_dict, "2", 66); + + qdict_array_split(test_dict, &test_list); + + dict1 = qobject_to(QDict, qlist_pop(test_list)); + dict2 = qobject_to(QDict, qlist_pop(test_list)); + int1 = qobject_to(QNum, qlist_pop(test_list)); + + g_assert(dict1); + g_assert(dict2); + g_assert(int1); + g_assert(qlist_empty(test_list)); + + qobject_unref(test_list); + + g_assert(qdict_get_int(dict1, "a") == 42); + g_assert(qdict_get_int(dict1, "b") == 23); + + g_assert(qdict_size(dict1) == 2); + + qobject_unref(dict1); + + g_assert(qdict_get_int(dict2, "x") == 0); + + g_assert(qdict_size(dict2) == 1); + + qobject_unref(dict2); + + g_assert_cmpint(qnum_get_int(int1), ==, 66); + + qobject_unref(int1); + + g_assert(qdict_get_int(test_dict, "4.y") == 1); + g_assert(qdict_get_int(test_dict, "o.o") == 7); + + g_assert(qdict_size(test_dict) == 2); + + qobject_unref(test_dict); + + /* + * Test the split of + * + * { + * "0": 42, + * "1": 23, + * "1.x": 84 + * } + * + * to + * + * [ + * 42 + * ] + * + * and + * + * { + * "1": 23, + * "1.x": 84 + * } + * + * That is, test whether splitting stops if there is both an entry with key + * of "%u" and other entries with keys prefixed "%u." for the same index. + */ + + test_dict = qdict_new(); + + qdict_put_int(test_dict, "0", 42); + qdict_put_int(test_dict, "1", 23); + qdict_put_int(test_dict, "1.x", 84); + + qdict_array_split(test_dict, &test_list); + + int1 = qobject_to(QNum, qlist_pop(test_list)); + + g_assert(int1); + g_assert(qlist_empty(test_list)); + + qobject_unref(test_list); + + g_assert_cmpint(qnum_get_int(int1), ==, 42); + + qobject_unref(int1); + + g_assert(qdict_get_int(test_dict, "1") == 23); + g_assert(qdict_get_int(test_dict, "1.x") == 84); + + g_assert(qdict_size(test_dict) == 2); + + qobject_unref(test_dict); +} + +static void qdict_array_entries_test(void) +{ + QDict *dict = qdict_new(); + + g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 0); + + qdict_put_int(dict, "bar", 0); + qdict_put_int(dict, "baz.0", 0); + g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 0); + + qdict_put_int(dict, "foo.1", 0); + g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, -EINVAL); + qdict_put_int(dict, "foo.0", 0); + g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 2); + qdict_put_int(dict, "foo.bar", 0); + g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, -EINVAL); + qdict_del(dict, "foo.bar"); + + qdict_put_int(dict, "foo.2.a", 0); + qdict_put_int(dict, "foo.2.b", 0); + qdict_put_int(dict, "foo.2.c", 0); + g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 3); + g_assert_cmpint(qdict_array_entries(dict, ""), ==, -EINVAL); + + qobject_unref(dict); + + dict = qdict_new(); + qdict_put_int(dict, "1", 0); + g_assert_cmpint(qdict_array_entries(dict, ""), ==, -EINVAL); + qdict_put_int(dict, "0", 0); + g_assert_cmpint(qdict_array_entries(dict, ""), ==, 2); + qdict_put_int(dict, "bar", 0); + g_assert_cmpint(qdict_array_entries(dict, ""), ==, -EINVAL); + qdict_del(dict, "bar"); + + qdict_put_int(dict, "2.a", 0); + qdict_put_int(dict, "2.b", 0); + qdict_put_int(dict, "2.c", 0); + g_assert_cmpint(qdict_array_entries(dict, ""), ==, 3); + + qobject_unref(dict); +} + +static void qdict_join_test(void) +{ + QDict *dict1, *dict2; + bool overwrite = false; + int i; + + dict1 = qdict_new(); + dict2 = qdict_new(); + + /* Test everything once without overwrite and once with */ + do { + /* Test empty dicts */ + qdict_join(dict1, dict2, overwrite); + + g_assert(qdict_size(dict1) == 0); + g_assert(qdict_size(dict2) == 0); + + /* First iteration: Test movement */ + /* Second iteration: Test empty source and non-empty destination */ + qdict_put_int(dict2, "foo", 42); + + for (i = 0; i < 2; i++) { + qdict_join(dict1, dict2, overwrite); + + g_assert(qdict_size(dict1) == 1); + g_assert(qdict_size(dict2) == 0); + + g_assert(qdict_get_int(dict1, "foo") == 42); + } + + /* Test non-empty source and destination without conflict */ + qdict_put_int(dict2, "bar", 23); + + qdict_join(dict1, dict2, overwrite); + + g_assert(qdict_size(dict1) == 2); + g_assert(qdict_size(dict2) == 0); + + g_assert(qdict_get_int(dict1, "foo") == 42); + g_assert(qdict_get_int(dict1, "bar") == 23); + + /* Test conflict */ + qdict_put_int(dict2, "foo", 84); + + qdict_join(dict1, dict2, overwrite); + + g_assert(qdict_size(dict1) == 2); + g_assert(qdict_size(dict2) == !overwrite); + + g_assert(qdict_get_int(dict1, "foo") == (overwrite ? 84 : 42)); + g_assert(qdict_get_int(dict1, "bar") == 23); + + if (!overwrite) { + g_assert(qdict_get_int(dict2, "foo") == 84); + } + + /* Check the references */ + g_assert(qdict_get(dict1, "foo")->base.refcnt == 1); + g_assert(qdict_get(dict1, "bar")->base.refcnt == 1); + + if (!overwrite) { + g_assert(qdict_get(dict2, "foo")->base.refcnt == 1); + } + + /* Clean up */ + qdict_del(dict1, "foo"); + qdict_del(dict1, "bar"); + + if (!overwrite) { + qdict_del(dict2, "foo"); + } + } while (overwrite ^= true); + + qobject_unref(dict1); + qobject_unref(dict2); +} + +static void qdict_crumple_test_recursive(void) +{ + QDict *src, *dst, *rule, *vnc, *acl, *listen; + QDict *empty, *empty_dict, *empty_list_0; + QList *rules, *empty_list, *empty_dict_a; + + src = qdict_new(); + qdict_put_str(src, "vnc.listen.addr", "127.0.0.1"); + qdict_put_str(src, "vnc.listen.port", "5901"); + qdict_put_str(src, "vnc.acl.rules.0.match", "fred"); + qdict_put_str(src, "vnc.acl.rules.0.policy", "allow"); + qdict_put_str(src, "vnc.acl.rules.1.match", "bob"); + qdict_put_str(src, "vnc.acl.rules.1.policy", "deny"); + qdict_put_str(src, "vnc.acl.default", "deny"); + qdict_put_str(src, "vnc.acl..name", "acl0"); + qdict_put_str(src, "vnc.acl.rule..name", "acl0"); + qdict_put(src, "empty.dict.a", qlist_new()); + qdict_put(src, "empty.list.0", qdict_new()); + + dst = qobject_to(QDict, qdict_crumple(src, &error_abort)); + g_assert(dst); + g_assert_cmpint(qdict_size(dst), ==, 2); + + vnc = qdict_get_qdict(dst, "vnc"); + g_assert(vnc); + g_assert_cmpint(qdict_size(vnc), ==, 3); + + listen = qdict_get_qdict(vnc, "listen"); + g_assert(listen); + g_assert_cmpint(qdict_size(listen), ==, 2); + g_assert_cmpstr("127.0.0.1", ==, qdict_get_str(listen, "addr")); + g_assert_cmpstr("5901", ==, qdict_get_str(listen, "port")); + + acl = qdict_get_qdict(vnc, "acl"); + g_assert(acl); + g_assert_cmpint(qdict_size(acl), ==, 3); + + rules = qdict_get_qlist(acl, "rules"); + g_assert(rules); + g_assert_cmpint(qlist_size(rules), ==, 2); + + rule = qobject_to(QDict, qlist_pop(rules)); + g_assert(rule); + g_assert_cmpint(qdict_size(rule), ==, 2); + g_assert_cmpstr("fred", ==, qdict_get_str(rule, "match")); + g_assert_cmpstr("allow", ==, qdict_get_str(rule, "policy")); + qobject_unref(rule); + + rule = qobject_to(QDict, qlist_pop(rules)); + g_assert(rule); + g_assert_cmpint(qdict_size(rule), ==, 2); + g_assert_cmpstr("bob", ==, qdict_get_str(rule, "match")); + g_assert_cmpstr("deny", ==, qdict_get_str(rule, "policy")); + qobject_unref(rule); + + /* With recursive crumpling, we should see all names unescaped */ + g_assert_cmpstr("acl0", ==, qdict_get_str(vnc, "acl.name")); + g_assert_cmpstr("acl0", ==, qdict_get_str(acl, "rule.name")); + + empty = qdict_get_qdict(dst, "empty"); + g_assert(empty); + g_assert_cmpint(qdict_size(empty), ==, 2); + empty_dict = qdict_get_qdict(empty, "dict"); + g_assert(empty_dict); + g_assert_cmpint(qdict_size(empty_dict), ==, 1); + empty_dict_a = qdict_get_qlist(empty_dict, "a"); + g_assert(empty_dict_a && qlist_empty(empty_dict_a)); + empty_list = qdict_get_qlist(empty, "list"); + g_assert(empty_list); + g_assert_cmpint(qlist_size(empty_list), ==, 1); + empty_list_0 = qobject_to(QDict, qlist_pop(empty_list)); + g_assert(empty_list_0); + g_assert_cmpint(qdict_size(empty_list_0), ==, 0); + qobject_unref(empty_list_0); + + qobject_unref(src); + qobject_unref(dst); +} + +static void qdict_crumple_test_empty(void) +{ + QDict *src, *dst; + + src = qdict_new(); + + dst = qobject_to(QDict, qdict_crumple(src, &error_abort)); + + g_assert_cmpint(qdict_size(dst), ==, 0); + + qobject_unref(src); + qobject_unref(dst); +} + +static int qdict_count_entries(QDict *dict) +{ + const QDictEntry *e; + int count = 0; + + for (e = qdict_first(dict); e; e = qdict_next(dict, e)) { + count++; + } + + return count; +} + +static void qdict_rename_keys_test(void) +{ + QDict *dict = qdict_new(); + QDict *copy; + QDictRenames *renames; + Error *local_err = NULL; + + qdict_put_str(dict, "abc", "foo"); + qdict_put_str(dict, "abcdef", "bar"); + qdict_put_int(dict, "number", 42); + qdict_put_bool(dict, "flag", true); + qdict_put_null(dict, "nothing"); + + /* Empty rename list */ + renames = (QDictRenames[]) { + { NULL, "this can be anything" } + }; + copy = qdict_clone_shallow(dict); + qdict_rename_keys(copy, renames, &error_abort); + + g_assert_cmpstr(qdict_get_str(copy, "abc"), ==, "foo"); + g_assert_cmpstr(qdict_get_str(copy, "abcdef"), ==, "bar"); + g_assert_cmpint(qdict_get_int(copy, "number"), ==, 42); + g_assert_cmpint(qdict_get_bool(copy, "flag"), ==, true); + g_assert(qobject_type(qdict_get(copy, "nothing")) == QTYPE_QNULL); + g_assert_cmpint(qdict_count_entries(copy), ==, 5); + + qobject_unref(copy); + + /* Simple rename of all entries */ + renames = (QDictRenames[]) { + { "abc", "str1" }, + { "abcdef", "str2" }, + { "number", "int" }, + { "flag", "bool" }, + { "nothing", "null" }, + { NULL , NULL } + }; + copy = qdict_clone_shallow(dict); + qdict_rename_keys(copy, renames, &error_abort); + + g_assert(!qdict_haskey(copy, "abc")); + g_assert(!qdict_haskey(copy, "abcdef")); + g_assert(!qdict_haskey(copy, "number")); + g_assert(!qdict_haskey(copy, "flag")); + g_assert(!qdict_haskey(copy, "nothing")); + + g_assert_cmpstr(qdict_get_str(copy, "str1"), ==, "foo"); + g_assert_cmpstr(qdict_get_str(copy, "str2"), ==, "bar"); + g_assert_cmpint(qdict_get_int(copy, "int"), ==, 42); + g_assert_cmpint(qdict_get_bool(copy, "bool"), ==, true); + g_assert(qobject_type(qdict_get(copy, "null")) == QTYPE_QNULL); + g_assert_cmpint(qdict_count_entries(copy), ==, 5); + + qobject_unref(copy); + + /* Renames are processed top to bottom */ + renames = (QDictRenames[]) { + { "abc", "tmp" }, + { "abcdef", "abc" }, + { "number", "abcdef" }, + { "flag", "number" }, + { "nothing", "flag" }, + { "tmp", "nothing" }, + { NULL , NULL } + }; + copy = qdict_clone_shallow(dict); + qdict_rename_keys(copy, renames, &error_abort); + + g_assert_cmpstr(qdict_get_str(copy, "nothing"), ==, "foo"); + g_assert_cmpstr(qdict_get_str(copy, "abc"), ==, "bar"); + g_assert_cmpint(qdict_get_int(copy, "abcdef"), ==, 42); + g_assert_cmpint(qdict_get_bool(copy, "number"), ==, true); + g_assert(qobject_type(qdict_get(copy, "flag")) == QTYPE_QNULL); + g_assert(!qdict_haskey(copy, "tmp")); + g_assert_cmpint(qdict_count_entries(copy), ==, 5); + + qobject_unref(copy); + + /* Conflicting rename */ + renames = (QDictRenames[]) { + { "abcdef", "abc" }, + { NULL , NULL } + }; + copy = qdict_clone_shallow(dict); + qdict_rename_keys(copy, renames, &local_err); + + error_free_or_abort(&local_err); + + g_assert_cmpstr(qdict_get_str(copy, "abc"), ==, "foo"); + g_assert_cmpstr(qdict_get_str(copy, "abcdef"), ==, "bar"); + g_assert_cmpint(qdict_get_int(copy, "number"), ==, 42); + g_assert_cmpint(qdict_get_bool(copy, "flag"), ==, true); + g_assert(qobject_type(qdict_get(copy, "nothing")) == QTYPE_QNULL); + g_assert_cmpint(qdict_count_entries(copy), ==, 5); + + qobject_unref(copy); + + /* Renames in an empty dict */ + renames = (QDictRenames[]) { + { "abcdef", "abc" }, + { NULL , NULL } + }; + + qobject_unref(dict); + dict = qdict_new(); + + qdict_rename_keys(dict, renames, &error_abort); + g_assert(qdict_first(dict) == NULL); + + qobject_unref(dict); +} + +static void qdict_crumple_test_bad_inputs(void) +{ + QDict *src, *nested; + Error *error = NULL; + + src = qdict_new(); + /* rule.0 can't be both a string and a dict */ + qdict_put_str(src, "rule.0", "fred"); + qdict_put_str(src, "rule.0.policy", "allow"); + + g_assert(qdict_crumple(src, &error) == NULL); + error_free_or_abort(&error); + qobject_unref(src); + + src = qdict_new(); + /* rule can't be both a list and a dict */ + qdict_put_str(src, "rule.0", "fred"); + qdict_put_str(src, "rule.a", "allow"); + + g_assert(qdict_crumple(src, &error) == NULL); + error_free_or_abort(&error); + qobject_unref(src); + + src = qdict_new(); + /* The input should be flat, ie no dicts or lists */ + nested = qdict_new(); + qdict_put(nested, "x", qdict_new()); + qdict_put(src, "rule.a", nested); + qdict_put_str(src, "rule.b", "allow"); + + g_assert(qdict_crumple(src, &error) == NULL); + error_free_or_abort(&error); + qobject_unref(src); + + src = qdict_new(); + /* List indexes must not have gaps */ + qdict_put_str(src, "rule.0", "deny"); + qdict_put_str(src, "rule.3", "allow"); + + g_assert(qdict_crumple(src, &error) == NULL); + error_free_or_abort(&error); + qobject_unref(src); + + src = qdict_new(); + /* List indexes must be in %zu format */ + qdict_put_str(src, "rule.0", "deny"); + qdict_put_str(src, "rule.+1", "allow"); + + g_assert(qdict_crumple(src, &error) == NULL); + error_free_or_abort(&error); + qobject_unref(src); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/public/defaults", qdict_defaults_test); + g_test_add_func("/public/flatten", qdict_flatten_test); + g_test_add_func("/public/clone_flatten", qdict_clone_flatten_test); + g_test_add_func("/public/array_split", qdict_array_split_test); + g_test_add_func("/public/array_entries", qdict_array_entries_test); + g_test_add_func("/public/join", qdict_join_test); + g_test_add_func("/public/crumple/recursive", + qdict_crumple_test_recursive); + g_test_add_func("/public/crumple/empty", + qdict_crumple_test_empty); + g_test_add_func("/public/crumple/bad_inputs", + qdict_crumple_test_bad_inputs); + + g_test_add_func("/public/rename_keys", qdict_rename_keys_test); + + return g_test_run(); +} diff --git a/tests/unit/check-qdict.c b/tests/unit/check-qdict.c new file mode 100644 index 0000000000..b5efa859b0 --- /dev/null +++ b/tests/unit/check-qdict.c @@ -0,0 +1,379 @@ +/* + * QDict unit-tests. + * + * Copyright (C) 2009 Red Hat Inc. + * + * Authors: + * Luiz Capitulino + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qmp/qnum.h" +#include "qapi/qmp/qstring.h" + +/* + * Public Interface test-cases + * + * (with some violations to access 'private' data) + */ + +static void qdict_new_test(void) +{ + QDict *qdict; + + qdict = qdict_new(); + g_assert(qdict != NULL); + g_assert(qdict_size(qdict) == 0); + g_assert(qdict->base.refcnt == 1); + g_assert(qobject_type(QOBJECT(qdict)) == QTYPE_QDICT); + + qobject_unref(qdict); +} + +static void qdict_put_obj_test(void) +{ + QNum *qn; + QDict *qdict; + QDictEntry *ent; + const int num = 42; + + qdict = qdict_new(); + + // key "" will have tdb hash 12345 + qdict_put_int(qdict, "", num); + + g_assert(qdict_size(qdict) == 1); + ent = QLIST_FIRST(&qdict->table[12345 % QDICT_BUCKET_MAX]); + qn = qobject_to(QNum, ent->value); + g_assert_cmpint(qnum_get_int(qn), ==, num); + + qobject_unref(qdict); +} + +static void qdict_destroy_simple_test(void) +{ + QDict *qdict; + + qdict = qdict_new(); + qdict_put_int(qdict, "num", 0); + qdict_put_str(qdict, "str", "foo"); + + qobject_unref(qdict); +} + +static void qdict_get_test(void) +{ + QNum *qn; + QObject *obj; + const int value = -42; + const char *key = "test"; + QDict *tests_dict = qdict_new(); + + qdict_put_int(tests_dict, key, value); + + obj = qdict_get(tests_dict, key); + g_assert(obj != NULL); + + qn = qobject_to(QNum, obj); + g_assert_cmpint(qnum_get_int(qn), ==, value); + + qobject_unref(tests_dict); +} + +static void qdict_get_int_test(void) +{ + int ret; + const int value = 100; + const char *key = "int"; + QDict *tests_dict = qdict_new(); + + qdict_put_int(tests_dict, key, value); + + ret = qdict_get_int(tests_dict, key); + g_assert(ret == value); + + qobject_unref(tests_dict); +} + +static void qdict_get_try_int_test(void) +{ + int ret; + const int value = 100; + const char *key = "int"; + QDict *tests_dict = qdict_new(); + + qdict_put_int(tests_dict, key, value); + qdict_put_str(tests_dict, "string", "test"); + + ret = qdict_get_try_int(tests_dict, key, 0); + g_assert(ret == value); + + ret = qdict_get_try_int(tests_dict, "missing", -42); + g_assert_cmpuint(ret, ==, -42); + + ret = qdict_get_try_int(tests_dict, "string", -42); + g_assert_cmpuint(ret, ==, -42); + + qobject_unref(tests_dict); +} + +static void qdict_get_str_test(void) +{ + const char *p; + const char *key = "key"; + const char *str = "string"; + QDict *tests_dict = qdict_new(); + + qdict_put_str(tests_dict, key, str); + + p = qdict_get_str(tests_dict, key); + g_assert(p != NULL); + g_assert(strcmp(p, str) == 0); + + qobject_unref(tests_dict); +} + +static void qdict_get_try_str_test(void) +{ + const char *p; + const char *key = "key"; + const char *str = "string"; + QDict *tests_dict = qdict_new(); + + qdict_put_str(tests_dict, key, str); + + p = qdict_get_try_str(tests_dict, key); + g_assert(p != NULL); + g_assert(strcmp(p, str) == 0); + + qobject_unref(tests_dict); +} + +static void qdict_haskey_not_test(void) +{ + QDict *tests_dict = qdict_new(); + g_assert(qdict_haskey(tests_dict, "test") == 0); + + qobject_unref(tests_dict); +} + +static void qdict_haskey_test(void) +{ + const char *key = "test"; + QDict *tests_dict = qdict_new(); + + qdict_put_int(tests_dict, key, 0); + g_assert(qdict_haskey(tests_dict, key) == 1); + + qobject_unref(tests_dict); +} + +static void qdict_del_test(void) +{ + const char *key = "key test"; + QDict *tests_dict = qdict_new(); + + qdict_put_str(tests_dict, key, "foo"); + g_assert(qdict_size(tests_dict) == 1); + + qdict_del(tests_dict, key); + + g_assert(qdict_size(tests_dict) == 0); + g_assert(qdict_haskey(tests_dict, key) == 0); + + qobject_unref(tests_dict); +} + +static void qobject_to_qdict_test(void) +{ + QDict *tests_dict = qdict_new(); + g_assert(qobject_to(QDict, QOBJECT(tests_dict)) == tests_dict); + + qobject_unref(tests_dict); +} + +static void qdict_iterapi_test(void) +{ + int count; + const QDictEntry *ent; + QDict *tests_dict = qdict_new(); + + g_assert(qdict_first(tests_dict) == NULL); + + qdict_put_int(tests_dict, "key1", 1); + qdict_put_int(tests_dict, "key2", 2); + qdict_put_int(tests_dict, "key3", 3); + + count = 0; + for (ent = qdict_first(tests_dict); ent; ent = qdict_next(tests_dict, ent)){ + g_assert(qdict_haskey(tests_dict, qdict_entry_key(ent)) == 1); + count++; + } + + g_assert(count == qdict_size(tests_dict)); + + /* Do it again to test restarting */ + count = 0; + for (ent = qdict_first(tests_dict); ent; ent = qdict_next(tests_dict, ent)){ + g_assert(qdict_haskey(tests_dict, qdict_entry_key(ent)) == 1); + count++; + } + + g_assert(count == qdict_size(tests_dict)); + + qobject_unref(tests_dict); +} + +/* + * Errors test-cases + */ + +static void qdict_put_exists_test(void) +{ + int value; + const char *key = "exists"; + QDict *tests_dict = qdict_new(); + + qdict_put_int(tests_dict, key, 1); + qdict_put_int(tests_dict, key, 2); + + value = qdict_get_int(tests_dict, key); + g_assert(value == 2); + + g_assert(qdict_size(tests_dict) == 1); + + qobject_unref(tests_dict); +} + +static void qdict_get_not_exists_test(void) +{ + QDict *tests_dict = qdict_new(); + g_assert(qdict_get(tests_dict, "foo") == NULL); + + qobject_unref(tests_dict); +} + +/* + * Stress test-case + * + * This is a lot big for a unit-test, but there is no other place + * to have it. + */ + +static void remove_dots(char *string) +{ + char *p = strchr(string, ':'); + if (p) + *p = '\0'; +} + +static QString *read_line(FILE *file, char *key) +{ + char value[128]; + + if (fscanf(file, "%127s%127s", key, value) == EOF) { + return NULL; + } + remove_dots(key); + return qstring_from_str(value); +} + +#define reset_file(file) fseek(file, 0L, SEEK_SET) + +static void qdict_stress_test(void) +{ + size_t lines; + char key[128]; + FILE *test_file; + QDict *qdict; + QString *value; + const char *test_file_path = "tests/data/qobject/qdict.txt"; + + test_file = fopen(test_file_path, "r"); + g_assert(test_file != NULL); + + // Create the dict + qdict = qdict_new(); + g_assert(qdict != NULL); + + // Add everything from the test file + for (lines = 0;; lines++) { + value = read_line(test_file, key); + if (!value) + break; + + qdict_put(qdict, key, value); + } + g_assert(qdict_size(qdict) == lines); + + // Check if everything is really in there + reset_file(test_file); + for (;;) { + const char *str1, *str2; + + value = read_line(test_file, key); + if (!value) + break; + + str1 = qstring_get_str(value); + + str2 = qdict_get_str(qdict, key); + g_assert(str2 != NULL); + + g_assert(strcmp(str1, str2) == 0); + + qobject_unref(value); + } + + // Delete everything + reset_file(test_file); + for (;;) { + value = read_line(test_file, key); + if (!value) + break; + + qdict_del(qdict, key); + qobject_unref(value); + + g_assert(qdict_haskey(qdict, key) == 0); + } + fclose(test_file); + + g_assert(qdict_size(qdict) == 0); + qobject_unref(qdict); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/public/new", qdict_new_test); + g_test_add_func("/public/put_obj", qdict_put_obj_test); + g_test_add_func("/public/destroy_simple", qdict_destroy_simple_test); + + /* Continue, but now with fixtures */ + g_test_add_func("/public/get", qdict_get_test); + g_test_add_func("/public/get_int", qdict_get_int_test); + g_test_add_func("/public/get_try_int", qdict_get_try_int_test); + g_test_add_func("/public/get_str", qdict_get_str_test); + g_test_add_func("/public/get_try_str", qdict_get_try_str_test); + g_test_add_func("/public/haskey_not", qdict_haskey_not_test); + g_test_add_func("/public/haskey", qdict_haskey_test); + g_test_add_func("/public/del", qdict_del_test); + g_test_add_func("/public/to_qdict", qobject_to_qdict_test); + g_test_add_func("/public/iterapi", qdict_iterapi_test); + + g_test_add_func("/errors/put_exists", qdict_put_exists_test); + g_test_add_func("/errors/get_not_exists", qdict_get_not_exists_test); + + /* The Big one */ + if (g_test_slow()) { + g_test_add_func("/stress/test", qdict_stress_test); + } + + return g_test_run(); +} diff --git a/tests/unit/check-qjson.c b/tests/unit/check-qjson.c new file mode 100644 index 0000000000..c845f91d43 --- /dev/null +++ b/tests/unit/check-qjson.c @@ -0,0 +1,1518 @@ +/* + * Copyright IBM, Corp. 2009 + * Copyright (c) 2013, 2015 Red Hat Inc. + * + * Authors: + * Anthony Liguori + * Markus Armbruster + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include "qemu/osdep.h" + +#include "qapi/error.h" +#include "qapi/qmp/qbool.h" +#include "qapi/qmp/qjson.h" +#include "qapi/qmp/qlit.h" +#include "qapi/qmp/qnull.h" +#include "qapi/qmp/qnum.h" +#include "qapi/qmp/qstring.h" +#include "qemu/unicode.h" +#include "qemu-common.h" + +static QString *from_json_str(const char *jstr, bool single, Error **errp) +{ + char quote = single ? '\'' : '"'; + char *qjstr = g_strdup_printf("%c%s%c", quote, jstr, quote); + QString *ret = qobject_to(QString, qobject_from_json(qjstr, errp)); + + g_free(qjstr); + return ret; +} + +static char *to_json_str(QString *str) +{ + GString *json = qobject_to_json(QOBJECT(str)); + + if (!json) { + return NULL; + } + /* peel off double quotes */ + g_string_truncate(json, json->len - 1); + g_string_erase(json, 0, 1); + return g_string_free(json, false); +} + +static void escaped_string(void) +{ + struct { + /* Content of JSON string to parse with qobject_from_json() */ + const char *json_in; + /* Expected parse output; to unparse with qobject_to_json() */ + const char *utf8_out; + int skip; + } test_cases[] = { + { "\\b\\f\\n\\r\\t\\\\\\\"", "\b\f\n\r\t\\\"" }, + { "\\/\\'", "/'", .skip = 1 }, + { "single byte utf-8 \\u0020", "single byte utf-8 ", .skip = 1 }, + { "double byte utf-8 \\u00A2", "double byte utf-8 \xc2\xa2" }, + { "triple byte utf-8 \\u20AC", "triple byte utf-8 \xe2\x82\xac" }, + { "quadruple byte utf-8 \\uD834\\uDD1E", /* U+1D11E */ + "quadruple byte utf-8 \xF0\x9D\x84\x9E" }, + { "\\", NULL }, + { "\\z", NULL }, + { "\\ux", NULL }, + { "\\u1x", NULL }, + { "\\u12x", NULL }, + { "\\u123x", NULL }, + { "\\u12345", "\341\210\2645" }, + { "\\u0000x", "\xC0\x80x" }, + { "unpaired leading surrogate \\uD800", NULL }, + { "unpaired leading surrogate \\uD800\\uCAFE", NULL }, + { "unpaired leading surrogate \\uD800\\uD801\\uDC02", NULL }, + { "unpaired trailing surrogate \\uDC00", NULL }, + { "backward surrogate pair \\uDC00\\uD800", NULL }, + { "noncharacter U+FDD0 \\uFDD0", NULL }, + { "noncharacter U+FDEF \\uFDEF", NULL }, + { "noncharacter U+1FFFE \\uD87F\\uDFFE", NULL }, + { "noncharacter U+10FFFF \\uDC3F\\uDFFF", NULL }, + {} + }; + int i, j; + QString *cstr; + char *jstr; + + for (i = 0; test_cases[i].json_in; i++) { + for (j = 0; j < 2; j++) { + if (test_cases[i].utf8_out) { + cstr = from_json_str(test_cases[i].json_in, j, &error_abort); + g_assert_cmpstr(qstring_get_str(cstr), + ==, test_cases[i].utf8_out); + if (!test_cases[i].skip) { + jstr = to_json_str(cstr); + g_assert_cmpstr(jstr, ==, test_cases[i].json_in); + g_free(jstr); + } + qobject_unref(cstr); + } else { + cstr = from_json_str(test_cases[i].json_in, j, NULL); + g_assert(!cstr); + } + } + } +} + +static void string_with_quotes(void) +{ + const char *test_cases[] = { + "\"the bee's knees\"", + "'double quote \"'", + NULL + }; + int i; + QString *str; + char *cstr; + + for (i = 0; test_cases[i]; i++) { + str = qobject_to(QString, + qobject_from_json(test_cases[i], &error_abort)); + g_assert(str); + cstr = g_strndup(test_cases[i] + 1, strlen(test_cases[i]) - 2); + g_assert_cmpstr(qstring_get_str(str), ==, cstr); + g_free(cstr); + qobject_unref(str); + } +} + +static void utf8_string(void) +{ + /* + * Most test cases are scraped from Markus Kuhn's UTF-8 decoder + * capability and stress test at + * http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt + */ + static const struct { + /* Content of JSON string to parse with qobject_from_json() */ + const char *json_in; + /* Expected parse output */ + const char *utf8_out; + /* Expected unparse output, defaults to @json_in */ + const char *json_out; + } test_cases[] = { + /* 0 Control characters */ + { + /* + * Note: \x00 is impossible, other representations of + * U+0000 are covered under 4.3 + */ + "\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" + "\x10\x11\x12\x13\x14\x15\x16\x17" + "\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F", + NULL, + "\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007" + "\\b\\t\\n\\u000B\\f\\r\\u000E\\u000F" + "\\u0010\\u0011\\u0012\\u0013\\u0014\\u0015\\u0016\\u0017" + "\\u0018\\u0019\\u001A\\u001B\\u001C\\u001D\\u001E\\u001F", + }, + /* 1 Some correct UTF-8 text */ + { + /* a bit of German */ + "Falsches \xC3\x9C" "ben von Xylophonmusik qu\xC3\xA4lt" + " jeden gr\xC3\xB6\xC3\x9F" "eren Zwerg.", + "Falsches \xC3\x9C" "ben von Xylophonmusik qu\xC3\xA4lt" + " jeden gr\xC3\xB6\xC3\x9F" "eren Zwerg.", + "Falsches \\u00DCben von Xylophonmusik qu\\u00E4lt" + " jeden gr\\u00F6\\u00DFeren Zwerg.", + }, + { + /* a bit of Greek */ + "\xCE\xBA\xE1\xBD\xB9\xCF\x83\xCE\xBC\xCE\xB5", + "\xCE\xBA\xE1\xBD\xB9\xCF\x83\xCE\xBC\xCE\xB5", + "\\u03BA\\u1F79\\u03C3\\u03BC\\u03B5", + }, + /* '%' character when not interpolating */ + { + "100%", + "100%", + }, + /* 2 Boundary condition test cases */ + /* 2.1 First possible sequence of a certain length */ + /* + * 2.1.1 1 byte U+0020 + * Control characters are already covered by their own test + * case under 0. Test the first 1 byte non-control character + * here. + */ + { + " ", + " ", + }, + /* 2.1.2 2 bytes U+0080 */ + { + "\xC2\x80", + "\xC2\x80", + "\\u0080", + }, + /* 2.1.3 3 bytes U+0800 */ + { + "\xE0\xA0\x80", + "\xE0\xA0\x80", + "\\u0800", + }, + /* 2.1.4 4 bytes U+10000 */ + { + "\xF0\x90\x80\x80", + "\xF0\x90\x80\x80", + "\\uD800\\uDC00", + }, + /* 2.1.5 5 bytes U+200000 */ + { + "\xF8\x88\x80\x80\x80", + NULL, + "\\uFFFD", + }, + /* 2.1.6 6 bytes U+4000000 */ + { + "\xFC\x84\x80\x80\x80\x80", + NULL, + "\\uFFFD", + }, + /* 2.2 Last possible sequence of a certain length */ + /* 2.2.1 1 byte U+007F */ + { + "\x7F", + "\x7F", + "\\u007F", + }, + /* 2.2.2 2 bytes U+07FF */ + { + "\xDF\xBF", + "\xDF\xBF", + "\\u07FF", + }, + /* + * 2.2.3 3 bytes U+FFFC + * The last possible sequence is actually U+FFFF. But that's + * a noncharacter, and already covered by its own test case + * under 5.3. Same for U+FFFE. U+FFFD is the last character + * in the BMP, and covered under 2.3. Because of U+FFFD's + * special role as replacement character, it's worth testing + * U+FFFC here. + */ + { + "\xEF\xBF\xBC", + "\xEF\xBF\xBC", + "\\uFFFC", + }, + /* 2.2.4 4 bytes U+1FFFFF */ + { + "\xF7\xBF\xBF\xBF", + NULL, + "\\uFFFD", + }, + /* 2.2.5 5 bytes U+3FFFFFF */ + { + "\xFB\xBF\xBF\xBF\xBF", + NULL, + "\\uFFFD", + }, + /* 2.2.6 6 bytes U+7FFFFFFF */ + { + "\xFD\xBF\xBF\xBF\xBF\xBF", + NULL, + "\\uFFFD", + }, + /* 2.3 Other boundary conditions */ + { + /* last one before surrogate range: U+D7FF */ + "\xED\x9F\xBF", + "\xED\x9F\xBF", + "\\uD7FF", + }, + { + /* first one after surrogate range: U+E000 */ + "\xEE\x80\x80", + "\xEE\x80\x80", + "\\uE000", + }, + { + /* last one in BMP: U+FFFD */ + "\xEF\xBF\xBD", + "\xEF\xBF\xBD", + "\\uFFFD", + }, + { + /* last one in last plane: U+10FFFD */ + "\xF4\x8F\xBF\xBD", + "\xF4\x8F\xBF\xBD", + "\\uDBFF\\uDFFD" + }, + { + /* first one beyond Unicode range: U+110000 */ + "\xF4\x90\x80\x80", + NULL, + "\\uFFFD", + }, + /* 3 Malformed sequences */ + /* 3.1 Unexpected continuation bytes */ + /* 3.1.1 First continuation byte */ + { + "\x80", + NULL, + "\\uFFFD", + }, + /* 3.1.2 Last continuation byte */ + { + "\xBF", + NULL, + "\\uFFFD", + }, + /* 3.1.3 2 continuation bytes */ + { + "\x80\xBF", + NULL, + "\\uFFFD\\uFFFD", + }, + /* 3.1.4 3 continuation bytes */ + { + "\x80\xBF\x80", + NULL, + "\\uFFFD\\uFFFD\\uFFFD", + }, + /* 3.1.5 4 continuation bytes */ + { + "\x80\xBF\x80\xBF", + NULL, + "\\uFFFD\\uFFFD\\uFFFD\\uFFFD", + }, + /* 3.1.6 5 continuation bytes */ + { + "\x80\xBF\x80\xBF\x80", + NULL, + "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD", + }, + /* 3.1.7 6 continuation bytes */ + { + "\x80\xBF\x80\xBF\x80\xBF", + NULL, + "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD", + }, + /* 3.1.8 7 continuation bytes */ + { + "\x80\xBF\x80\xBF\x80\xBF\x80", + NULL, + "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD", + }, + /* 3.1.9 Sequence of all 64 possible continuation bytes */ + { + "\x80\x81\x82\x83\x84\x85\x86\x87" + "\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F" + "\x90\x91\x92\x93\x94\x95\x96\x97" + "\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F" + "\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7" + "\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF" + "\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7" + "\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF", + NULL, + "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD" + "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD" + "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD" + "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD" + "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD" + "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD" + "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD" + "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD", + }, + /* 3.2 Lonely start characters */ + /* 3.2.1 All 32 first bytes of 2-byte sequences, followed by space */ + { + "\xC0 \xC1 \xC2 \xC3 \xC4 \xC5 \xC6 \xC7 " + "\xC8 \xC9 \xCA \xCB \xCC \xCD \xCE \xCF " + "\xD0 \xD1 \xD2 \xD3 \xD4 \xD5 \xD6 \xD7 " + "\xD8 \xD9 \xDA \xDB \xDC \xDD \xDE \xDF ", + NULL, + "\\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD " + "\\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD " + "\\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD " + "\\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD ", + }, + /* 3.2.2 All 16 first bytes of 3-byte sequences, followed by space */ + { + "\xE0 \xE1 \xE2 \xE3 \xE4 \xE5 \xE6 \xE7 " + "\xE8 \xE9 \xEA \xEB \xEC \xED \xEE \xEF ", + NULL, + "\\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD " + "\\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD ", + }, + /* 3.2.3 All 8 first bytes of 4-byte sequences, followed by space */ + { + "\xF0 \xF1 \xF2 \xF3 \xF4 \xF5 \xF6 \xF7 ", + NULL, + "\\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD ", + }, + /* 3.2.4 All 4 first bytes of 5-byte sequences, followed by space */ + { + "\xF8 \xF9 \xFA \xFB ", + NULL, + "\\uFFFD \\uFFFD \\uFFFD \\uFFFD ", + }, + /* 3.2.5 All 2 first bytes of 6-byte sequences, followed by space */ + { + "\xFC \xFD ", + NULL, + "\\uFFFD \\uFFFD ", + }, + /* 3.3 Sequences with last continuation byte missing */ + /* 3.3.1 2-byte sequence with last byte missing (U+0000) */ + { + "\xC0", + NULL, + "\\uFFFD", + }, + /* 3.3.2 3-byte sequence with last byte missing (U+0000) */ + { + "\xE0\x80", + NULL, + "\\uFFFD", + }, + /* 3.3.3 4-byte sequence with last byte missing (U+0000) */ + { + "\xF0\x80\x80", + NULL, + "\\uFFFD", + }, + /* 3.3.4 5-byte sequence with last byte missing (U+0000) */ + { + "\xF8\x80\x80\x80", + NULL, + "\\uFFFD", + }, + /* 3.3.5 6-byte sequence with last byte missing (U+0000) */ + { + "\xFC\x80\x80\x80\x80", + NULL, + "\\uFFFD", + }, + /* 3.3.6 2-byte sequence with last byte missing (U+07FF) */ + { + "\xDF", + NULL, + "\\uFFFD", + }, + /* 3.3.7 3-byte sequence with last byte missing (U+FFFF) */ + { + "\xEF\xBF", + NULL, + "\\uFFFD", + }, + /* 3.3.8 4-byte sequence with last byte missing (U+1FFFFF) */ + { + "\xF7\xBF\xBF", + NULL, + "\\uFFFD", + }, + /* 3.3.9 5-byte sequence with last byte missing (U+3FFFFFF) */ + { + "\xFB\xBF\xBF\xBF", + NULL, + "\\uFFFD", + }, + /* 3.3.10 6-byte sequence with last byte missing (U+7FFFFFFF) */ + { + "\xFD\xBF\xBF\xBF\xBF", + NULL, + "\\uFFFD", + }, + /* 3.4 Concatenation of incomplete sequences */ + { + "\xC0\xE0\x80\xF0\x80\x80\xF8\x80\x80\x80\xFC\x80\x80\x80\x80" + "\xDF\xEF\xBF\xF7\xBF\xBF\xFB\xBF\xBF\xBF\xFD\xBF\xBF\xBF\xBF", + NULL, + "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD" + "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD", + }, + /* 3.5 Impossible bytes */ + { + "\xFE", + NULL, + "\\uFFFD", + }, + { + "\xFF", + NULL, + "\\uFFFD", + }, + { + "\xFE\xFE\xFF\xFF", + NULL, + "\\uFFFD\\uFFFD\\uFFFD\\uFFFD", + }, + /* 4 Overlong sequences */ + /* 4.1 Overlong '/' */ + { + "\xC0\xAF", + NULL, + "\\uFFFD", + }, + { + "\xE0\x80\xAF", + NULL, + "\\uFFFD", + }, + { + "\xF0\x80\x80\xAF", + NULL, + "\\uFFFD", + }, + { + "\xF8\x80\x80\x80\xAF", + NULL, + "\\uFFFD", + }, + { + "\xFC\x80\x80\x80\x80\xAF", + NULL, + "\\uFFFD", + }, + /* + * 4.2 Maximum overlong sequences + * Highest Unicode value that is still resulting in an + * overlong sequence if represented with the given number of + * bytes. This is a boundary test for safe UTF-8 decoders. + */ + { + /* \U+007F */ + "\xC1\xBF", + NULL, + "\\uFFFD", + }, + { + /* \U+07FF */ + "\xE0\x9F\xBF", + NULL, + "\\uFFFD", + }, + { + /* + * \U+FFFC + * The actual maximum would be U+FFFF, but that's a + * noncharacter. Testing U+FFFC seems more useful. See + * also 2.2.3 + */ + "\xF0\x8F\xBF\xBC", + NULL, + "\\uFFFD", + }, + { + /* \U+1FFFFF */ + "\xF8\x87\xBF\xBF\xBF", + NULL, + "\\uFFFD", + }, + { + /* \U+3FFFFFF */ + "\xFC\x83\xBF\xBF\xBF\xBF", + NULL, + "\\uFFFD", + }, + /* 4.3 Overlong representation of the NUL character */ + { + /* \U+0000 */ + "\xC0\x80", + "\xC0\x80", + "\\u0000", + }, + { + /* \U+0000 */ + "\xE0\x80\x80", + NULL, + "\\uFFFD", + }, + { + /* \U+0000 */ + "\xF0\x80\x80\x80", + NULL, + "\\uFFFD", + }, + { + /* \U+0000 */ + "\xF8\x80\x80\x80\x80", + NULL, + "\\uFFFD", + }, + { + /* \U+0000 */ + "\xFC\x80\x80\x80\x80\x80", + NULL, + "\\uFFFD", + }, + /* 5 Illegal code positions */ + /* 5.1 Single UTF-16 surrogates */ + { + /* \U+D800 */ + "\xED\xA0\x80", + NULL, + "\\uFFFD", + }, + { + /* \U+DB7F */ + "\xED\xAD\xBF", + NULL, + "\\uFFFD", + }, + { + /* \U+DB80 */ + "\xED\xAE\x80", + NULL, + "\\uFFFD", + }, + { + /* \U+DBFF */ + "\xED\xAF\xBF", + NULL, + "\\uFFFD", + }, + { + /* \U+DC00 */ + "\xED\xB0\x80", + NULL, + "\\uFFFD", + }, + { + /* \U+DF80 */ + "\xED\xBE\x80", + NULL, + "\\uFFFD", + }, + { + /* \U+DFFF */ + "\xED\xBF\xBF", + NULL, + "\\uFFFD", + }, + /* 5.2 Paired UTF-16 surrogates */ + { + /* \U+D800\U+DC00 */ + "\xED\xA0\x80\xED\xB0\x80", + NULL, + "\\uFFFD\\uFFFD", + }, + { + /* \U+D800\U+DFFF */ + "\xED\xA0\x80\xED\xBF\xBF", + NULL, + "\\uFFFD\\uFFFD", + }, + { + /* \U+DB7F\U+DC00 */ + "\xED\xAD\xBF\xED\xB0\x80", + NULL, + "\\uFFFD\\uFFFD", + }, + { + /* \U+DB7F\U+DFFF */ + "\xED\xAD\xBF\xED\xBF\xBF", + NULL, + "\\uFFFD\\uFFFD", + }, + { + /* \U+DB80\U+DC00 */ + "\xED\xAE\x80\xED\xB0\x80", + NULL, + "\\uFFFD\\uFFFD", + }, + { + /* \U+DB80\U+DFFF */ + "\xED\xAE\x80\xED\xBF\xBF", + NULL, + "\\uFFFD\\uFFFD", + }, + { + /* \U+DBFF\U+DC00 */ + "\xED\xAF\xBF\xED\xB0\x80", + NULL, + "\\uFFFD\\uFFFD", + }, + { + /* \U+DBFF\U+DFFF */ + "\xED\xAF\xBF\xED\xBF\xBF", + NULL, + "\\uFFFD\\uFFFD", + }, + /* 5.3 Other illegal code positions */ + /* BMP noncharacters */ + { + /* \U+FFFE */ + "\xEF\xBF\xBE", + NULL, + "\\uFFFD", + }, + { + /* \U+FFFF */ + "\xEF\xBF\xBF", + NULL, + "\\uFFFD", + }, + { + /* U+FDD0 */ + "\xEF\xB7\x90", + NULL, + "\\uFFFD", + }, + { + /* U+FDEF */ + "\xEF\xB7\xAF", + NULL, + "\\uFFFD", + }, + /* Plane 1 .. 16 noncharacters */ + { + /* U+1FFFE U+1FFFF U+2FFFE U+2FFFF ... U+10FFFE U+10FFFF */ + "\xF0\x9F\xBF\xBE\xF0\x9F\xBF\xBF" + "\xF0\xAF\xBF\xBE\xF0\xAF\xBF\xBF" + "\xF0\xBF\xBF\xBE\xF0\xBF\xBF\xBF" + "\xF1\x8F\xBF\xBE\xF1\x8F\xBF\xBF" + "\xF1\x9F\xBF\xBE\xF1\x9F\xBF\xBF" + "\xF1\xAF\xBF\xBE\xF1\xAF\xBF\xBF" + "\xF1\xBF\xBF\xBE\xF1\xBF\xBF\xBF" + "\xF2\x8F\xBF\xBE\xF2\x8F\xBF\xBF" + "\xF2\x9F\xBF\xBE\xF2\x9F\xBF\xBF" + "\xF2\xAF\xBF\xBE\xF2\xAF\xBF\xBF" + "\xF2\xBF\xBF\xBE\xF2\xBF\xBF\xBF" + "\xF3\x8F\xBF\xBE\xF3\x8F\xBF\xBF" + "\xF3\x9F\xBF\xBE\xF3\x9F\xBF\xBF" + "\xF3\xAF\xBF\xBE\xF3\xAF\xBF\xBF" + "\xF3\xBF\xBF\xBE\xF3\xBF\xBF\xBF" + "\xF4\x8F\xBF\xBE\xF4\x8F\xBF\xBF", + NULL, + "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD" + "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD" + "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD" + "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD", + }, + {} + }; + int i, j; + QString *str; + const char *json_in, *utf8_out, *utf8_in, *json_out, *tail; + char *end, *in, *jstr; + + for (i = 0; test_cases[i].json_in; i++) { + for (j = 0; j < 2; j++) { + json_in = test_cases[i].json_in; + utf8_out = test_cases[i].utf8_out; + utf8_in = test_cases[i].utf8_out ?: test_cases[i].json_in; + json_out = test_cases[i].json_out ?: test_cases[i].json_in; + + /* Parse @json_in, expect @utf8_out */ + if (utf8_out) { + str = from_json_str(json_in, j, &error_abort); + g_assert_cmpstr(qstring_get_str(str), ==, utf8_out); + qobject_unref(str); + } else { + str = from_json_str(json_in, j, NULL); + g_assert(!str); + /* + * Failure may be due to any sequence, but *all* sequences + * are expected to fail. Test each one in isolation. + */ + for (tail = json_in; *tail; tail = end) { + mod_utf8_codepoint(tail, 6, &end); + if (*end == ' ') { + end++; + } + in = g_strndup(tail, end - tail); + str = from_json_str(in, j, NULL); + g_assert(!str); + g_free(in); + } + } + + /* Unparse @utf8_in, expect @json_out */ + str = qstring_from_str(utf8_in); + jstr = to_json_str(str); + g_assert_cmpstr(jstr, ==, json_out); + qobject_unref(str); + g_free(jstr); + + /* Parse @json_out right back, unless it has replacements */ + if (!strstr(json_out, "\\uFFFD")) { + str = from_json_str(json_out, j, &error_abort); + g_assert_cmpstr(qstring_get_str(str), ==, utf8_in); + qobject_unref(str); + } + } + } +} + +static void int_number(void) +{ + struct { + const char *encoded; + int64_t decoded; + const char *reencoded; + } test_cases[] = { + { "0", 0 }, + { "1234", 1234 }, + { "1", 1 }, + { "-32", -32 }, + { "-0", 0, "0" }, + {}, + }; + int i; + QNum *qnum; + int64_t ival; + uint64_t uval; + GString *str; + + for (i = 0; test_cases[i].encoded; i++) { + qnum = qobject_to(QNum, + qobject_from_json(test_cases[i].encoded, + &error_abort)); + g_assert(qnum); + g_assert(qnum_get_try_int(qnum, &ival)); + g_assert_cmpint(ival, ==, test_cases[i].decoded); + if (test_cases[i].decoded >= 0) { + g_assert(qnum_get_try_uint(qnum, &uval)); + g_assert_cmpuint(uval, ==, (uint64_t)test_cases[i].decoded); + } else { + g_assert(!qnum_get_try_uint(qnum, &uval)); + } + g_assert_cmpfloat(qnum_get_double(qnum), ==, + (double)test_cases[i].decoded); + + str = qobject_to_json(QOBJECT(qnum)); + g_assert_cmpstr(str->str, ==, + test_cases[i].reencoded ?: test_cases[i].encoded); + g_string_free(str, true); + + qobject_unref(qnum); + } +} + +static void uint_number(void) +{ + struct { + const char *encoded; + uint64_t decoded; + const char *reencoded; + } test_cases[] = { + { "9223372036854775808", (uint64_t)1 << 63 }, + { "18446744073709551615", UINT64_MAX }, + {}, + }; + int i; + QNum *qnum; + int64_t ival; + uint64_t uval; + GString *str; + + for (i = 0; test_cases[i].encoded; i++) { + qnum = qobject_to(QNum, + qobject_from_json(test_cases[i].encoded, + &error_abort)); + g_assert(qnum); + g_assert(qnum_get_try_uint(qnum, &uval)); + g_assert_cmpuint(uval, ==, test_cases[i].decoded); + g_assert(!qnum_get_try_int(qnum, &ival)); + g_assert_cmpfloat(qnum_get_double(qnum), ==, + (double)test_cases[i].decoded); + + str = qobject_to_json(QOBJECT(qnum)); + g_assert_cmpstr(str->str, ==, + test_cases[i].reencoded ?: test_cases[i].encoded); + g_string_free(str, true); + + qobject_unref(qnum); + } +} + +static void float_number(void) +{ + struct { + const char *encoded; + double decoded; + const char *reencoded; + } test_cases[] = { + { "32.43", 32.43 }, + { "0.222", 0.222 }, + { "-32.12313", -32.12313, "-32.123130000000003" }, + { "-32.20e-10", -32.20e-10, "-3.22e-09" }, + { "18446744073709551616", 0x1p64, "1.8446744073709552e+19" }, + { "-9223372036854775809", -0x1p63, "-9.2233720368547758e+18" }, + {}, + }; + int i; + QNum *qnum; + int64_t ival; + uint64_t uval; + GString *str; + + for (i = 0; test_cases[i].encoded; i++) { + qnum = qobject_to(QNum, + qobject_from_json(test_cases[i].encoded, + &error_abort)); + g_assert(qnum); + g_assert_cmpfloat(qnum_get_double(qnum), ==, test_cases[i].decoded); + g_assert(!qnum_get_try_int(qnum, &ival)); + g_assert(!qnum_get_try_uint(qnum, &uval)); + + str = qobject_to_json(QOBJECT(qnum)); + g_assert_cmpstr(str->str, ==, + test_cases[i].reencoded ?: test_cases[i].encoded); + g_string_free(str, true); + + qobject_unref(qnum); + } +} + +static void keyword_literal(void) +{ + QObject *obj; + QBool *qbool; + QNull *null; + GString *str; + + obj = qobject_from_json("true", &error_abort); + qbool = qobject_to(QBool, obj); + g_assert(qbool); + g_assert(qbool_get_bool(qbool) == true); + + str = qobject_to_json(obj); + g_assert_cmpstr(str->str, ==, "true"); + g_string_free(str, true); + + qobject_unref(qbool); + + obj = qobject_from_json("false", &error_abort); + qbool = qobject_to(QBool, obj); + g_assert(qbool); + g_assert(qbool_get_bool(qbool) == false); + + str = qobject_to_json(obj); + g_assert_cmpstr(str->str, ==, "false"); + g_string_free(str, true); + + qobject_unref(qbool); + + obj = qobject_from_json("null", &error_abort); + g_assert(obj != NULL); + g_assert(qobject_type(obj) == QTYPE_QNULL); + + null = qnull(); + g_assert(QOBJECT(null) == obj); + + qobject_unref(obj); + qobject_unref(null); +} + +static void interpolation_valid(void) +{ + long long value_lld = 0x123456789abcdefLL; + int64_t value_d64 = value_lld; + long value_ld = (long)value_lld; + int value_d = (int)value_lld; + unsigned long long value_llu = 0xfedcba9876543210ULL; + uint64_t value_u64 = value_llu; + unsigned long value_lu = (unsigned long)value_llu; + unsigned value_u = (unsigned)value_llu; + double value_f = 2.323423423; + const char *value_s = "hello world"; + QObject *value_p = QOBJECT(qnull()); + QBool *qbool; + QNum *qnum; + QString *qstr; + QObject *qobj; + + /* bool */ + + qbool = qobject_to(QBool, qobject_from_jsonf_nofail("%i", false)); + g_assert(qbool); + g_assert(qbool_get_bool(qbool) == false); + qobject_unref(qbool); + + /* Test that non-zero values other than 1 get collapsed to true */ + qbool = qobject_to(QBool, qobject_from_jsonf_nofail("%i", 2)); + g_assert(qbool); + g_assert(qbool_get_bool(qbool) == true); + qobject_unref(qbool); + + /* number */ + + qnum = qobject_to(QNum, qobject_from_jsonf_nofail("%d", value_d)); + g_assert_cmpint(qnum_get_int(qnum), ==, value_d); + qobject_unref(qnum); + + qnum = qobject_to(QNum, qobject_from_jsonf_nofail("%ld", value_ld)); + g_assert_cmpint(qnum_get_int(qnum), ==, value_ld); + qobject_unref(qnum); + + qnum = qobject_to(QNum, qobject_from_jsonf_nofail("%lld", value_lld)); + g_assert_cmpint(qnum_get_int(qnum), ==, value_lld); + qobject_unref(qnum); + + qnum = qobject_to(QNum, qobject_from_jsonf_nofail("%" PRId64, value_d64)); + g_assert_cmpint(qnum_get_int(qnum), ==, value_lld); + qobject_unref(qnum); + + qnum = qobject_to(QNum, qobject_from_jsonf_nofail("%u", value_u)); + g_assert_cmpuint(qnum_get_uint(qnum), ==, value_u); + qobject_unref(qnum); + + qnum = qobject_to(QNum, qobject_from_jsonf_nofail("%lu", value_lu)); + g_assert_cmpuint(qnum_get_uint(qnum), ==, value_lu); + qobject_unref(qnum); + + qnum = qobject_to(QNum, qobject_from_jsonf_nofail("%llu", value_llu)); + g_assert_cmpuint(qnum_get_uint(qnum), ==, value_llu); + qobject_unref(qnum); + + qnum = qobject_to(QNum, qobject_from_jsonf_nofail("%" PRIu64, value_u64)); + g_assert_cmpuint(qnum_get_uint(qnum), ==, value_llu); + qobject_unref(qnum); + + qnum = qobject_to(QNum, qobject_from_jsonf_nofail("%f", value_f)); + g_assert(qnum_get_double(qnum) == value_f); + qobject_unref(qnum); + + /* string */ + + qstr = qobject_to(QString, qobject_from_jsonf_nofail("%s", value_s)); + g_assert_cmpstr(qstring_get_str(qstr), ==, value_s); + qobject_unref(qstr); + + /* object */ + + qobj = qobject_from_jsonf_nofail("%p", value_p); + g_assert(qobj == value_p); +} + +static void interpolation_unknown(void) +{ + if (g_test_subprocess()) { + qobject_from_jsonf_nofail("%x", 666); + } + g_test_trap_subprocess(NULL, 0, 0); + g_test_trap_assert_failed(); + g_test_trap_assert_stderr("*Unexpected error*" + "invalid interpolation '%x'*"); +} + +static void interpolation_string(void) +{ + if (g_test_subprocess()) { + qobject_from_jsonf_nofail("['%s', %s]", "eins", "zwei"); + } + g_test_trap_subprocess(NULL, 0, 0); + g_test_trap_assert_failed(); + g_test_trap_assert_stderr("*Unexpected error*" + "can't interpolate into string*"); +} + +static void simple_dict(void) +{ + int i; + struct { + const char *encoded; + QLitObject decoded; + } test_cases[] = { + { + .encoded = "{\"foo\": 42, \"bar\": \"hello world\"}", + .decoded = QLIT_QDICT(((QLitDictEntry[]){ + { "foo", QLIT_QNUM(42) }, + { "bar", QLIT_QSTR("hello world") }, + { } + })), + }, { + .encoded = "{}", + .decoded = QLIT_QDICT(((QLitDictEntry[]){ + { } + })), + }, { + .encoded = "{\"foo\": 43}", + .decoded = QLIT_QDICT(((QLitDictEntry[]){ + { "foo", QLIT_QNUM(43) }, + { } + })), + }, + { } + }; + + for (i = 0; test_cases[i].encoded; i++) { + QObject *obj; + GString *str; + + obj = qobject_from_json(test_cases[i].encoded, &error_abort); + g_assert(qlit_equal_qobject(&test_cases[i].decoded, obj)); + + str = qobject_to_json(obj); + qobject_unref(obj); + + obj = qobject_from_json(str->str, &error_abort); + g_assert(qlit_equal_qobject(&test_cases[i].decoded, obj)); + qobject_unref(obj); + g_string_free(str, true); + } +} + +/* + * this generates json of the form: + * a(0,m) = [0, 1, ..., m-1] + * a(n,m) = { + * 'key0': a(0,m), + * 'key1': a(1,m), + * ... + * 'key(n-1)': a(n-1,m) + * } + */ +static void gen_test_json(GString *gstr, int nest_level_max, + int elem_count) +{ + int i; + + g_assert(gstr); + if (nest_level_max == 0) { + g_string_append(gstr, "["); + for (i = 0; i < elem_count; i++) { + g_string_append_printf(gstr, "%d", i); + if (i < elem_count - 1) { + g_string_append_printf(gstr, ", "); + } + } + g_string_append(gstr, "]"); + return; + } + + g_string_append(gstr, "{"); + for (i = 0; i < nest_level_max; i++) { + g_string_append_printf(gstr, "'key%d': ", i); + gen_test_json(gstr, i, elem_count); + if (i < nest_level_max - 1) { + g_string_append(gstr, ","); + } + } + g_string_append(gstr, "}"); +} + +static void large_dict(void) +{ + GString *gstr = g_string_new(""); + QObject *obj; + + gen_test_json(gstr, 10, 100); + obj = qobject_from_json(gstr->str, &error_abort); + g_assert(obj != NULL); + + qobject_unref(obj); + g_string_free(gstr, true); +} + +static void simple_list(void) +{ + int i; + struct { + const char *encoded; + QLitObject decoded; + } test_cases[] = { + { + .encoded = "[43,42]", + .decoded = QLIT_QLIST(((QLitObject[]){ + QLIT_QNUM(43), + QLIT_QNUM(42), + { } + })), + }, + { + .encoded = "[43]", + .decoded = QLIT_QLIST(((QLitObject[]){ + QLIT_QNUM(43), + { } + })), + }, + { + .encoded = "[]", + .decoded = QLIT_QLIST(((QLitObject[]){ + { } + })), + }, + { + .encoded = "[{}]", + .decoded = QLIT_QLIST(((QLitObject[]){ + QLIT_QDICT(((QLitDictEntry[]){ + {}, + })), + {}, + })), + }, + { } + }; + + for (i = 0; test_cases[i].encoded; i++) { + QObject *obj; + GString *str; + + obj = qobject_from_json(test_cases[i].encoded, &error_abort); + g_assert(qlit_equal_qobject(&test_cases[i].decoded, obj)); + + str = qobject_to_json(obj); + qobject_unref(obj); + + obj = qobject_from_json(str->str, &error_abort); + g_assert(qlit_equal_qobject(&test_cases[i].decoded, obj)); + qobject_unref(obj); + g_string_free(str, true); + } +} + +static void simple_whitespace(void) +{ + int i; + struct { + const char *encoded; + QLitObject decoded; + } test_cases[] = { + { + .encoded = " [ 43 , 42 ]", + .decoded = QLIT_QLIST(((QLitObject[]){ + QLIT_QNUM(43), + QLIT_QNUM(42), + { } + })), + }, + { + .encoded = "\t[ 43 , { 'h' : 'b' },\r\n\t[ ], 42 ]\n", + .decoded = QLIT_QLIST(((QLitObject[]){ + QLIT_QNUM(43), + QLIT_QDICT(((QLitDictEntry[]){ + { "h", QLIT_QSTR("b") }, + { }})), + QLIT_QLIST(((QLitObject[]){ + { }})), + QLIT_QNUM(42), + { } + })), + }, + { + .encoded = " [ 43 , { 'h' : 'b' , 'a' : 32 }, [ ], 42 ]", + .decoded = QLIT_QLIST(((QLitObject[]){ + QLIT_QNUM(43), + QLIT_QDICT(((QLitDictEntry[]){ + { "h", QLIT_QSTR("b") }, + { "a", QLIT_QNUM(32) }, + { }})), + QLIT_QLIST(((QLitObject[]){ + { }})), + QLIT_QNUM(42), + { } + })), + }, + { } + }; + + for (i = 0; test_cases[i].encoded; i++) { + QObject *obj; + GString *str; + + obj = qobject_from_json(test_cases[i].encoded, &error_abort); + g_assert(qlit_equal_qobject(&test_cases[i].decoded, obj)); + + str = qobject_to_json(obj); + qobject_unref(obj); + + obj = qobject_from_json(str->str, &error_abort); + g_assert(qlit_equal_qobject(&test_cases[i].decoded, obj)); + + qobject_unref(obj); + g_string_free(str, true); + } +} + +static void simple_interpolation(void) +{ + QObject *embedded_obj; + QObject *obj; + QLitObject decoded = QLIT_QLIST(((QLitObject[]){ + QLIT_QNUM(1), + QLIT_QSTR("100%"), + QLIT_QLIST(((QLitObject[]){ + QLIT_QNUM(32), + QLIT_QNUM(42), + {}})), + {}})); + + embedded_obj = qobject_from_json("[32, 42]", &error_abort); + g_assert(embedded_obj != NULL); + + obj = qobject_from_jsonf_nofail("[%d, '100%%', %p]", 1, embedded_obj); + g_assert(qlit_equal_qobject(&decoded, obj)); + + qobject_unref(obj); +} + +static void empty_input(void) +{ + Error *err = NULL; + QObject *obj; + + obj = qobject_from_json("", &err); + error_free_or_abort(&err); + g_assert(obj == NULL); +} + +static void blank_input(void) +{ + Error *err = NULL; + QObject *obj; + + obj = qobject_from_json("\n ", &err); + error_free_or_abort(&err); + g_assert(obj == NULL); +} + +static void junk_input(void) +{ + /* Note: junk within strings is covered elsewhere */ + Error *err = NULL; + QObject *obj; + + obj = qobject_from_json("@", &err); + error_free_or_abort(&err); + g_assert(obj == NULL); + + obj = qobject_from_json("{\x01", &err); + error_free_or_abort(&err); + g_assert(obj == NULL); + + obj = qobject_from_json("[0\xFF]", &err); + error_free_or_abort(&err); + g_assert(obj == NULL); + + obj = qobject_from_json("00", &err); + error_free_or_abort(&err); + g_assert(obj == NULL); + + obj = qobject_from_json("[1e", &err); + error_free_or_abort(&err); + g_assert(obj == NULL); + + obj = qobject_from_json("truer", &err); + error_free_or_abort(&err); + g_assert(obj == NULL); +} + +static void unterminated_string(void) +{ + Error *err = NULL; + QObject *obj = qobject_from_json("\"abc", &err); + error_free_or_abort(&err); + g_assert(obj == NULL); +} + +static void unterminated_sq_string(void) +{ + Error *err = NULL; + QObject *obj = qobject_from_json("'abc", &err); + error_free_or_abort(&err); + g_assert(obj == NULL); +} + +static void unterminated_escape(void) +{ + Error *err = NULL; + QObject *obj = qobject_from_json("\"abc\\\"", &err); + error_free_or_abort(&err); + g_assert(obj == NULL); +} + +static void unterminated_array(void) +{ + Error *err = NULL; + QObject *obj = qobject_from_json("[32", &err); + error_free_or_abort(&err); + g_assert(obj == NULL); +} + +static void unterminated_array_comma(void) +{ + Error *err = NULL; + QObject *obj = qobject_from_json("[32,", &err); + error_free_or_abort(&err); + g_assert(obj == NULL); +} + +static void invalid_array_comma(void) +{ + Error *err = NULL; + QObject *obj = qobject_from_json("[32,}", &err); + error_free_or_abort(&err); + g_assert(obj == NULL); +} + +static void unterminated_dict(void) +{ + Error *err = NULL; + QObject *obj = qobject_from_json("{'abc':32", &err); + error_free_or_abort(&err); + g_assert(obj == NULL); +} + +static void unterminated_dict_comma(void) +{ + Error *err = NULL; + QObject *obj = qobject_from_json("{'abc':32,", &err); + error_free_or_abort(&err); + g_assert(obj == NULL); +} + +static void invalid_dict_comma(void) +{ + Error *err = NULL; + QObject *obj = qobject_from_json("{'abc':32,}", &err); + error_free_or_abort(&err); + g_assert(obj == NULL); +} + +static void invalid_dict_key(void) +{ + Error *err = NULL; + QObject *obj = qobject_from_json("{32:'abc'}", &err); + error_free_or_abort(&err); + g_assert(obj == NULL); +} + +static void unterminated_literal(void) +{ + Error *err = NULL; + QObject *obj = qobject_from_json("nul", &err); + error_free_or_abort(&err); + g_assert(obj == NULL); +} + +static char *make_nest(char *buf, size_t cnt) +{ + memset(buf, '[', cnt - 1); + buf[cnt - 1] = '{'; + buf[cnt] = '}'; + memset(buf + cnt + 1, ']', cnt - 1); + buf[2 * cnt] = 0; + return buf; +} + +static void limits_nesting(void) +{ + Error *err = NULL; + enum { max_nesting = 1024 }; /* see qobject/json-streamer.c */ + char buf[2 * (max_nesting + 1) + 1]; + QObject *obj; + + obj = qobject_from_json(make_nest(buf, max_nesting), &error_abort); + g_assert(obj != NULL); + qobject_unref(obj); + + obj = qobject_from_json(make_nest(buf, max_nesting + 1), &err); + error_free_or_abort(&err); + g_assert(obj == NULL); +} + +static void multiple_values(void) +{ + Error *err = NULL; + QObject *obj; + + obj = qobject_from_json("false true", &err); + error_free_or_abort(&err); + g_assert(obj == NULL); + + obj = qobject_from_json("} true", &err); + error_free_or_abort(&err); + g_assert(obj == NULL); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/literals/string/escaped", escaped_string); + g_test_add_func("/literals/string/quotes", string_with_quotes); + g_test_add_func("/literals/string/utf8", utf8_string); + + g_test_add_func("/literals/number/int", int_number); + g_test_add_func("/literals/number/uint", uint_number); + g_test_add_func("/literals/number/float", float_number); + + g_test_add_func("/literals/keyword", keyword_literal); + + g_test_add_func("/literals/interpolation/valid", interpolation_valid); + g_test_add_func("/literals/interpolation/unkown", interpolation_unknown); + g_test_add_func("/literals/interpolation/string", interpolation_string); + + g_test_add_func("/dicts/simple_dict", simple_dict); + g_test_add_func("/dicts/large_dict", large_dict); + g_test_add_func("/lists/simple_list", simple_list); + + g_test_add_func("/mixed/simple_whitespace", simple_whitespace); + g_test_add_func("/mixed/interpolation", simple_interpolation); + + g_test_add_func("/errors/empty", empty_input); + g_test_add_func("/errors/blank", blank_input); + g_test_add_func("/errors/junk", junk_input); + g_test_add_func("/errors/unterminated/string", unterminated_string); + g_test_add_func("/errors/unterminated/escape", unterminated_escape); + g_test_add_func("/errors/unterminated/sq_string", unterminated_sq_string); + g_test_add_func("/errors/unterminated/array", unterminated_array); + g_test_add_func("/errors/unterminated/array_comma", unterminated_array_comma); + g_test_add_func("/errors/unterminated/dict", unterminated_dict); + g_test_add_func("/errors/unterminated/dict_comma", unterminated_dict_comma); + g_test_add_func("/errors/invalid_array_comma", invalid_array_comma); + g_test_add_func("/errors/invalid_dict_comma", invalid_dict_comma); + g_test_add_func("/errors/invalid_dict_key", invalid_dict_key); + g_test_add_func("/errors/unterminated/literal", unterminated_literal); + g_test_add_func("/errors/limits/nesting", limits_nesting); + g_test_add_func("/errors/multiple_values", multiple_values); + + return g_test_run(); +} diff --git a/tests/unit/check-qlist.c b/tests/unit/check-qlist.c new file mode 100644 index 0000000000..3cd0ccbf19 --- /dev/null +++ b/tests/unit/check-qlist.c @@ -0,0 +1,103 @@ +/* + * QList unit-tests. + * + * Copyright (C) 2009 Red Hat Inc. + * + * Authors: + * Luiz Capitulino + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + */ +#include "qemu/osdep.h" + +#include "qapi/qmp/qnum.h" +#include "qapi/qmp/qlist.h" + +/* + * Public Interface test-cases + * + * (with some violations to access 'private' data) + */ + +static void qlist_new_test(void) +{ + QList *qlist; + + qlist = qlist_new(); + g_assert(qlist != NULL); + g_assert(qlist->base.refcnt == 1); + g_assert(qobject_type(QOBJECT(qlist)) == QTYPE_QLIST); + + qobject_unref(qlist); +} + +static void qlist_append_test(void) +{ + QNum *qi; + QList *qlist; + QListEntry *entry; + + qi = qnum_from_int(42); + + qlist = qlist_new(); + qlist_append(qlist, qi); + + entry = QTAILQ_FIRST(&qlist->head); + g_assert(entry != NULL); + g_assert(entry->value == QOBJECT(qi)); + + qobject_unref(qlist); +} + +static void qobject_to_qlist_test(void) +{ + QList *qlist; + + qlist = qlist_new(); + + g_assert(qobject_to(QList, QOBJECT(qlist)) == qlist); + + qobject_unref(qlist); +} + +static void qlist_iter_test(void) +{ + const int iter_max = 42; + int i; + QList *qlist; + QListEntry *entry; + QNum *qi; + int64_t val; + + qlist = qlist_new(); + + for (i = 0; i < iter_max; i++) + qlist_append_int(qlist, i); + + i = 0; + QLIST_FOREACH_ENTRY(qlist, entry) { + qi = qobject_to(QNum, qlist_entry_obj(entry)); + g_assert(qi != NULL); + + g_assert(qnum_get_try_int(qi, &val)); + g_assert_cmpint(val, ==, i); + i++; + } + + g_assert(i == iter_max); + + qobject_unref(qlist); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/public/new", qlist_new_test); + g_test_add_func("/public/append", qlist_append_test); + g_test_add_func("/public/to_qlist", qobject_to_qlist_test); + g_test_add_func("/public/iter", qlist_iter_test); + + return g_test_run(); +} diff --git a/tests/unit/check-qlit.c b/tests/unit/check-qlit.c new file mode 100644 index 0000000000..bd6798d912 --- /dev/null +++ b/tests/unit/check-qlit.c @@ -0,0 +1,101 @@ +/* + * QLit unit-tests. + * + * Copyright (C) 2017 Red Hat Inc. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" + +#include "qapi/qmp/qbool.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qmp/qlist.h" +#include "qapi/qmp/qlit.h" +#include "qapi/qmp/qnum.h" +#include "qapi/qmp/qstring.h" + +static QLitObject qlit = QLIT_QDICT(((QLitDictEntry[]) { + { "foo", QLIT_QNUM(42) }, + { "bar", QLIT_QSTR("hello world") }, + { "baz", QLIT_QNULL }, + { "bee", QLIT_QLIST(((QLitObject[]) { + QLIT_QNUM(43), + QLIT_QNUM(44), + QLIT_QBOOL(true), + { }, + }))}, + { }, +})); + +static QLitObject qlit_foo = QLIT_QDICT(((QLitDictEntry[]) { + { "foo", QLIT_QNUM(42) }, + { }, +})); + +static QObject *make_qobject(void) +{ + QDict *qdict = qdict_new(); + QList *list = qlist_new(); + + qdict_put_int(qdict, "foo", 42); + qdict_put_str(qdict, "bar", "hello world"); + qdict_put_null(qdict, "baz"); + + qlist_append_int(list, 43); + qlist_append_int(list, 44); + qlist_append_bool(list, true); + qdict_put(qdict, "bee", list); + + return QOBJECT(qdict); +} + +static void qlit_equal_qobject_test(void) +{ + QObject *qobj = make_qobject(); + + g_assert(qlit_equal_qobject(&qlit, qobj)); + + g_assert(!qlit_equal_qobject(&qlit_foo, qobj)); + + qdict_put(qobject_to(QDict, qobj), "bee", qlist_new()); + g_assert(!qlit_equal_qobject(&qlit, qobj)); + + qobject_unref(qobj); +} + +static void qobject_from_qlit_test(void) +{ + QObject *obj, *qobj = qobject_from_qlit(&qlit); + QDict *qdict; + QList *bee; + + qdict = qobject_to(QDict, qobj); + g_assert_cmpint(qdict_get_int(qdict, "foo"), ==, 42); + g_assert_cmpstr(qdict_get_str(qdict, "bar"), ==, "hello world"); + g_assert(qobject_type(qdict_get(qdict, "baz")) == QTYPE_QNULL); + + bee = qdict_get_qlist(qdict, "bee"); + obj = qlist_pop(bee); + g_assert_cmpint(qnum_get_int(qobject_to(QNum, obj)), ==, 43); + qobject_unref(obj); + obj = qlist_pop(bee); + g_assert_cmpint(qnum_get_int(qobject_to(QNum, obj)), ==, 44); + qobject_unref(obj); + obj = qlist_pop(bee); + g_assert(qbool_get_bool(qobject_to(QBool, obj))); + qobject_unref(obj); + + qobject_unref(qobj); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/qlit/equal_qobject", qlit_equal_qobject_test); + g_test_add_func("/qlit/qobject_from_qlit", qobject_from_qlit_test); + + return g_test_run(); +} diff --git a/tests/unit/check-qnull.c b/tests/unit/check-qnull.c new file mode 100644 index 0000000000..ebf21db83c --- /dev/null +++ b/tests/unit/check-qnull.c @@ -0,0 +1,78 @@ +/* + * QNull unit-tests. + * + * Copyright (C) 2016 Red Hat Inc. + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + */ +#include "qemu/osdep.h" + +#include "qapi/qmp/qnull.h" +#include "qemu-common.h" +#include "qapi/qobject-input-visitor.h" +#include "qapi/qobject-output-visitor.h" +#include "qapi/error.h" + +/* + * Public Interface test-cases + * + * (with some violations to access 'private' data) + */ + +static void qnull_ref_test(void) +{ + QObject *obj; + + g_assert(qnull_.base.refcnt == 1); + obj = QOBJECT(qnull()); + g_assert(obj); + g_assert(obj == QOBJECT(&qnull_)); + g_assert(qnull_.base.refcnt == 2); + g_assert(qobject_type(obj) == QTYPE_QNULL); + qobject_unref(obj); + g_assert(qnull_.base.refcnt == 1); +} + +static void qnull_visit_test(void) +{ + QObject *obj; + Visitor *v; + QNull *null; + + /* + * Most tests of interactions between QObject and visitors are in + * test-qmp-*-visitor; but these tests live here because they + * depend on layering violations to check qnull_ refcnt. + */ + + g_assert(qnull_.base.refcnt == 1); + obj = QOBJECT(qnull()); + v = qobject_input_visitor_new(obj); + qobject_unref(obj); + visit_type_null(v, NULL, &null, &error_abort); + g_assert(obj == QOBJECT(&qnull_)); + qobject_unref(null); + visit_free(v); + + null = NULL; + v = qobject_output_visitor_new(&obj); + visit_type_null(v, NULL, &null, &error_abort); + visit_complete(v, &obj); + g_assert(obj == QOBJECT(&qnull_)); + qobject_unref(null); + qobject_unref(obj); + visit_free(v); + + g_assert(qnull_.base.refcnt == 1); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/public/qnull_ref", qnull_ref_test); + g_test_add_func("/public/qnull_visit", qnull_visit_test); + + return g_test_run(); +} diff --git a/tests/unit/check-qnum.c b/tests/unit/check-qnum.c new file mode 100644 index 0000000000..b85fca2302 --- /dev/null +++ b/tests/unit/check-qnum.c @@ -0,0 +1,175 @@ +/* + * QNum unit-tests. + * + * Copyright (C) 2009 Red Hat Inc. + * Copyright IBM, Corp. 2009 + * + * Authors: + * Luiz Capitulino + * Anthony Liguori + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + */ + +#include "qemu/osdep.h" + +#include "qapi/qmp/qnum.h" +#include "qemu-common.h" + +/* + * Public Interface test-cases + * + * (with some violations to access 'private' data) + */ + +static void qnum_from_int_test(void) +{ + QNum *qn; + const int value = -42; + + qn = qnum_from_int(value); + g_assert(qn != NULL); + g_assert_cmpint(qn->kind, ==, QNUM_I64); + g_assert_cmpint(qn->u.i64, ==, value); + g_assert_cmpint(qn->base.refcnt, ==, 1); + g_assert_cmpint(qobject_type(QOBJECT(qn)), ==, QTYPE_QNUM); + + qobject_unref(qn); +} + +static void qnum_from_uint_test(void) +{ + QNum *qn; + const uint64_t value = UINT64_MAX; + + qn = qnum_from_uint(value); + g_assert(qn != NULL); + g_assert_cmpint(qn->kind, ==, QNUM_U64); + g_assert(qn->u.u64 == value); + g_assert(qn->base.refcnt == 1); + g_assert(qobject_type(QOBJECT(qn)) == QTYPE_QNUM); + + qobject_unref(qn); +} + +static void qnum_from_double_test(void) +{ + QNum *qn; + const double value = -42.23423; + + qn = qnum_from_double(value); + g_assert(qn != NULL); + g_assert_cmpint(qn->kind, ==, QNUM_DOUBLE); + g_assert_cmpfloat(qn->u.dbl, ==, value); + g_assert_cmpint(qn->base.refcnt, ==, 1); + g_assert_cmpint(qobject_type(QOBJECT(qn)), ==, QTYPE_QNUM); + + qobject_unref(qn); +} + +static void qnum_from_int64_test(void) +{ + QNum *qn; + const int64_t value = 0x1234567890abcdefLL; + + qn = qnum_from_int(value); + g_assert_cmpint((int64_t) qn->u.i64, ==, value); + + qobject_unref(qn); +} + +static void qnum_get_int_test(void) +{ + QNum *qn; + const int value = 123456; + + qn = qnum_from_int(value); + g_assert_cmpint(qnum_get_int(qn), ==, value); + + qobject_unref(qn); +} + +static void qnum_get_uint_test(void) +{ + QNum *qn; + const int value = 123456; + uint64_t val; + int64_t ival; + + qn = qnum_from_uint(value); + g_assert(qnum_get_try_uint(qn, &val)); + g_assert_cmpuint(val, ==, value); + qobject_unref(qn); + + qn = qnum_from_int(value); + g_assert(qnum_get_try_uint(qn, &val)); + g_assert_cmpuint(val, ==, value); + qobject_unref(qn); + + /* invalid cases */ + qn = qnum_from_int(-1); + g_assert(!qnum_get_try_uint(qn, &val)); + qobject_unref(qn); + + qn = qnum_from_uint(-1ULL); + g_assert(!qnum_get_try_int(qn, &ival)); + qobject_unref(qn); + + qn = qnum_from_double(0.42); + g_assert(!qnum_get_try_uint(qn, &val)); + qobject_unref(qn); +} + +static void qobject_to_qnum_test(void) +{ + QNum *qn; + + qn = qnum_from_int(0); + g_assert(qobject_to(QNum, QOBJECT(qn)) == qn); + qobject_unref(qn); + + qn = qnum_from_double(0); + g_assert(qobject_to(QNum, QOBJECT(qn)) == qn); + qobject_unref(qn); +} + +static void qnum_to_string_test(void) +{ + QNum *qn; + char *tmp; + + qn = qnum_from_int(123456); + tmp = qnum_to_string(qn); + g_assert_cmpstr(tmp, ==, "123456"); + g_free(tmp); + qobject_unref(qn); + + qn = qnum_from_double(0.42); + tmp = qnum_to_string(qn); + g_assert_cmpstr(tmp, ==, "0.41999999999999998"); + g_free(tmp); + qobject_unref(qn); + + qn = qnum_from_double(2.718281828459045); + tmp = qnum_to_string(qn); + g_assert_cmpstr(tmp, ==, "2.7182818284590451"); + g_free(tmp); + qobject_unref(qn); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/qnum/from_int", qnum_from_int_test); + g_test_add_func("/qnum/from_uint", qnum_from_uint_test); + g_test_add_func("/qnum/from_double", qnum_from_double_test); + g_test_add_func("/qnum/from_int64", qnum_from_int64_test); + g_test_add_func("/qnum/get_int", qnum_get_int_test); + g_test_add_func("/qnum/get_uint", qnum_get_uint_test); + g_test_add_func("/qnum/to_qnum", qobject_to_qnum_test); + g_test_add_func("/qnum/to_string", qnum_to_string_test); + + return g_test_run(); +} diff --git a/tests/unit/check-qobject.c b/tests/unit/check-qobject.c new file mode 100644 index 0000000000..c1713d15af --- /dev/null +++ b/tests/unit/check-qobject.c @@ -0,0 +1,334 @@ +/* + * Generic QObject unit-tests. + * + * Copyright (C) 2017 Red Hat Inc. + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "block/qdict.h" +#include "qapi/error.h" +#include "qapi/qmp/qbool.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qmp/qlist.h" +#include "qapi/qmp/qnull.h" +#include "qapi/qmp/qnum.h" +#include "qapi/qmp/qstring.h" +#include "qemu-common.h" + +#include + +/* Marks the end of the test_equality() argument list. + * We cannot use NULL there because that is a valid argument. */ +static QObject test_equality_end_of_arguments; + +/** + * Test whether all variadic QObject *arguments are equal (@expected + * is true) or whether they are all not equal (@expected is false). + * Every QObject is tested to be equal to itself (to test + * reflexivity), all tests are done both ways (to test symmetry), and + * transitivity is not assumed but checked (each object is compared to + * every other one). + * + * Note that qobject_is_equal() is not really an equivalence relation, + * so this function may not be used for all objects (reflexivity is + * not guaranteed, e.g. in the case of a QNum containing NaN). + * + * The @_ argument is required because a boolean may not be the last + * argument before a variadic argument list (C11 7.16.1.4 para. 4). + */ +static void do_test_equality(bool expected, int _, ...) +{ + va_list ap_count, ap_extract; + QObject **args; + int arg_count = 0; + int i, j; + + va_start(ap_count, _); + va_copy(ap_extract, ap_count); + while (va_arg(ap_count, QObject *) != &test_equality_end_of_arguments) { + arg_count++; + } + va_end(ap_count); + + args = g_new(QObject *, arg_count); + for (i = 0; i < arg_count; i++) { + args[i] = va_arg(ap_extract, QObject *); + } + va_end(ap_extract); + + for (i = 0; i < arg_count; i++) { + g_assert(qobject_is_equal(args[i], args[i]) == true); + + for (j = i + 1; j < arg_count; j++) { + g_assert(qobject_is_equal(args[i], args[j]) == expected); + } + } + + g_free(args); +} + +#define check_equal(...) \ + do_test_equality(true, 0, __VA_ARGS__, &test_equality_end_of_arguments) +#define check_unequal(...) \ + do_test_equality(false, 0, __VA_ARGS__, &test_equality_end_of_arguments) + +static void do_free_all(int _, ...) +{ + va_list ap; + QObject *obj; + + va_start(ap, _); + while ((obj = va_arg(ap, QObject *)) != NULL) { + qobject_unref(obj); + } + va_end(ap); +} + +#define free_all(...) \ + do_free_all(0, __VA_ARGS__, NULL) + +static void qobject_is_equal_null_test(void) +{ + check_unequal(qnull(), NULL); +} + +static void qobject_is_equal_num_test(void) +{ + QNum *u0, *i0, *d0, *dnan, *um42, *im42, *dm42; + + u0 = qnum_from_uint(0u); + i0 = qnum_from_int(0); + d0 = qnum_from_double(0.0); + dnan = qnum_from_double(NAN); + um42 = qnum_from_uint((uint64_t)-42); + im42 = qnum_from_int(-42); + dm42 = qnum_from_double(-42.0); + + /* Integers representing a mathematically equal number should + * compare equal */ + check_equal(u0, i0); + /* Doubles, however, are always unequal to integers */ + check_unequal(u0, d0); + check_unequal(i0, d0); + + /* Do not assume any object is equal to itself -- note however + * that NaN cannot occur in a JSON object anyway. */ + g_assert(qobject_is_equal(QOBJECT(dnan), QOBJECT(dnan)) == false); + + /* No unsigned overflow */ + check_unequal(um42, im42); + check_unequal(um42, dm42); + check_unequal(im42, dm42); + + free_all(u0, i0, d0, dnan, um42, im42, dm42); +} + +static void qobject_is_equal_bool_test(void) +{ + QBool *btrue_0, *btrue_1, *bfalse_0, *bfalse_1; + + btrue_0 = qbool_from_bool(true); + btrue_1 = qbool_from_bool(true); + bfalse_0 = qbool_from_bool(false); + bfalse_1 = qbool_from_bool(false); + + check_equal(btrue_0, btrue_1); + check_equal(bfalse_0, bfalse_1); + check_unequal(btrue_0, bfalse_0); + + free_all(btrue_0, btrue_1, bfalse_0, bfalse_1); +} + +static void qobject_is_equal_string_test(void) +{ + QString *str_base, *str_whitespace_0, *str_whitespace_1, *str_whitespace_2; + QString *str_whitespace_3, *str_case, *str_built; + + str_base = qstring_from_str("foo"); + str_whitespace_0 = qstring_from_str(" foo"); + str_whitespace_1 = qstring_from_str("foo "); + str_whitespace_2 = qstring_from_str("foo\b"); + str_whitespace_3 = qstring_from_str("fooo\b"); + str_case = qstring_from_str("Foo"); + + /* Should yield "foo" */ + str_built = qstring_from_substr("buffoon", 3, 6); + + check_unequal(str_base, str_whitespace_0, str_whitespace_1, + str_whitespace_2, str_whitespace_3, str_case); + + check_equal(str_base, str_built); + + free_all(str_base, str_whitespace_0, str_whitespace_1, str_whitespace_2, + str_whitespace_3, str_case, str_built); +} + +static void qobject_is_equal_list_test(void) +{ + QList *list_0, *list_1, *list_cloned; + QList *list_reordered, *list_longer, *list_shorter; + + list_0 = qlist_new(); + list_1 = qlist_new(); + list_reordered = qlist_new(); + list_longer = qlist_new(); + list_shorter = qlist_new(); + + qlist_append_int(list_0, 1); + qlist_append_int(list_0, 2); + qlist_append_int(list_0, 3); + + qlist_append_int(list_1, 1); + qlist_append_int(list_1, 2); + qlist_append_int(list_1, 3); + + qlist_append_int(list_reordered, 1); + qlist_append_int(list_reordered, 3); + qlist_append_int(list_reordered, 2); + + qlist_append_int(list_longer, 1); + qlist_append_int(list_longer, 2); + qlist_append_int(list_longer, 3); + qlist_append_null(list_longer); + + qlist_append_int(list_shorter, 1); + qlist_append_int(list_shorter, 2); + + list_cloned = qlist_copy(list_0); + + check_equal(list_0, list_1, list_cloned); + check_unequal(list_0, list_reordered, list_longer, list_shorter); + + /* With a NaN in it, the list should no longer compare equal to + * itself */ + qlist_append(list_0, qnum_from_double(NAN)); + g_assert(qobject_is_equal(QOBJECT(list_0), QOBJECT(list_0)) == false); + + free_all(list_0, list_1, list_cloned, list_reordered, list_longer, + list_shorter); +} + +static void qobject_is_equal_dict_test(void) +{ + QDict *dict_0, *dict_1, *dict_cloned; + QDict *dict_different_key, *dict_different_value, *dict_different_null_key; + QDict *dict_longer, *dict_shorter, *dict_nested; + QDict *dict_crumpled; + + dict_0 = qdict_new(); + dict_1 = qdict_new(); + dict_different_key = qdict_new(); + dict_different_value = qdict_new(); + dict_different_null_key = qdict_new(); + dict_longer = qdict_new(); + dict_shorter = qdict_new(); + dict_nested = qdict_new(); + + qdict_put_int(dict_0, "f.o", 1); + qdict_put_int(dict_0, "bar", 2); + qdict_put_int(dict_0, "baz", 3); + qdict_put_null(dict_0, "null"); + + qdict_put_int(dict_1, "f.o", 1); + qdict_put_int(dict_1, "bar", 2); + qdict_put_int(dict_1, "baz", 3); + qdict_put_null(dict_1, "null"); + + qdict_put_int(dict_different_key, "F.o", 1); + qdict_put_int(dict_different_key, "bar", 2); + qdict_put_int(dict_different_key, "baz", 3); + qdict_put_null(dict_different_key, "null"); + + qdict_put_int(dict_different_value, "f.o", 42); + qdict_put_int(dict_different_value, "bar", 2); + qdict_put_int(dict_different_value, "baz", 3); + qdict_put_null(dict_different_value, "null"); + + qdict_put_int(dict_different_null_key, "f.o", 1); + qdict_put_int(dict_different_null_key, "bar", 2); + qdict_put_int(dict_different_null_key, "baz", 3); + qdict_put_null(dict_different_null_key, "none"); + + qdict_put_int(dict_longer, "f.o", 1); + qdict_put_int(dict_longer, "bar", 2); + qdict_put_int(dict_longer, "baz", 3); + qdict_put_int(dict_longer, "xyz", 4); + qdict_put_null(dict_longer, "null"); + + qdict_put_int(dict_shorter, "f.o", 1); + qdict_put_int(dict_shorter, "bar", 2); + qdict_put_int(dict_shorter, "baz", 3); + + qdict_put(dict_nested, "f", qdict_new()); + qdict_put_int(qdict_get_qdict(dict_nested, "f"), "o", 1); + qdict_put_int(dict_nested, "bar", 2); + qdict_put_int(dict_nested, "baz", 3); + qdict_put_null(dict_nested, "null"); + + dict_cloned = qdict_clone_shallow(dict_0); + + check_equal(dict_0, dict_1, dict_cloned); + check_unequal(dict_0, dict_different_key, dict_different_value, + dict_different_null_key, dict_longer, dict_shorter, + dict_nested); + + dict_crumpled = qobject_to(QDict, qdict_crumple(dict_1, &error_abort)); + check_equal(dict_crumpled, dict_nested); + + qdict_flatten(dict_nested); + check_equal(dict_0, dict_nested); + + /* Containing an NaN value will make this dict compare unequal to + * itself */ + qdict_put(dict_0, "NaN", qnum_from_double(NAN)); + g_assert(qobject_is_equal(QOBJECT(dict_0), QOBJECT(dict_0)) == false); + + free_all(dict_0, dict_1, dict_cloned, dict_different_key, + dict_different_value, dict_different_null_key, dict_longer, + dict_shorter, dict_nested, dict_crumpled); +} + +static void qobject_is_equal_conversion_test(void) +{ + QNum *u0, *i0, *d0; + QString *s0, *s_empty; + QBool *bfalse; + + u0 = qnum_from_uint(0u); + i0 = qnum_from_int(0); + d0 = qnum_from_double(0.0); + s0 = qstring_from_str("0"); + s_empty = qstring_new(); + bfalse = qbool_from_bool(false); + + /* No automatic type conversion */ + check_unequal(u0, s0, s_empty, bfalse, qnull(), NULL); + check_unequal(i0, s0, s_empty, bfalse, qnull(), NULL); + check_unequal(d0, s0, s_empty, bfalse, qnull(), NULL); + + free_all(u0, i0, d0, s0, s_empty, bfalse); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/public/qobject_is_equal_null", + qobject_is_equal_null_test); + g_test_add_func("/public/qobject_is_equal_num", qobject_is_equal_num_test); + g_test_add_func("/public/qobject_is_equal_bool", + qobject_is_equal_bool_test); + g_test_add_func("/public/qobject_is_equal_string", + qobject_is_equal_string_test); + g_test_add_func("/public/qobject_is_equal_list", + qobject_is_equal_list_test); + g_test_add_func("/public/qobject_is_equal_dict", + qobject_is_equal_dict_test); + g_test_add_func("/public/qobject_is_equal_conversion", + qobject_is_equal_conversion_test); + + return g_test_run(); +} diff --git a/tests/unit/check-qom-interface.c b/tests/unit/check-qom-interface.c new file mode 100644 index 0000000000..c99be97ed8 --- /dev/null +++ b/tests/unit/check-qom-interface.c @@ -0,0 +1,103 @@ +/* + * QOM interface test. + * + * Copyright (C) 2013 Red Hat Inc. + * + * Authors: + * Igor Mammedov + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + */ +#include "qemu/osdep.h" + +#include "qom/object.h" +#include "qemu/module.h" + + +#define TYPE_TEST_IF "test-interface" +typedef struct TestIfClass TestIfClass; +DECLARE_CLASS_CHECKERS(TestIfClass, TEST_IF, + TYPE_TEST_IF) +#define TEST_IF(obj) \ + INTERFACE_CHECK(TestIf, (obj), TYPE_TEST_IF) + +typedef struct TestIf TestIf; + +struct TestIfClass { + InterfaceClass parent_class; + + uint32_t test; +}; + +static const TypeInfo test_if_info = { + .name = TYPE_TEST_IF, + .parent = TYPE_INTERFACE, + .class_size = sizeof(TestIfClass), +}; + +#define PATTERN 0xFAFBFCFD + +static void test_class_init(ObjectClass *oc, void *data) +{ + TestIfClass *tc = TEST_IF_CLASS(oc); + + g_assert(tc); + tc->test = PATTERN; +} + +#define TYPE_DIRECT_IMPL "direct-impl" + +static const TypeInfo direct_impl_info = { + .name = TYPE_DIRECT_IMPL, + .parent = TYPE_OBJECT, + .class_init = test_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_TEST_IF }, + { } + } +}; + +#define TYPE_INTERMEDIATE_IMPL "intermediate-impl" + +static const TypeInfo intermediate_impl_info = { + .name = TYPE_INTERMEDIATE_IMPL, + .parent = TYPE_DIRECT_IMPL, +}; + +static void test_interface_impl(const char *type) +{ + Object *obj = object_new(type); + TestIf *iobj = TEST_IF(obj); + TestIfClass *ioc = TEST_IF_GET_CLASS(iobj); + + g_assert(iobj); + g_assert(ioc->test == PATTERN); + object_unref(obj); +} + +static void interface_direct_test(void) +{ + test_interface_impl(TYPE_DIRECT_IMPL); +} + +static void interface_intermediate_test(void) +{ + test_interface_impl(TYPE_INTERMEDIATE_IMPL); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + module_call_init(MODULE_INIT_QOM); + type_register_static(&test_if_info); + type_register_static(&direct_impl_info); + type_register_static(&intermediate_impl_info); + + g_test_add_func("/qom/interface/direct_impl", interface_direct_test); + g_test_add_func("/qom/interface/intermediate_impl", + interface_intermediate_test); + + return g_test_run(); +} diff --git a/tests/unit/check-qom-proplist.c b/tests/unit/check-qom-proplist.c new file mode 100644 index 0000000000..1b76581980 --- /dev/null +++ b/tests/unit/check-qom-proplist.c @@ -0,0 +1,638 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + * + * Author: Daniel P. Berrange + */ + +#include "qemu/osdep.h" + +#include "qapi/error.h" +#include "qom/object.h" +#include "qemu/module.h" +#include "qemu/option.h" +#include "qemu/config-file.h" +#include "qom/object_interfaces.h" + + +#define TYPE_DUMMY "qemu-dummy" + +typedef struct DummyObject DummyObject; +typedef struct DummyObjectClass DummyObjectClass; + +DECLARE_INSTANCE_CHECKER(DummyObject, DUMMY_OBJECT, + TYPE_DUMMY) + +typedef enum DummyAnimal DummyAnimal; + +enum DummyAnimal { + DUMMY_FROG, + DUMMY_ALLIGATOR, + DUMMY_PLATYPUS, + + DUMMY_LAST, +}; + +const QEnumLookup dummy_animal_map = { + .array = (const char *const[]) { + [DUMMY_FROG] = "frog", + [DUMMY_ALLIGATOR] = "alligator", + [DUMMY_PLATYPUS] = "platypus", + }, + .size = DUMMY_LAST +}; + +struct DummyObject { + Object parent_obj; + + bool bv; + DummyAnimal av; + char *sv; +}; + +struct DummyObjectClass { + ObjectClass parent_class; +}; + + +static void dummy_set_bv(Object *obj, + bool value, + Error **errp) +{ + DummyObject *dobj = DUMMY_OBJECT(obj); + + dobj->bv = value; +} + +static bool dummy_get_bv(Object *obj, + Error **errp) +{ + DummyObject *dobj = DUMMY_OBJECT(obj); + + return dobj->bv; +} + + +static void dummy_set_av(Object *obj, + int value, + Error **errp) +{ + DummyObject *dobj = DUMMY_OBJECT(obj); + + dobj->av = value; +} + +static int dummy_get_av(Object *obj, + Error **errp) +{ + DummyObject *dobj = DUMMY_OBJECT(obj); + + return dobj->av; +} + + +static void dummy_set_sv(Object *obj, + const char *value, + Error **errp) +{ + DummyObject *dobj = DUMMY_OBJECT(obj); + + g_free(dobj->sv); + dobj->sv = g_strdup(value); +} + +static char *dummy_get_sv(Object *obj, + Error **errp) +{ + DummyObject *dobj = DUMMY_OBJECT(obj); + + return g_strdup(dobj->sv); +} + + +static void dummy_init(Object *obj) +{ + object_property_add_bool(obj, "bv", + dummy_get_bv, + dummy_set_bv); +} + + +static void dummy_class_init(ObjectClass *cls, void *data) +{ + object_class_property_add_str(cls, "sv", + dummy_get_sv, + dummy_set_sv); + object_class_property_add_enum(cls, "av", + "DummyAnimal", + &dummy_animal_map, + dummy_get_av, + dummy_set_av); +} + + +static void dummy_finalize(Object *obj) +{ + DummyObject *dobj = DUMMY_OBJECT(obj); + + g_free(dobj->sv); +} + + +static const TypeInfo dummy_info = { + .name = TYPE_DUMMY, + .parent = TYPE_OBJECT, + .instance_size = sizeof(DummyObject), + .instance_init = dummy_init, + .instance_finalize = dummy_finalize, + .class_size = sizeof(DummyObjectClass), + .class_init = dummy_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_USER_CREATABLE }, + { } + } +}; + + +/* + * The following 3 object classes are used to + * simulate the kind of relationships seen in + * qdev, which result in complex object + * property destruction ordering. + * + * DummyDev has a 'bus' child to a DummyBus + * DummyBus has a 'backend' child to a DummyBackend + * DummyDev has a 'backend' link to DummyBackend + * + * When DummyDev is finalized, it unparents the + * DummyBackend, which unparents the DummyDev + * which deletes the 'backend' link from DummyDev + * to DummyBackend. This illustrates that the + * object_property_del_all() method needs to + * cope with the list of properties being changed + * while it iterates over them. + */ +typedef struct DummyDev DummyDev; +typedef struct DummyDevClass DummyDevClass; +typedef struct DummyBus DummyBus; +typedef struct DummyBusClass DummyBusClass; +typedef struct DummyBackend DummyBackend; +typedef struct DummyBackendClass DummyBackendClass; + +#define TYPE_DUMMY_DEV "qemu-dummy-dev" +#define TYPE_DUMMY_BUS "qemu-dummy-bus" +#define TYPE_DUMMY_BACKEND "qemu-dummy-backend" + +DECLARE_INSTANCE_CHECKER(DummyDev, DUMMY_DEV, + TYPE_DUMMY_DEV) +DECLARE_INSTANCE_CHECKER(DummyBus, DUMMY_BUS, + TYPE_DUMMY_BUS) +DECLARE_INSTANCE_CHECKER(DummyBackend, DUMMY_BACKEND, + TYPE_DUMMY_BACKEND) + +struct DummyDev { + Object parent_obj; + + DummyBus *bus; +}; + +struct DummyDevClass { + ObjectClass parent_class; +}; + +struct DummyBus { + Object parent_obj; + + DummyBackend *backend; +}; + +struct DummyBusClass { + ObjectClass parent_class; +}; + +struct DummyBackend { + Object parent_obj; +}; + +struct DummyBackendClass { + ObjectClass parent_class; +}; + + +static void dummy_dev_finalize(Object *obj) +{ + DummyDev *dev = DUMMY_DEV(obj); + + object_unref(OBJECT(dev->bus)); +} + +static void dummy_dev_init(Object *obj) +{ + DummyDev *dev = DUMMY_DEV(obj); + DummyBus *bus = DUMMY_BUS(object_new(TYPE_DUMMY_BUS)); + DummyBackend *backend = DUMMY_BACKEND(object_new(TYPE_DUMMY_BACKEND)); + + object_property_add_child(obj, "bus", OBJECT(bus)); + dev->bus = bus; + object_property_add_child(OBJECT(bus), "backend", OBJECT(backend)); + bus->backend = backend; + + object_property_add_link(obj, "backend", TYPE_DUMMY_BACKEND, + (Object **)&bus->backend, NULL, 0); +} + +static void dummy_dev_unparent(Object *obj) +{ + DummyDev *dev = DUMMY_DEV(obj); + object_unparent(OBJECT(dev->bus)); +} + +static void dummy_dev_class_init(ObjectClass *klass, void *opaque) +{ + klass->unparent = dummy_dev_unparent; +} + + +static void dummy_bus_finalize(Object *obj) +{ + DummyBus *bus = DUMMY_BUS(obj); + + object_unref(OBJECT(bus->backend)); +} + +static void dummy_bus_init(Object *obj) +{ +} + +static void dummy_bus_unparent(Object *obj) +{ + DummyBus *bus = DUMMY_BUS(obj); + object_property_del(obj->parent, "backend"); + object_unparent(OBJECT(bus->backend)); +} + +static void dummy_bus_class_init(ObjectClass *klass, void *opaque) +{ + klass->unparent = dummy_bus_unparent; +} + +static void dummy_backend_init(Object *obj) +{ +} + + +static const TypeInfo dummy_dev_info = { + .name = TYPE_DUMMY_DEV, + .parent = TYPE_OBJECT, + .instance_size = sizeof(DummyDev), + .instance_init = dummy_dev_init, + .instance_finalize = dummy_dev_finalize, + .class_size = sizeof(DummyDevClass), + .class_init = dummy_dev_class_init, +}; + +static const TypeInfo dummy_bus_info = { + .name = TYPE_DUMMY_BUS, + .parent = TYPE_OBJECT, + .instance_size = sizeof(DummyBus), + .instance_init = dummy_bus_init, + .instance_finalize = dummy_bus_finalize, + .class_size = sizeof(DummyBusClass), + .class_init = dummy_bus_class_init, +}; + +static const TypeInfo dummy_backend_info = { + .name = TYPE_DUMMY_BACKEND, + .parent = TYPE_OBJECT, + .instance_size = sizeof(DummyBackend), + .instance_init = dummy_backend_init, + .class_size = sizeof(DummyBackendClass), +}; + +static QemuOptsList qemu_object_opts = { + .name = "object", + .implied_opt_name = "qom-type", + .head = QTAILQ_HEAD_INITIALIZER(qemu_object_opts.head), + .desc = { + { } + }, +}; + + +static void test_dummy_createv(void) +{ + Error *err = NULL; + Object *parent = object_get_objects_root(); + DummyObject *dobj = DUMMY_OBJECT( + object_new_with_props(TYPE_DUMMY, + parent, + "dummy0", + &err, + "bv", "yes", + "sv", "Hiss hiss hiss", + "av", "platypus", + NULL)); + + g_assert(err == NULL); + g_assert_cmpstr(dobj->sv, ==, "Hiss hiss hiss"); + g_assert(dobj->bv == true); + g_assert(dobj->av == DUMMY_PLATYPUS); + + g_assert(object_resolve_path_component(parent, "dummy0") + == OBJECT(dobj)); + + object_unparent(OBJECT(dobj)); +} + + +static Object *new_helper(Error **errp, + Object *parent, + ...) +{ + va_list vargs; + Object *obj; + + va_start(vargs, parent); + obj = object_new_with_propv(TYPE_DUMMY, + parent, + "dummy0", + errp, + vargs); + va_end(vargs); + return obj; +} + +static void test_dummy_createlist(void) +{ + Error *err = NULL; + Object *parent = object_get_objects_root(); + DummyObject *dobj = DUMMY_OBJECT( + new_helper(&err, + parent, + "bv", "yes", + "sv", "Hiss hiss hiss", + "av", "platypus", + NULL)); + + g_assert(err == NULL); + g_assert_cmpstr(dobj->sv, ==, "Hiss hiss hiss"); + g_assert(dobj->bv == true); + g_assert(dobj->av == DUMMY_PLATYPUS); + + g_assert(object_resolve_path_component(parent, "dummy0") + == OBJECT(dobj)); + + object_unparent(OBJECT(dobj)); +} + +static void test_dummy_createcmdl(void) +{ + QemuOpts *opts; + DummyObject *dobj; + Error *err = NULL; + const char *params = TYPE_DUMMY \ + ",id=dev0," \ + "bv=yes,sv=Hiss hiss hiss,av=platypus"; + + qemu_add_opts(&qemu_object_opts); + opts = qemu_opts_parse(&qemu_object_opts, params, true, &err); + g_assert(err == NULL); + g_assert(opts); + + dobj = DUMMY_OBJECT(user_creatable_add_opts(opts, &err)); + g_assert(err == NULL); + g_assert(dobj); + g_assert_cmpstr(dobj->sv, ==, "Hiss hiss hiss"); + g_assert(dobj->bv == true); + g_assert(dobj->av == DUMMY_PLATYPUS); + + user_creatable_del("dev0", &error_abort); + + object_unref(OBJECT(dobj)); + + /* + * cmdline-parsing via qemu_opts_parse() results in a QemuOpts entry + * corresponding to the Object's ID to be added to the QemuOptsList + * for objects. To avoid having this entry conflict with future + * Objects using the same ID (which can happen in cases where + * qemu_opts_parse() is used to parse the object params, such as + * with hmp_object_add() at the time of this comment), we need to + * check for this in user_creatable_del() and remove the QemuOpts if + * it is present. + * + * The below check ensures this works as expected. + */ + g_assert_null(qemu_opts_find(&qemu_object_opts, "dev0")); +} + +static void test_dummy_badenum(void) +{ + Error *err = NULL; + Object *parent = object_get_objects_root(); + Object *dobj = + object_new_with_props(TYPE_DUMMY, + parent, + "dummy0", + &err, + "bv", "yes", + "sv", "Hiss hiss hiss", + "av", "yeti", + NULL); + + g_assert(dobj == NULL); + g_assert(err != NULL); + g_assert_cmpstr(error_get_pretty(err), ==, + "Invalid parameter 'yeti'"); + + g_assert(object_resolve_path_component(parent, "dummy0") + == NULL); + + error_free(err); +} + + +static void test_dummy_getenum(void) +{ + Error *err = NULL; + int val; + Object *parent = object_get_objects_root(); + DummyObject *dobj = DUMMY_OBJECT( + object_new_with_props(TYPE_DUMMY, + parent, + "dummy0", + &err, + "av", "platypus", + NULL)); + + g_assert(err == NULL); + g_assert(dobj->av == DUMMY_PLATYPUS); + + val = object_property_get_enum(OBJECT(dobj), + "av", + "DummyAnimal", + &error_abort); + g_assert(val == DUMMY_PLATYPUS); + + /* A bad enum type name */ + val = object_property_get_enum(OBJECT(dobj), + "av", + "BadAnimal", + &err); + g_assert(val == -1); + error_free_or_abort(&err); + + /* A non-enum property name */ + val = object_property_get_enum(OBJECT(dobj), + "iv", + "DummyAnimal", + &err); + g_assert(val == -1); + error_free_or_abort(&err); + + object_unparent(OBJECT(dobj)); +} + + +static void test_dummy_prop_iterator(ObjectPropertyIterator *iter, + const char *expected[], int n) +{ + ObjectProperty *prop; + int i; + + while ((prop = object_property_iter_next(iter))) { + for (i = 0; i < n; i++) { + if (!g_strcmp0(prop->name, expected[i])) { + break; + } + } + g_assert(i < n); + expected[i] = NULL; + } + + for (i = 0; i < n; i++) { + g_assert(!expected[i]); + } +} + +static void test_dummy_iterator(void) +{ + const char *expected[] = { + "type", /* inherited from TYPE_OBJECT */ + "sv", "av", /* class properties */ + "bv"}; /* instance property */ + Object *parent = object_get_objects_root(); + DummyObject *dobj = DUMMY_OBJECT( + object_new_with_props(TYPE_DUMMY, + parent, + "dummy0", + &error_abort, + "bv", "yes", + "sv", "Hiss hiss hiss", + "av", "platypus", + NULL)); + ObjectPropertyIterator iter; + + object_property_iter_init(&iter, OBJECT(dobj)); + test_dummy_prop_iterator(&iter, expected, ARRAY_SIZE(expected)); + object_unparent(OBJECT(dobj)); +} + +static void test_dummy_class_iterator(void) +{ + const char *expected[] = { "type", "av", "sv" }; + ObjectPropertyIterator iter; + ObjectClass *klass = object_class_by_name(TYPE_DUMMY); + + object_class_property_iter_init(&iter, klass); + test_dummy_prop_iterator(&iter, expected, ARRAY_SIZE(expected)); +} + +static void test_dummy_delchild(void) +{ + Object *parent = object_get_objects_root(); + DummyDev *dev = DUMMY_DEV( + object_new_with_props(TYPE_DUMMY_DEV, + parent, + "dev0", + &error_abort, + NULL)); + + object_unparent(OBJECT(dev)); +} + +static void test_qom_partial_path(void) +{ + Object *root = object_get_objects_root(); + Object *cont1 = container_get(root, "/cont1"); + Object *obj1 = object_new(TYPE_DUMMY); + Object *obj2a = object_new(TYPE_DUMMY); + Object *obj2b = object_new(TYPE_DUMMY); + bool ambiguous; + + /* Objects created: + * /cont1 + * /cont1/obj1 + * /cont1/obj2 (obj2a) + * /obj2 (obj2b) + */ + object_property_add_child(cont1, "obj1", obj1); + object_unref(obj1); + object_property_add_child(cont1, "obj2", obj2a); + object_unref(obj2a); + object_property_add_child(root, "obj2", obj2b); + object_unref(obj2b); + + ambiguous = false; + g_assert(!object_resolve_path_type("", TYPE_DUMMY, &ambiguous)); + g_assert(ambiguous); + g_assert(!object_resolve_path_type("", TYPE_DUMMY, NULL)); + + ambiguous = false; + g_assert(!object_resolve_path("obj2", &ambiguous)); + g_assert(ambiguous); + g_assert(!object_resolve_path("obj2", NULL)); + + ambiguous = false; + g_assert(object_resolve_path("obj1", &ambiguous) == obj1); + g_assert(!ambiguous); + g_assert(object_resolve_path("obj1", NULL) == obj1); + + object_unparent(obj2b); + object_unparent(cont1); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + module_call_init(MODULE_INIT_QOM); + type_register_static(&dummy_info); + type_register_static(&dummy_dev_info); + type_register_static(&dummy_bus_info); + type_register_static(&dummy_backend_info); + + g_test_add_func("/qom/proplist/createlist", test_dummy_createlist); + g_test_add_func("/qom/proplist/createv", test_dummy_createv); + g_test_add_func("/qom/proplist/createcmdline", test_dummy_createcmdl); + g_test_add_func("/qom/proplist/badenum", test_dummy_badenum); + g_test_add_func("/qom/proplist/getenum", test_dummy_getenum); + g_test_add_func("/qom/proplist/iterator", test_dummy_iterator); + g_test_add_func("/qom/proplist/class_iterator", test_dummy_class_iterator); + g_test_add_func("/qom/proplist/delchild", test_dummy_delchild); + g_test_add_func("/qom/resolve/partial", test_qom_partial_path); + + return g_test_run(); +} diff --git a/tests/unit/check-qstring.c b/tests/unit/check-qstring.c new file mode 100644 index 0000000000..4bf9772093 --- /dev/null +++ b/tests/unit/check-qstring.c @@ -0,0 +1,82 @@ +/* + * QString unit-tests. + * + * Copyright (C) 2009 Red Hat Inc. + * + * Authors: + * Luiz Capitulino + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + */ +#include "qemu/osdep.h" + +#include "qapi/qmp/qstring.h" +#include "qemu-common.h" + +/* + * Public Interface test-cases + * + * (with some violations to access 'private' data) + */ + +static void qstring_from_str_test(void) +{ + QString *qstring; + const char *str = "QEMU"; + + qstring = qstring_from_str(str); + g_assert(qstring != NULL); + g_assert(qstring->base.refcnt == 1); + g_assert(strcmp(str, qstring->string) == 0); + g_assert(qobject_type(QOBJECT(qstring)) == QTYPE_QSTRING); + + qobject_unref(qstring); +} + +static void qstring_get_str_test(void) +{ + QString *qstring; + const char *ret_str; + const char *str = "QEMU/KVM"; + + qstring = qstring_from_str(str); + ret_str = qstring_get_str(qstring); + g_assert(strcmp(ret_str, str) == 0); + + qobject_unref(qstring); +} + +static void qstring_from_substr_test(void) +{ + QString *qs; + + qs = qstring_from_substr("virtualization", 3, 10); + g_assert(qs != NULL); + g_assert(strcmp(qstring_get_str(qs), "tualiza") == 0); + + qobject_unref(qs); +} + + +static void qobject_to_qstring_test(void) +{ + QString *qstring; + + qstring = qstring_from_str("foo"); + g_assert(qobject_to(QString, QOBJECT(qstring)) == qstring); + + qobject_unref(qstring); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/public/from_str", qstring_from_str_test); + g_test_add_func("/public/get_str", qstring_get_str_test); + g_test_add_func("/public/from_substr", qstring_from_substr_test); + g_test_add_func("/public/to_qstring", qobject_to_qstring_test); + + return g_test_run(); +} diff --git a/tests/unit/crypto-tls-psk-helpers.c b/tests/unit/crypto-tls-psk-helpers.c new file mode 100644 index 0000000000..a8395477c3 --- /dev/null +++ b/tests/unit/crypto-tls-psk-helpers.c @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2015-2018 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + * + * Author: Richard W.M. Jones + */ + +#include "qemu/osdep.h" + +/* Include this first because it defines QCRYPTO_HAVE_TLS_TEST_SUPPORT */ +#include "crypto-tls-x509-helpers.h" + +#include "crypto-tls-psk-helpers.h" +#include "qemu/sockets.h" + +#ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT + +void test_tls_psk_init(const char *pskfile) +{ + FILE *fp; + + fp = fopen(pskfile, "w"); + if (fp == NULL) { + g_critical("Failed to create pskfile %s", pskfile); + abort(); + } + /* Don't do this in real applications! Use psktool. */ + fprintf(fp, "qemu:009d5638c40fde0c\n"); + fclose(fp); +} + +void test_tls_psk_cleanup(const char *pskfile) +{ + unlink(pskfile); +} + +#endif /* QCRYPTO_HAVE_TLS_TEST_SUPPORT */ diff --git a/tests/unit/crypto-tls-psk-helpers.h b/tests/unit/crypto-tls-psk-helpers.h new file mode 100644 index 0000000000..5aa9951cb6 --- /dev/null +++ b/tests/unit/crypto-tls-psk-helpers.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2015-2018 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + * + * Author: Richard W.M. Jones + */ + +#ifndef TESTS_CRYPTO_TLS_PSK_HELPERS_H +#define TESTS_CRYPTO_TLS_PSK_HELPERS_H + +#include + +#ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT + +void test_tls_psk_init(const char *keyfile); +void test_tls_psk_cleanup(const char *keyfile); + +#endif /* QCRYPTO_HAVE_TLS_TEST_SUPPORT */ + +#endif diff --git a/tests/unit/crypto-tls-x509-helpers.c b/tests/unit/crypto-tls-x509-helpers.c new file mode 100644 index 0000000000..97658592a2 --- /dev/null +++ b/tests/unit/crypto-tls-x509-helpers.c @@ -0,0 +1,508 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + * + * Author: Daniel P. Berrange + */ + +#include "qemu/osdep.h" + +#include "crypto-tls-x509-helpers.h" +#include "crypto/init.h" +#include "qemu/sockets.h" + +#ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT + +/* + * This stores some static data that is needed when + * encoding extensions in the x509 certs + */ +asn1_node pkix_asn1; + +/* + * To avoid consuming random entropy to generate keys, + * here's one we prepared earlier :-) + */ +gnutls_x509_privkey_t privkey; +# define PRIVATE_KEY \ + "-----BEGIN RSA PRIVATE KEY-----\n" \ + "MIIG5AIBAAKCAYEAyjWyLSNm5PZvYUKUcDWGqbLX10b2ood+YaFjWSnJrqx/q3qh\n" \ + "rVGBJglD25AJENJsmZF3zPP1oMhfIxsXu63Hdkb6Rdlc2RUoUP34x9VC1izH25mR\n" \ + "6c8DPDp1d6IraZ/llDMI1HsBFz0qGWtvOHgm815XG4PAr/N8rDsuqfv/cJ01KlnO\n" \ + "0OdO5QRXCJf9g/dYd41MPu7wOXk9FqjQlmRoP59HgtJ+zUpE4z+Keruw9cMT9VJj\n" \ + "0oT+pQ9ysenqeZ3gbT224T1khrEhT5kifhtFLNyDssRchUUWH0hiqoOO1vgb+850\n" \ + "W6/1VdxvuPam48py4diSPi1Vip8NITCOBaX9FIpVp4Ruw4rTPVMNMjq9Cpx/DwMP\n" \ + "9MbfXfnaVaZaMrmq67/zPhl0eVbUrecH2hQ3ZB9oIF4GkNskzlWF5+yPy6zqk304\n" \ + "AKaiFR6jRyh3YfHo2XFqV8x/hxdsIEXOtEUGhSIcpynsW+ckUCartzu7xbhXjd4b\n" \ + "kxJT89+riPFYij09AgMBAAECggGBAKyFkaZXXROeejrmHlV6JZGlp+fhgM38gkRz\n" \ + "+Jp7P7rLLAY3E7gXIPQ91WqAAmwazFNdvHPd9USfkCQYmnAi/VoZhrCPmlsQZRxt\n" \ + "A5QjjOnEvSPMa6SrXZxGWDCg6R8uMCb4P+FhrPWR1thnRDZOtRTQ+crc50p3mHgt\n" \ + "6ktXWIJRbqnag8zSfQqCYGtRmhe8sfsWT+Yl4El4+jjaAVU/B364u7+PLmaiphGp\n" \ + "BdJfTsTwEpgtGkPj+osDmhzXcZkfq3V+fz5JLkemsCiQKmn4VJRpg8c3ZmE8NPNt\n" \ + "gRtGWZ4W3WKDvhotT65WpQx4+6R8Duux/blNPBmH1Upmwd7kj7GYFBArbCjgd9PT\n" \ + "xgfCSUZpgOZHHkcgSB+022a8XncXna7WYYij28SLtwImFyu0nNtqECFQHH5u+k6C\n" \ + "LRYBSN+3t3At8dQuk01NVrJBndmjmXRfxpqUtTdeaNgVpdUYRY98s30G68NYGSra\n" \ + "aEvhhRSghkcLNetkobpY9pUgeqW/tQKBwQDZHHK9nDMt/zk1TxtILeUSitPXcv1/\n" \ + "8ufXqO0miHdH23XuXhIEA6Ef26RRVGDGgpjkveDJK/1w5feJ4H/ni4Vclil/cm38\n" \ + "OwRqjjd7ElHJX6JQbsxEx/gNTk5/QW1iAL9TXUalgepsSXYT6AJ0/CJv0jmJSJ36\n" \ + "YoKMOM8uqzb2KhN6i+RlJRi5iY53kUhWTJq5ArWvNhUzQNSYODI4bNxlsKSBL2Ik\n" \ + "LZ5QKHuaEjQet0IlPlfIb4PzMm8CHa/urOcCgcEA7m3zW/lL5bIFoKPjWig5Lbn1\n" \ + "aHfrG2ngqzWtgWtfZqMH8OkZc1Mdhhmvd46titjiLjeI+UP/uHXR0068PnrNngzl\n" \ + "tTgwlakzu+bWzqhBm1F+3/341st/FEk07r0P/3/PhezVjwfO8c8Exj7pLxH4wrH0\n" \ + "ROHgDbClmlJRu6OO78wk1+Vapf5DWa8YfA+q+fdvr7KvgGyytheKMT/b/dsqOq7y\n" \ + "qZPjmaJKWAvV3RWG8lWHFSdHx2IAHMHfGr17Y/w7AoHBALzwZeYebeekiVucGSjq\n" \ + "T8SgLhT7zCIx+JMUPjVfYzaUhP/Iu7Lkma6IzWm9nW6Drpy5pUpMzwUWDCLfzU9q\n" \ + "eseFIl337kEn9wLn+t5OpgAyCqYmlftxbqvdrrBN9uvnrJjWvqk/8wsDrw9JxAGc\n" \ + "fjeD4nBXUqvYWLXApoR9mZoGKedmoH9pFig4zlO9ig8YITnKYuQ0k6SD0b8agJHc\n" \ + "Ir0YSUDnRGgpjvFBGbeOCe+FGbohk/EpItJc3IAh5740lwKBwAdXd2DjokSmYKn7\n" \ + "oeqKxofz6+yVlLW5YuOiuX78sWlVp87xPolgi84vSEnkKM/Xsc8+goc6YstpRVa+\n" \ + "W+mImoA9YW1dF5HkLeWhTAf9AlgoAEIhbeIfTgBv6KNZSv7RDrDPBBxtXx/vAfSg\n" \ + "x0ldwk0scZsVYXLKd67yzfV7KdGUdaX4N/xYgfZm/9gCG3+q8NN2KxVHQ5F71BOE\n" \ + "JeABOaGo9WvnU+DNMIDZjHJMUWVw4MHz/a/UArDf/2CxaPVBNQKBwASg6j4ohSTk\n" \ + "J7aE6RQ3OBmmDDpixcoCJt9u9SjHVYMlbs5CEJGVSczk0SG3y8P1lOWNDSRnMksZ\n" \ + "xWnHdP/ogcuYMuvK7UACNAF0zNddtzOhzcpNmejFj+WCHYY/UmPr2/Kf6t7Cxk2K\n" \ + "3cZ4tqWsiTmBT8Bknmah7L5DrhS+ZBJliDeFAA8fZHdMH0Xjr4UBp9kF90EMTdW1\n" \ + "Xr5uz7ZrMsYpYQI7mmyqV9SSjUg4iBXwVSoag1iDJ1K8Qg/L7Semgg==\n" \ + "-----END RSA PRIVATE KEY-----\n" + +/* + * This loads the private key we defined earlier + */ +static gnutls_x509_privkey_t test_tls_load_key(void) +{ + gnutls_x509_privkey_t key; + const gnutls_datum_t data = { (unsigned char *)PRIVATE_KEY, + strlen(PRIVATE_KEY) }; + int err; + + err = gnutls_x509_privkey_init(&key); + if (err < 0) { + g_critical("Failed to init key %s", gnutls_strerror(err)); + abort(); + } + + err = gnutls_x509_privkey_import(key, &data, + GNUTLS_X509_FMT_PEM); + if (err < 0) { + if (err != GNUTLS_E_BASE64_UNEXPECTED_HEADER_ERROR && + err != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + g_critical("Failed to import key %s", gnutls_strerror(err)); + abort(); + } + + err = gnutls_x509_privkey_import_pkcs8( + key, &data, GNUTLS_X509_FMT_PEM, NULL, 0); + if (err < 0) { + g_critical("Failed to import PKCS8 key %s", gnutls_strerror(err)); + abort(); + } + } + + return key; +} + + +void test_tls_init(const char *keyfile) +{ + qcrypto_init(&error_abort); + + if (asn1_array2tree(pkix_asn1_tab, &pkix_asn1, NULL) != ASN1_SUCCESS) { + abort(); + } + + privkey = test_tls_load_key(); + if (!g_file_set_contents(keyfile, PRIVATE_KEY, -1, NULL)) { + abort(); + } +} + + +void test_tls_cleanup(const char *keyfile) +{ + asn1_delete_structure(&pkix_asn1); + unlink(keyfile); +} + +/* + * Turns an ASN1 object into a DER encoded byte array + */ +static void test_tls_der_encode(asn1_node src, + const char *src_name, + gnutls_datum_t *res) +{ + int size; + char *data = NULL; + + size = 0; + asn1_der_coding(src, src_name, NULL, &size, NULL); + + data = g_new0(char, size); + + asn1_der_coding(src, src_name, data, &size, NULL); + + res->data = (unsigned char *)data; + res->size = size; +} + + +static void +test_tls_get_ipaddr(const char *addrstr, + char **data, + int *datalen) +{ + struct addrinfo *res; + struct addrinfo hints; + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_NUMERICHOST; + g_assert(getaddrinfo(addrstr, NULL, &hints, &res) == 0); + + *datalen = res->ai_addrlen; + *data = g_new(char, *datalen); + memcpy(*data, res->ai_addr, *datalen); + freeaddrinfo(res); +} + +/* + * This is a fairly lame x509 certificate generator. + * + * Do not copy/use this code for generating real certificates + * since it leaves out many things that you would want in + * certificates for real world usage. + * + * This is good enough only for doing tests of the QEMU + * TLS certificate code + */ +void +test_tls_generate_cert(QCryptoTLSTestCertReq *req, + gnutls_x509_crt_t ca) +{ + gnutls_x509_crt_t crt; + int err; + static char buffer[1024 * 1024]; + size_t size = sizeof(buffer); + char serial[5] = { 1, 2, 3, 4, 0 }; + gnutls_datum_t der; + time_t start = time(NULL) + (60 * 60 * req->start_offset); + time_t expire = time(NULL) + (60 * 60 * (req->expire_offset + ? req->expire_offset : 24)); + + /* + * Prepare our new certificate object + */ + err = gnutls_x509_crt_init(&crt); + if (err < 0) { + g_critical("Failed to initialize certificate %s", gnutls_strerror(err)); + abort(); + } + err = gnutls_x509_crt_set_key(crt, privkey); + if (err < 0) { + g_critical("Failed to set certificate key %s", gnutls_strerror(err)); + abort(); + } + + /* + * A v3 certificate is required in order to be able + * set any of the basic constraints, key purpose and + * key usage data + */ + gnutls_x509_crt_set_version(crt, 3); + + if (req->country) { + err = gnutls_x509_crt_set_dn_by_oid( + crt, GNUTLS_OID_X520_COUNTRY_NAME, 0, + req->country, strlen(req->country)); + if (err < 0) { + g_critical("Failed to set certificate country name %s", + gnutls_strerror(err)); + abort(); + } + } + if (req->cn) { + err = gnutls_x509_crt_set_dn_by_oid( + crt, GNUTLS_OID_X520_COMMON_NAME, 0, + req->cn, strlen(req->cn)); + if (err < 0) { + g_critical("Failed to set certificate common name %s", + gnutls_strerror(err)); + abort(); + } + } + + /* + * Setup the subject altnames, which are used + * for hostname checks in live sessions + */ + if (req->altname1) { + err = gnutls_x509_crt_set_subject_alt_name( + crt, GNUTLS_SAN_DNSNAME, + req->altname1, + strlen(req->altname1), + GNUTLS_FSAN_APPEND); + if (err < 0) { + g_critical("Failed to set certificate alt name %s", + gnutls_strerror(err)); + abort(); + } + } + if (req->altname2) { + err = gnutls_x509_crt_set_subject_alt_name( + crt, GNUTLS_SAN_DNSNAME, + req->altname2, + strlen(req->altname2), + GNUTLS_FSAN_APPEND); + if (err < 0) { + g_critical("Failed to set certificate %s alt name", + gnutls_strerror(err)); + abort(); + } + } + + /* + * IP address need to be put into the cert in their + * raw byte form, not strings, hence this is a little + * more complicated + */ + if (req->ipaddr1) { + char *data; + int len; + + test_tls_get_ipaddr(req->ipaddr1, &data, &len); + + err = gnutls_x509_crt_set_subject_alt_name( + crt, GNUTLS_SAN_IPADDRESS, + data, len, GNUTLS_FSAN_APPEND); + if (err < 0) { + g_critical("Failed to set certificate alt name %s", + gnutls_strerror(err)); + abort(); + } + g_free(data); + } + if (req->ipaddr2) { + char *data; + int len; + + test_tls_get_ipaddr(req->ipaddr2, &data, &len); + + err = gnutls_x509_crt_set_subject_alt_name( + crt, GNUTLS_SAN_IPADDRESS, + data, len, GNUTLS_FSAN_APPEND); + if (err < 0) { + g_critical("Failed to set certificate alt name %s", + gnutls_strerror(err)); + abort(); + } + g_free(data); + } + + + /* + * Basic constraints are used to decide if the cert + * is for a CA or not. We can't use the convenient + * gnutls API for setting this, since it hardcodes + * the 'critical' field which we want control over + */ + if (req->basicConstraintsEnable) { + asn1_node ext = NULL; + + asn1_create_element(pkix_asn1, "PKIX1.BasicConstraints", &ext); + asn1_write_value(ext, "cA", + req->basicConstraintsIsCA ? "TRUE" : "FALSE", 1); + asn1_write_value(ext, "pathLenConstraint", NULL, 0); + test_tls_der_encode(ext, "", &der); + err = gnutls_x509_crt_set_extension_by_oid( + crt, "2.5.29.19", + der.data, der.size, + req->basicConstraintsCritical); + if (err < 0) { + g_critical("Failed to set certificate basic constraints %s", + gnutls_strerror(err)); + g_free(der.data); + abort(); + } + asn1_delete_structure(&ext); + g_free(der.data); + } + + /* + * Next up the key usage extension. Again we can't + * use the gnutls API since it hardcodes the extension + * to be 'critical' + */ + if (req->keyUsageEnable) { + asn1_node ext = NULL; + char str[2]; + + str[0] = req->keyUsageValue & 0xff; + str[1] = (req->keyUsageValue >> 8) & 0xff; + + asn1_create_element(pkix_asn1, "PKIX1.KeyUsage", &ext); + asn1_write_value(ext, "", str, 9); + test_tls_der_encode(ext, "", &der); + err = gnutls_x509_crt_set_extension_by_oid( + crt, "2.5.29.15", + der.data, der.size, + req->keyUsageCritical); + if (err < 0) { + g_critical("Failed to set certificate key usage %s", + gnutls_strerror(err)); + g_free(der.data); + abort(); + } + asn1_delete_structure(&ext); + g_free(der.data); + } + + /* + * Finally the key purpose extension. This time + * gnutls has the opposite problem, always hardcoding + * it to be non-critical. So once again we have to + * set this the hard way building up ASN1 data ourselves + */ + if (req->keyPurposeEnable) { + asn1_node ext = NULL; + + asn1_create_element(pkix_asn1, "PKIX1.ExtKeyUsageSyntax", &ext); + if (req->keyPurposeOID1) { + asn1_write_value(ext, "", "NEW", 1); + asn1_write_value(ext, "?LAST", req->keyPurposeOID1, 1); + } + if (req->keyPurposeOID2) { + asn1_write_value(ext, "", "NEW", 1); + asn1_write_value(ext, "?LAST", req->keyPurposeOID2, 1); + } + test_tls_der_encode(ext, "", &der); + err = gnutls_x509_crt_set_extension_by_oid( + crt, "2.5.29.37", + der.data, der.size, + req->keyPurposeCritical); + if (err < 0) { + g_critical("Failed to set certificate key purpose %s", + gnutls_strerror(err)); + g_free(der.data); + abort(); + } + asn1_delete_structure(&ext); + g_free(der.data); + } + + /* + * Any old serial number will do, so lets pick 5 + */ + err = gnutls_x509_crt_set_serial(crt, serial, 5); + if (err < 0) { + g_critical("Failed to set certificate serial %s", + gnutls_strerror(err)); + abort(); + } + + err = gnutls_x509_crt_set_activation_time(crt, start); + if (err < 0) { + g_critical("Failed to set certificate activation %s", + gnutls_strerror(err)); + abort(); + } + err = gnutls_x509_crt_set_expiration_time(crt, expire); + if (err < 0) { + g_critical("Failed to set certificate expiration %s", + gnutls_strerror(err)); + abort(); + } + + + /* + * If no 'ca' is set then we are self signing + * the cert. This is done for the root CA certs + */ + err = gnutls_x509_crt_sign2(crt, ca ? ca : crt, privkey, + GNUTLS_DIG_SHA256, 0); + if (err < 0) { + g_critical("Failed to sign certificate %s", + gnutls_strerror(err)); + abort(); + } + + /* + * Finally write the new cert out to disk + */ + err = gnutls_x509_crt_export( + crt, GNUTLS_X509_FMT_PEM, buffer, &size); + if (err < 0) { + g_critical("Failed to export certificate %s: %d", + gnutls_strerror(err), err); + abort(); + } + + if (!g_file_set_contents(req->filename, buffer, -1, NULL)) { + g_critical("Failed to write certificate %s", + req->filename); + abort(); + } + + req->crt = crt; +} + + +void test_tls_write_cert_chain(const char *filename, + gnutls_x509_crt_t *certs, + size_t ncerts) +{ + size_t i; + size_t capacity = 1024, offset = 0; + char *buffer = g_new0(char, capacity); + int err; + + for (i = 0; i < ncerts; i++) { + size_t len = capacity - offset; + retry: + err = gnutls_x509_crt_export(certs[i], GNUTLS_X509_FMT_PEM, + buffer + offset, &len); + if (err < 0) { + if (err == GNUTLS_E_SHORT_MEMORY_BUFFER) { + buffer = g_renew(char, buffer, offset + len); + capacity = offset + len; + goto retry; + } + g_critical("Failed to export certificate chain %s: %d", + gnutls_strerror(err), err); + abort(); + } + offset += len; + } + + if (!g_file_set_contents(filename, buffer, offset, NULL)) { + abort(); + } + g_free(buffer); +} + + +void test_tls_discard_cert(QCryptoTLSTestCertReq *req) +{ + if (!req->crt) { + return; + } + + gnutls_x509_crt_deinit(req->crt); + req->crt = NULL; + + if (getenv("QEMU_TEST_DEBUG_CERTS") == NULL) { + unlink(req->filename); + } +} + +#endif /* QCRYPTO_HAVE_TLS_TEST_SUPPORT */ diff --git a/tests/unit/crypto-tls-x509-helpers.h b/tests/unit/crypto-tls-x509-helpers.h new file mode 100644 index 0000000000..8fcd7785ab --- /dev/null +++ b/tests/unit/crypto-tls-x509-helpers.h @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + * + * Author: Daniel P. Berrange + */ + +#ifndef TESTS_CRYPTO_TLS_X509_HELPERS_H +#define TESTS_CRYPTO_TLS_X509_HELPERS_H + +#include +#include + +#if !(defined WIN32) && \ + defined(CONFIG_TASN1) +# define QCRYPTO_HAVE_TLS_TEST_SUPPORT +#endif + +#ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT +# include + + +/* + * This contains parameter about how to generate + * certificates. + */ +typedef struct QCryptoTLSTestCertReq QCryptoTLSTestCertReq; +struct QCryptoTLSTestCertReq { + gnutls_x509_crt_t crt; + + const char *filename; + + /* Identifying information */ + const char *country; + const char *cn; + const char *altname1; + const char *altname2; + const char *ipaddr1; + const char *ipaddr2; + + /* Basic constraints */ + bool basicConstraintsEnable; + bool basicConstraintsCritical; + bool basicConstraintsIsCA; + + /* Key usage */ + bool keyUsageEnable; + bool keyUsageCritical; + int keyUsageValue; + + /* Key purpose (aka Extended key usage) */ + bool keyPurposeEnable; + bool keyPurposeCritical; + const char *keyPurposeOID1; + const char *keyPurposeOID2; + + /* zero for current time, or non-zero for hours from now */ + int start_offset; + /* zero for 24 hours from now, or non-zero for hours from now */ + int expire_offset; +}; + +void test_tls_generate_cert(QCryptoTLSTestCertReq *req, + gnutls_x509_crt_t ca); +void test_tls_write_cert_chain(const char *filename, + gnutls_x509_crt_t *certs, + size_t ncerts); +void test_tls_discard_cert(QCryptoTLSTestCertReq *req); + +void test_tls_init(const char *keyfile); +void test_tls_cleanup(const char *keyfile); + +# define TLS_CERT_REQ(varname, cavarname, \ + country, commonname, \ + altname1, altname2, \ + ipaddr1, ipaddr2, \ + basicconsenable, basicconscritical, basicconsca, \ + keyusageenable, keyusagecritical, keyusagevalue, \ + keypurposeenable, keypurposecritical, \ + keypurposeoid1, keypurposeoid2, \ + startoffset, endoffset) \ + static QCryptoTLSTestCertReq varname = { \ + NULL, WORKDIR #varname "-ctx.pem", \ + country, commonname, altname1, altname2, \ + ipaddr1, ipaddr2, \ + basicconsenable, basicconscritical, basicconsca, \ + keyusageenable, keyusagecritical, keyusagevalue, \ + keypurposeenable, keypurposecritical, \ + keypurposeoid1, keypurposeoid2, \ + startoffset, endoffset \ + }; \ + test_tls_generate_cert(&varname, cavarname.crt) + +# define TLS_ROOT_REQ(varname, \ + country, commonname, \ + altname1, altname2, \ + ipaddr1, ipaddr2, \ + basicconsenable, basicconscritical, basicconsca, \ + keyusageenable, keyusagecritical, keyusagevalue, \ + keypurposeenable, keypurposecritical, \ + keypurposeoid1, keypurposeoid2, \ + startoffset, endoffset) \ + static QCryptoTLSTestCertReq varname = { \ + NULL, WORKDIR #varname "-ctx.pem", \ + country, commonname, altname1, altname2, \ + ipaddr1, ipaddr2, \ + basicconsenable, basicconscritical, basicconsca, \ + keyusageenable, keyusagecritical, keyusagevalue, \ + keypurposeenable, keypurposecritical, \ + keypurposeoid1, keypurposeoid2, \ + startoffset, endoffset \ + }; \ + test_tls_generate_cert(&varname, NULL) + +extern const asn1_static_node pkix_asn1_tab[]; + +#endif /* QCRYPTO_HAVE_TLS_TEST_SUPPORT */ + +#endif diff --git a/tests/unit/io-channel-helpers.c b/tests/unit/io-channel-helpers.c new file mode 100644 index 0000000000..ff156ed3c4 --- /dev/null +++ b/tests/unit/io-channel-helpers.c @@ -0,0 +1,163 @@ +/* + * QEMU I/O channel test helpers + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + * + */ + +#include "qemu/osdep.h" +#include "io-channel-helpers.h" +#include "qemu/iov.h" + +struct QIOChannelTest { + QIOChannel *src; + QIOChannel *dst; + bool blocking; + size_t len; + size_t niov; + char *input; + struct iovec *inputv; + char *output; + struct iovec *outputv; + Error *writeerr; + Error *readerr; +}; + + +/* This thread sends all data using iovecs */ +static gpointer test_io_thread_writer(gpointer opaque) +{ + QIOChannelTest *data = opaque; + + qio_channel_set_blocking(data->src, data->blocking, NULL); + + qio_channel_writev_all(data->src, + data->inputv, + data->niov, + &data->writeerr); + + return NULL; +} + + +/* This thread receives all data using iovecs */ +static gpointer test_io_thread_reader(gpointer opaque) +{ + QIOChannelTest *data = opaque; + + qio_channel_set_blocking(data->dst, data->blocking, NULL); + + qio_channel_readv_all(data->dst, + data->outputv, + data->niov, + &data->readerr); + + return NULL; +} + + +QIOChannelTest *qio_channel_test_new(void) +{ + QIOChannelTest *data = g_new0(QIOChannelTest, 1); + size_t i; + size_t offset; + + + /* We'll send 1 MB of data */ +#define CHUNK_COUNT 250 +#define CHUNK_LEN 4194 + + data->len = CHUNK_COUNT * CHUNK_LEN; + data->input = g_new0(char, data->len); + data->output = g_new0(gchar, data->len); + + /* Fill input with a pattern */ + for (i = 0; i < data->len; i += CHUNK_LEN) { + memset(data->input + i, (i / CHUNK_LEN), CHUNK_LEN); + } + + /* We'll split the data across a bunch of IO vecs */ + data->niov = CHUNK_COUNT; + data->inputv = g_new0(struct iovec, data->niov); + data->outputv = g_new0(struct iovec, data->niov); + + for (i = 0, offset = 0; i < data->niov; i++, offset += CHUNK_LEN) { + data->inputv[i].iov_base = data->input + offset; + data->outputv[i].iov_base = data->output + offset; + data->inputv[i].iov_len = CHUNK_LEN; + data->outputv[i].iov_len = CHUNK_LEN; + } + + return data; +} + +void qio_channel_test_run_threads(QIOChannelTest *test, + bool blocking, + QIOChannel *src, + QIOChannel *dst) +{ + GThread *reader, *writer; + + test->src = src; + test->dst = dst; + test->blocking = blocking; + + reader = g_thread_new("reader", + test_io_thread_reader, + test); + writer = g_thread_new("writer", + test_io_thread_writer, + test); + + g_thread_join(reader); + g_thread_join(writer); + + test->dst = test->src = NULL; +} + + +void qio_channel_test_run_writer(QIOChannelTest *test, + QIOChannel *src) +{ + test->src = src; + test_io_thread_writer(test); + test->src = NULL; +} + + +void qio_channel_test_run_reader(QIOChannelTest *test, + QIOChannel *dst) +{ + test->dst = dst; + test_io_thread_reader(test); + test->dst = NULL; +} + + +void qio_channel_test_validate(QIOChannelTest *test) +{ + g_assert(test->readerr == NULL); + g_assert(test->writeerr == NULL); + g_assert_cmpint(memcmp(test->input, + test->output, + test->len), ==, 0); + + g_free(test->inputv); + g_free(test->outputv); + g_free(test->input); + g_free(test->output); + g_free(test); +} diff --git a/tests/unit/io-channel-helpers.h b/tests/unit/io-channel-helpers.h new file mode 100644 index 0000000000..3d14043710 --- /dev/null +++ b/tests/unit/io-channel-helpers.h @@ -0,0 +1,41 @@ +/* + * QEMU I/O channel test helpers + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#ifndef TEST_IO_CHANNEL_HELPERS_H +#define TEST_IO_CHANNEL_HELPERS_H + +#include "io/channel.h" + +typedef struct QIOChannelTest QIOChannelTest; + +QIOChannelTest *qio_channel_test_new(void); + +void qio_channel_test_run_threads(QIOChannelTest *test, + bool blocking, + QIOChannel *src, + QIOChannel *dst); + +void qio_channel_test_run_writer(QIOChannelTest *test, + QIOChannel *src); +void qio_channel_test_run_reader(QIOChannelTest *test, + QIOChannel *dst); + +void qio_channel_test_validate(QIOChannelTest *test); + +#endif /* TEST_IO_CHANNEL_HELPERS_H */ diff --git a/tests/unit/iothread.c b/tests/unit/iothread.c new file mode 100644 index 0000000000..afde12b4ef --- /dev/null +++ b/tests/unit/iothread.c @@ -0,0 +1,127 @@ +/* + * Event loop thread implementation for unit tests + * + * Copyright Red Hat Inc., 2013, 2016 + * + * Authors: + * Stefan Hajnoczi + * Paolo Bonzini + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "block/aio.h" +#include "qemu/main-loop.h" +#include "qemu/rcu.h" +#include "iothread.h" + +struct IOThread { + AioContext *ctx; + GMainContext *worker_context; + GMainLoop *main_loop; + + QemuThread thread; + QemuMutex init_done_lock; + QemuCond init_done_cond; /* is thread initialization done? */ + bool stopping; +}; + +static __thread IOThread *my_iothread; + +AioContext *qemu_get_current_aio_context(void) +{ + return my_iothread ? my_iothread->ctx : qemu_get_aio_context(); +} + +static void iothread_init_gcontext(IOThread *iothread) +{ + GSource *source; + + iothread->worker_context = g_main_context_new(); + source = aio_get_g_source(iothread_get_aio_context(iothread)); + g_source_attach(source, iothread->worker_context); + g_source_unref(source); + iothread->main_loop = g_main_loop_new(iothread->worker_context, TRUE); +} + +static void *iothread_run(void *opaque) +{ + IOThread *iothread = opaque; + + rcu_register_thread(); + + my_iothread = iothread; + qemu_mutex_lock(&iothread->init_done_lock); + iothread->ctx = aio_context_new(&error_abort); + + /* + * We must connect the ctx to a GMainContext, because in older versions + * of glib the g_source_ref()/unref() functions are not threadsafe + * on sources without a context. + */ + iothread_init_gcontext(iothread); + + /* + * g_main_context_push_thread_default() must be called before anything + * in this new thread uses glib. + */ + g_main_context_push_thread_default(iothread->worker_context); + + qemu_cond_signal(&iothread->init_done_cond); + qemu_mutex_unlock(&iothread->init_done_lock); + + while (!qatomic_read(&iothread->stopping)) { + aio_poll(iothread->ctx, true); + } + + g_main_context_pop_thread_default(iothread->worker_context); + rcu_unregister_thread(); + return NULL; +} + +static void iothread_stop_bh(void *opaque) +{ + IOThread *iothread = opaque; + + iothread->stopping = true; +} + +void iothread_join(IOThread *iothread) +{ + aio_bh_schedule_oneshot(iothread->ctx, iothread_stop_bh, iothread); + qemu_thread_join(&iothread->thread); + g_main_context_unref(iothread->worker_context); + g_main_loop_unref(iothread->main_loop); + qemu_cond_destroy(&iothread->init_done_cond); + qemu_mutex_destroy(&iothread->init_done_lock); + aio_context_unref(iothread->ctx); + g_free(iothread); +} + +IOThread *iothread_new(void) +{ + IOThread *iothread = g_new0(IOThread, 1); + + qemu_mutex_init(&iothread->init_done_lock); + qemu_cond_init(&iothread->init_done_cond); + qemu_thread_create(&iothread->thread, NULL, iothread_run, + iothread, QEMU_THREAD_JOINABLE); + + /* Wait for initialization to complete */ + qemu_mutex_lock(&iothread->init_done_lock); + while (iothread->ctx == NULL) { + qemu_cond_wait(&iothread->init_done_cond, + &iothread->init_done_lock); + } + qemu_mutex_unlock(&iothread->init_done_lock); + return iothread; +} + +AioContext *iothread_get_aio_context(IOThread *iothread) +{ + return iothread->ctx; +} diff --git a/tests/unit/iothread.h b/tests/unit/iothread.h new file mode 100644 index 0000000000..4877cea6a3 --- /dev/null +++ b/tests/unit/iothread.h @@ -0,0 +1,25 @@ +/* + * Event loop thread implementation for unit tests + * + * Copyright Red Hat Inc., 2013, 2016 + * + * Authors: + * Stefan Hajnoczi + * Paolo Bonzini + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#ifndef TEST_IOTHREAD_H +#define TEST_IOTHREAD_H + +#include "block/aio.h" +#include "qemu/thread.h" + +typedef struct IOThread IOThread; + +IOThread *iothread_new(void); +void iothread_join(IOThread *iothread); +AioContext *iothread_get_aio_context(IOThread *iothread); + +#endif diff --git a/tests/unit/meson.build b/tests/unit/meson.build new file mode 100644 index 0000000000..4bfe4627ba --- /dev/null +++ b/tests/unit/meson.build @@ -0,0 +1,184 @@ + +testblock = declare_dependency(dependencies: [block], sources: 'iothread.c') + +tests = { + 'check-block-qdict': [], + 'check-qdict': [], + 'check-qnum': [], + 'check-qstring': [], + 'check-qlist': [], + 'check-qnull': [], + 'check-qobject': [], + 'check-qjson': [], + 'check-qlit': [], + 'test-qobject-output-visitor': [testqapi], + 'test-clone-visitor': [testqapi], + 'test-qobject-input-visitor': [testqapi], + 'test-string-input-visitor': [testqapi], + 'test-string-output-visitor': [testqapi], + 'test-opts-visitor': [testqapi], + 'test-visitor-serialization': [testqapi], + 'test-bitmap': [], + # all code tested by test-x86-cpuid is inside topology.h + 'test-x86-cpuid': [], + 'test-cutils': [], + 'test-shift128': [], + 'test-mul64': [], + # all code tested by test-int128 is inside int128.h + 'test-int128': [], + 'rcutorture': [], + 'test-rcu-list': [], + 'test-rcu-simpleq': [], + 'test-rcu-tailq': [], + 'test-rcu-slist': [], + 'test-qdist': [], + 'test-qht': [], + 'test-bitops': [], + 'test-bitcnt': [], + 'test-qgraph': ['../qtest/libqos/qgraph.c'], + 'check-qom-interface': [qom], + 'check-qom-proplist': [qom], + 'test-qemu-opts': [], + 'test-keyval': [testqapi], + 'test-logging': [], + 'test-uuid': [], + 'ptimer-test': ['ptimer-test-stubs.c', meson.source_root() / 'hw/core/ptimer.c'], + 'test-qapi-util': [], +} + +if have_system or have_tools + tests += { + 'test-qmp-event': [testqapi], + } +endif + +if have_block + tests += { + 'test-coroutine': [testblock], + 'test-aio': [testblock], + 'test-aio-multithread': [testblock], + 'test-throttle': [testblock], + 'test-thread-pool': [testblock], + 'test-hbitmap': [testblock], + 'test-bdrv-drain': [testblock], + 'test-bdrv-graph-mod': [testblock], + 'test-blockjob': [testblock], + 'test-blockjob-txn': [testblock], + 'test-block-backend': [testblock], + 'test-block-iothread': [testblock], + 'test-write-threshold': [testblock], + 'test-crypto-hash': [crypto], + 'test-crypto-hmac': [crypto], + 'test-crypto-cipher': [crypto], + 'test-crypto-secret': [crypto, keyutils], + 'test-authz-simple': [authz], + 'test-authz-list': [authz], + 'test-authz-listfile': [authz], + 'test-io-task': [testblock], + 'test-io-channel-socket': ['socket-helpers.c', 'io-channel-helpers.c', io], + 'test-io-channel-file': ['io-channel-helpers.c', io], + 'test-io-channel-command': ['io-channel-helpers.c', io], + 'test-io-channel-buffer': ['io-channel-helpers.c', io], + 'test-crypto-ivgen': [io], + 'test-crypto-afsplit': [io], + 'test-crypto-block': [io], + } + if 'CONFIG_GNUTLS' in config_host and \ + 'CONFIG_TASN1' in config_host and \ + 'CONFIG_POSIX' in config_host + tests += { + 'test-crypto-tlscredsx509': ['crypto-tls-x509-helpers.c', 'pkix_asn1_tab.c', + tasn1, crypto, gnutls], + 'test-crypto-tlssession': ['crypto-tls-x509-helpers.c', 'pkix_asn1_tab.c', 'crypto-tls-psk-helpers.c', + tasn1, crypto, gnutls], + 'test-io-channel-tls': ['io-channel-helpers.c', 'crypto-tls-x509-helpers.c', 'pkix_asn1_tab.c', + tasn1, io, crypto, gnutls]} + endif + if 'CONFIG_AUTH_PAM' in config_host + tests += {'test-authz-pam': [authz]} + endif + if 'CONFIG_QEMU_PRIVATE_XTS' in config_host + tests += {'test-crypto-xts': [crypto, io]} + endif + if 'CONFIG_POSIX' in config_host + tests += {'test-image-locking': [testblock]} + endif + if 'CONFIG_REPLICATION' in config_host + tests += {'test-replication': [testblock]} + endif + if 'CONFIG_NETTLE' in config_host or 'CONFIG_GCRYPT' in config_host + tests += {'test-crypto-pbkdf': [io]} + endif + if 'CONFIG_EPOLL_CREATE1' in config_host + tests += {'test-fdmon-epoll': [testblock]} + endif +endif + +if have_system + tests += { + 'test-iov': [], + 'test-qmp-cmds': [testqapi], + 'test-xbzrle': [migration], + 'test-timed-average': [], + 'test-util-sockets': ['socket-helpers.c'], + 'test-base64': [], + 'test-bufferiszero': [], + 'test-vmstate': [migration, io] + } + if 'CONFIG_INOTIFY1' in config_host + tests += {'test-util-filemonitor': []} + endif + + # Some tests: test-char, test-qdev-global-props, and test-qga, + # are not runnable under TSan due to a known issue. + # https://github.com/google/sanitizers/issues/1116 + if 'CONFIG_TSAN' not in config_host + if 'CONFIG_POSIX' in config_host + tests += { + 'test-char': ['socket-helpers.c', qom, io, chardev] + } + endif + + tests += { + 'test-qdev-global-props': [qom, hwcore, testqapi] + } + endif +endif + +if 'CONFIG_TSAN' not in config_host and \ + 'CONFIG_GUEST_AGENT' in config_host and \ + 'CONFIG_LINUX' in config_host + tests += {'test-qga': ['../qtest/libqtest.c']} + test_deps += {'test-qga': qga} +endif + +test_env = environment() +test_env.set('G_TEST_SRCDIR', meson.current_source_dir()) +test_env.set('G_TEST_BUILDDIR', meson.current_build_dir()) + +slow_tests = { + 'test-crypto-tlscredsx509': 45, + 'test-crypto-tlssession': 45 +} + +foreach test_name, extra: tests + src = [test_name + '.c'] + deps = [qemuutil] + if extra.length() > 0 + # use a sourceset to quickly separate sources and deps + test_ss = ss.source_set() + test_ss.add(extra) + src += test_ss.all_sources() + deps += test_ss.all_dependencies() + endif + exe = executable(test_name, src, genh, dependencies: deps) + + test(test_name, exe, + depends: test_deps.get(test_name, []), + env: test_env, + args: ['--tap', '-k'], + protocol: 'tap', + timeout: slow_tests.get(test_name, 30), + priority: slow_tests.get(test_name, 30), + suite: ['unit']) +endforeach diff --git a/tests/unit/pkix_asn1_tab.c b/tests/unit/pkix_asn1_tab.c new file mode 100644 index 0000000000..15397cf77a --- /dev/null +++ b/tests/unit/pkix_asn1_tab.c @@ -0,0 +1,1108 @@ +/* + * This file is taken from gnutls 1.6.3 under the GPLv2+ + * and is under copyright of various GNUTLS contributors. + */ + +#include "qemu/osdep.h" +#include "crypto-tls-x509-helpers.h" + +#ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT + +const asn1_static_node pkix_asn1_tab[] = { + {"PKIX1", 536875024, 0}, + {0, 1073741836, 0}, + {"id-ce", 1879048204, 0}, + {"joint-iso-ccitt", 1073741825, "2"}, + {"ds", 1073741825, "5"}, + {0, 1, "29"}, + {"id-ce-authorityKeyIdentifier", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "35"}, + {"AuthorityKeyIdentifier", 1610612741, 0}, + {"keyIdentifier", 1610637314, "KeyIdentifier"}, + {0, 4104, "0"}, + {"authorityCertIssuer", 1610637314, "GeneralNames"}, + {0, 4104, "1"}, + {"authorityCertSerialNumber", 536895490, "CertificateSerialNumber"}, + {0, 4104, "2"}, + {"KeyIdentifier", 1073741831, 0}, + {"id-ce-subjectKeyIdentifier", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "14"}, + {"SubjectKeyIdentifier", 1073741826, "KeyIdentifier"}, + {"id-ce-keyUsage", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "15"}, + {"KeyUsage", 1610874886, 0}, + {"digitalSignature", 1073741825, "0"}, + {"nonRepudiation", 1073741825, "1"}, + {"keyEncipherment", 1073741825, "2"}, + {"dataEncipherment", 1073741825, "3"}, + {"keyAgreement", 1073741825, "4"}, + {"keyCertSign", 1073741825, "5"}, + {"cRLSign", 1073741825, "6"}, + {"encipherOnly", 1073741825, "7"}, + {"decipherOnly", 1, "8"}, + {"id-ce-privateKeyUsagePeriod", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "16"}, + {"PrivateKeyUsagePeriod", 1610612741, 0}, + {"notBefore", 1619025937, 0}, + {0, 4104, "0"}, + {"notAfter", 545284113, 0}, + {0, 4104, "1"}, + {"id-ce-certificatePolicies", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "32"}, + {"CertificatePolicies", 1612709899, 0}, + {"MAX", 1074266122, "1"}, + {0, 2, "PolicyInformation"}, + {"PolicyInformation", 1610612741, 0}, + {"policyIdentifier", 1073741826, "CertPolicyId"}, + {"policyQualifiers", 538984459, 0}, + {"MAX", 1074266122, "1"}, + {0, 2, "PolicyQualifierInfo"}, + {"CertPolicyId", 1073741836, 0}, + {"PolicyQualifierInfo", 1610612741, 0}, + {"policyQualifierId", 1073741826, "PolicyQualifierId"}, + {"qualifier", 541065229, 0}, + {"policyQualifierId", 1, 0}, + {"PolicyQualifierId", 1073741836, 0}, + {"CPSuri", 1073741826, "IA5String"}, + {"UserNotice", 1610612741, 0}, + {"noticeRef", 1073758210, "NoticeReference"}, + {"explicitText", 16386, "DisplayText"}, + {"NoticeReference", 1610612741, 0}, + {"organization", 1073741826, "DisplayText"}, + {"noticeNumbers", 536870923, 0}, + {0, 3, 0}, + {"DisplayText", 1610612754, 0}, + {"visibleString", 1612709890, "VisibleString"}, + {"200", 524298, "1"}, + {"bmpString", 1612709890, "BMPString"}, + {"200", 524298, "1"}, + {"utf8String", 538968066, "UTF8String"}, + {"200", 524298, "1"}, + {"id-ce-policyMappings", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "33"}, + {"PolicyMappings", 1612709899, 0}, + {"MAX", 1074266122, "1"}, + {0, 536870917, 0}, + {"issuerDomainPolicy", 1073741826, "CertPolicyId"}, + {"subjectDomainPolicy", 2, "CertPolicyId"}, + {"DirectoryString", 1610612754, 0}, + {"teletexString", 1612709890, "TeletexString"}, + {"MAX", 524298, "1"}, + {"printableString", 1612709890, "PrintableString"}, + {"MAX", 524298, "1"}, + {"universalString", 1612709890, "UniversalString"}, + {"MAX", 524298, "1"}, + {"utf8String", 1612709890, "UTF8String"}, + {"MAX", 524298, "1"}, + {"bmpString", 1612709890, "BMPString"}, + {"MAX", 524298, "1"}, + {"ia5String", 538968066, "IA5String"}, + {"MAX", 524298, "1"}, + {"id-ce-subjectAltName", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "17"}, + {"SubjectAltName", 1073741826, "GeneralNames"}, + {"GeneralNames", 1612709899, 0}, + {"MAX", 1074266122, "1"}, + {0, 2, "GeneralName"}, + {"GeneralName", 1610612754, 0}, + {"otherName", 1610620930, "AnotherName"}, + {0, 4104, "0"}, + {"rfc822Name", 1610620930, "IA5String"}, + {0, 4104, "1"}, + {"dNSName", 1610620930, "IA5String"}, + {0, 4104, "2"}, + {"x400Address", 1610620930, "ORAddress"}, + {0, 4104, "3"}, + {"directoryName", 1610620930, "RDNSequence"}, + {0, 2056, "4"}, + {"ediPartyName", 1610620930, "EDIPartyName"}, + {0, 4104, "5"}, + {"uniformResourceIdentifier", 1610620930, "IA5String"}, + {0, 4104, "6"}, + {"iPAddress", 1610620935, 0}, + {0, 4104, "7"}, + {"registeredID", 536879116, 0}, + {0, 4104, "8"}, + {"AnotherName", 1610612741, 0}, + {"type-id", 1073741836, 0}, + {"value", 541073421, 0}, + {0, 1073743880, "0"}, + {"type-id", 1, 0}, + {"EDIPartyName", 1610612741, 0}, + {"nameAssigner", 1610637314, "DirectoryString"}, + {0, 4104, "0"}, + {"partyName", 536879106, "DirectoryString"}, + {0, 4104, "1"}, + {"id-ce-issuerAltName", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "18"}, + {"IssuerAltName", 1073741826, "GeneralNames"}, + {"id-ce-subjectDirectoryAttributes", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "9"}, + {"SubjectDirectoryAttributes", 1612709899, 0}, + {"MAX", 1074266122, "1"}, + {0, 2, "Attribute"}, + {"id-ce-basicConstraints", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "19"}, + {"BasicConstraints", 1610612741, 0}, + {"cA", 1610645508, 0}, + {0, 131081, 0}, + {"pathLenConstraint", 537411587, 0}, + {"0", 10, "MAX"}, + {"id-ce-nameConstraints", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "30"}, + {"NameConstraints", 1610612741, 0}, + {"permittedSubtrees", 1610637314, "GeneralSubtrees"}, + {0, 4104, "0"}, + {"excludedSubtrees", 536895490, "GeneralSubtrees"}, + {0, 4104, "1"}, + {"GeneralSubtrees", 1612709899, 0}, + {"MAX", 1074266122, "1"}, + {0, 2, "GeneralSubtree"}, + {"GeneralSubtree", 1610612741, 0}, + {"base", 1073741826, "GeneralName"}, + {"minimum", 1610653698, "BaseDistance"}, + {0, 1073741833, "0"}, + {0, 4104, "0"}, + {"maximum", 536895490, "BaseDistance"}, + {0, 4104, "1"}, + {"BaseDistance", 1611137027, 0}, + {"0", 10, "MAX"}, + {"id-ce-policyConstraints", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "36"}, + {"PolicyConstraints", 1610612741, 0}, + {"requireExplicitPolicy", 1610637314, "SkipCerts"}, + {0, 4104, "0"}, + {"inhibitPolicyMapping", 536895490, "SkipCerts"}, + {0, 4104, "1"}, + {"SkipCerts", 1611137027, 0}, + {"0", 10, "MAX"}, + {"id-ce-cRLDistributionPoints", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "31"}, + {"CRLDistributionPoints", 1612709899, 0}, + {"MAX", 1074266122, "1"}, + {0, 2, "DistributionPoint"}, + {"DistributionPoint", 1610612741, 0}, + {"distributionPoint", 1610637314, "DistributionPointName"}, + {0, 2056, "0"}, + {"reasons", 1610637314, "ReasonFlags"}, + {0, 4104, "1"}, + {"cRLIssuer", 536895490, "GeneralNames"}, + {0, 4104, "2"}, + {"DistributionPointName", 1610612754, 0}, + {"fullName", 1610620930, "GeneralNames"}, + {0, 4104, "0"}, + {"nameRelativeToCRLIssuer", 536879106, "RelativeDistinguishedName"}, + {0, 4104, "1"}, + {"ReasonFlags", 1610874886, 0}, + {"unused", 1073741825, "0"}, + {"keyCompromise", 1073741825, "1"}, + {"cACompromise", 1073741825, "2"}, + {"affiliationChanged", 1073741825, "3"}, + {"superseded", 1073741825, "4"}, + {"cessationOfOperation", 1073741825, "5"}, + {"certificateHold", 1073741825, "6"}, + {"privilegeWithdrawn", 1073741825, "7"}, + {"aACompromise", 1, "8"}, + {"id-ce-extKeyUsage", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "37"}, + {"ExtKeyUsageSyntax", 1612709899, 0}, + {"MAX", 1074266122, "1"}, + {0, 2, "KeyPurposeId"}, + {"KeyPurposeId", 1073741836, 0}, + {"id-kp-serverAuth", 1879048204, 0}, + {0, 1073741825, "id-kp"}, + {0, 1, "1"}, + {"id-kp-clientAuth", 1879048204, 0}, + {0, 1073741825, "id-kp"}, + {0, 1, "2"}, + {"id-kp-codeSigning", 1879048204, 0}, + {0, 1073741825, "id-kp"}, + {0, 1, "3"}, + {"id-kp-emailProtection", 1879048204, 0}, + {0, 1073741825, "id-kp"}, + {0, 1, "4"}, + {"id-kp-ipsecEndSystem", 1879048204, 0}, + {0, 1073741825, "id-kp"}, + {0, 1, "5"}, + {"id-kp-ipsecTunnel", 1879048204, 0}, + {0, 1073741825, "id-kp"}, + {0, 1, "6"}, + {"id-kp-ipsecUser", 1879048204, 0}, + {0, 1073741825, "id-kp"}, + {0, 1, "7"}, + {"id-kp-timeStamping", 1879048204, 0}, + {0, 1073741825, "id-kp"}, + {0, 1, "8"}, + {"id-pe-authorityInfoAccess", 1879048204, 0}, + {0, 1073741825, "id-pe"}, + {0, 1, "1"}, + {"AuthorityInfoAccessSyntax", 1612709899, 0}, + {"MAX", 1074266122, "1"}, + {0, 2, "AccessDescription"}, + {"AccessDescription", 1610612741, 0}, + {"accessMethod", 1073741836, 0}, + {"accessLocation", 2, "GeneralName"}, + {"id-ce-cRLNumber", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "20"}, + {"CRLNumber", 1611137027, 0}, + {"0", 10, "MAX"}, + {"id-ce-issuingDistributionPoint", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "28"}, + {"IssuingDistributionPoint", 1610612741, 0}, + {"distributionPoint", 1610637314, "DistributionPointName"}, + {0, 4104, "0"}, + {"onlyContainsUserCerts", 1610653700, 0}, + {0, 1073872905, 0}, + {0, 4104, "1"}, + {"onlyContainsCACerts", 1610653700, 0}, + {0, 1073872905, 0}, + {0, 4104, "2"}, + {"onlySomeReasons", 1610637314, "ReasonFlags"}, + {0, 4104, "3"}, + {"indirectCRL", 536911876, 0}, + {0, 1073872905, 0}, + {0, 4104, "4"}, + {"id-ce-deltaCRLIndicator", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "27"}, + {"BaseCRLNumber", 1073741826, "CRLNumber"}, + {"id-ce-cRLReasons", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "21"}, + {"CRLReason", 1610874901, 0}, + {"unspecified", 1073741825, "0"}, + {"keyCompromise", 1073741825, "1"}, + {"cACompromise", 1073741825, "2"}, + {"affiliationChanged", 1073741825, "3"}, + {"superseded", 1073741825, "4"}, + {"cessationOfOperation", 1073741825, "5"}, + {"certificateHold", 1073741825, "6"}, + {"removeFromCRL", 1, "8"}, + {"id-ce-certificateIssuer", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "29"}, + {"CertificateIssuer", 1073741826, "GeneralNames"}, + {"id-ce-holdInstructionCode", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "23"}, + {"HoldInstructionCode", 1073741836, 0}, + {"holdInstruction", 1879048204, 0}, + {"joint-iso-itu-t", 1073741825, "2"}, + {"member-body", 1073741825, "2"}, + {"us", 1073741825, "840"}, + {"x9cm", 1073741825, "10040"}, + {0, 1, "2"}, + {"id-holdinstruction-none", 1879048204, 0}, + {0, 1073741825, "holdInstruction"}, + {0, 1, "1"}, + {"id-holdinstruction-callissuer", 1879048204, 0}, + {0, 1073741825, "holdInstruction"}, + {0, 1, "2"}, + {"id-holdinstruction-reject", 1879048204, 0}, + {0, 1073741825, "holdInstruction"}, + {0, 1, "3"}, + {"id-ce-invalidityDate", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "24"}, + {"InvalidityDate", 1082130449, 0}, + {"VisibleString", 1610620935, 0}, + {0, 4360, "26"}, + {"NumericString", 1610620935, 0}, + {0, 4360, "18"}, + {"IA5String", 1610620935, 0}, + {0, 4360, "22"}, + {"TeletexString", 1610620935, 0}, + {0, 4360, "20"}, + {"PrintableString", 1610620935, 0}, + {0, 4360, "19"}, + {"UniversalString", 1610620935, 0}, + {0, 4360, "28"}, + {"BMPString", 1610620935, 0}, + {0, 4360, "30"}, + {"UTF8String", 1610620935, 0}, + {0, 4360, "12"}, + {"id-pkix", 1879048204, 0}, + {"iso", 1073741825, "1"}, + {"identified-organization", 1073741825, "3"}, + {"dod", 1073741825, "6"}, + {"internet", 1073741825, "1"}, + {"security", 1073741825, "5"}, + {"mechanisms", 1073741825, "5"}, + {"pkix", 1, "7"}, + {"id-pe", 1879048204, 0}, + {0, 1073741825, "id-pkix"}, + {0, 1, "1"}, + {"id-qt", 1879048204, 0}, + {0, 1073741825, "id-pkix"}, + {0, 1, "2"}, + {"id-kp", 1879048204, 0}, + {0, 1073741825, "id-pkix"}, + {0, 1, "3"}, + {"id-ad", 1879048204, 0}, + {0, 1073741825, "id-pkix"}, + {0, 1, "48"}, + {"id-qt-cps", 1879048204, 0}, + {0, 1073741825, "id-qt"}, + {0, 1, "1"}, + {"id-qt-unotice", 1879048204, 0}, + {0, 1073741825, "id-qt"}, + {0, 1, "2"}, + {"id-ad-ocsp", 1879048204, 0}, + {0, 1073741825, "id-ad"}, + {0, 1, "1"}, + {"id-ad-caIssuers", 1879048204, 0}, + {0, 1073741825, "id-ad"}, + {0, 1, "2"}, + {"Attribute", 1610612741, 0}, + {"type", 1073741826, "AttributeType"}, + {"values", 536870927, 0}, + {0, 2, "AttributeValue"}, + {"AttributeType", 1073741836, 0}, + {"AttributeValue", 1614807053, 0}, + {"type", 1, 0}, + {"AttributeTypeAndValue", 1610612741, 0}, + {"type", 1073741826, "AttributeType"}, + {"value", 2, "AttributeValue"}, + {"id-at", 1879048204, 0}, + {"joint-iso-ccitt", 1073741825, "2"}, + {"ds", 1073741825, "5"}, + {0, 1, "4"}, + {"id-at-initials", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "43"}, + {"X520initials", 1073741826, "DirectoryString"}, + {"id-at-generationQualifier", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "44"}, + {"X520generationQualifier", 1073741826, "DirectoryString"}, + {"id-at-surname", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "4"}, + {"X520surName", 1073741826, "DirectoryString"}, + {"id-at-givenName", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "42"}, + {"X520givenName", 1073741826, "DirectoryString"}, + {"id-at-name", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "41"}, + {"X520name", 1073741826, "DirectoryString"}, + {"id-at-commonName", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "3"}, + {"X520CommonName", 1073741826, "DirectoryString"}, + {"id-at-localityName", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "7"}, + {"X520LocalityName", 1073741826, "DirectoryString"}, + {"id-at-stateOrProvinceName", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "8"}, + {"X520StateOrProvinceName", 1073741826, "DirectoryString"}, + {"id-at-organizationName", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "10"}, + {"X520OrganizationName", 1073741826, "DirectoryString"}, + {"id-at-organizationalUnitName", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "11"}, + {"X520OrganizationalUnitName", 1073741826, "DirectoryString"}, + {"id-at-title", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "12"}, + {"X520Title", 1073741826, "DirectoryString"}, + {"id-at-description", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "13"}, + {"X520Description", 1073741826, "DirectoryString"}, + {"id-at-dnQualifier", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "46"}, + {"X520dnQualifier", 1073741826, "PrintableString"}, + {"id-at-countryName", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "6"}, + {"X520countryName", 1612709890, "PrintableString"}, + {0, 1048586, "2"}, + {"id-at-serialNumber", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "5"}, + {"X520serialNumber", 1073741826, "PrintableString"}, + {"id-at-telephoneNumber", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "20"}, + {"X520telephoneNumber", 1073741826, "PrintableString"}, + {"id-at-facsimileTelephoneNumber", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "23"}, + {"X520facsimileTelephoneNumber", 1073741826, "PrintableString"}, + {"id-at-pseudonym", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "65"}, + {"X520pseudonym", 1073741826, "DirectoryString"}, + {"id-at-name", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "41"}, + {"X520name", 1073741826, "DirectoryString"}, + {"id-at-streetAddress", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "9"}, + {"X520streetAddress", 1073741826, "DirectoryString"}, + {"id-at-postalAddress", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "16"}, + {"X520postalAddress", 1073741826, "PostalAddress"}, + {"PostalAddress", 1610612747, 0}, + {0, 2, "DirectoryString"}, + {"pkcs", 1879048204, 0}, + {"iso", 1073741825, "1"}, + {"member-body", 1073741825, "2"}, + {"us", 1073741825, "840"}, + {"rsadsi", 1073741825, "113549"}, + {"pkcs", 1, "1"}, + {"pkcs-9", 1879048204, 0}, + {0, 1073741825, "pkcs"}, + {0, 1, "9"}, + {"emailAddress", 1880096780, "AttributeType"}, + {0, 1073741825, "pkcs-9"}, + {0, 1, "1"}, + {"Pkcs9email", 1612709890, "IA5String"}, + {"ub-emailaddress-length", 524298, "1"}, + {"Name", 1610612754, 0}, + {"rdnSequence", 2, "RDNSequence"}, + {"RDNSequence", 1610612747, 0}, + {0, 2, "RelativeDistinguishedName"}, + {"DistinguishedName", 1073741826, "RDNSequence"}, + {"RelativeDistinguishedName", 1612709903, 0}, + {"MAX", 1074266122, "1"}, + {0, 2, "AttributeTypeAndValue"}, + {"Certificate", 1610612741, 0}, + {"tbsCertificate", 1073741826, "TBSCertificate"}, + {"signatureAlgorithm", 1073741826, "AlgorithmIdentifier"}, + {"signature", 6, 0}, + {"TBSCertificate", 1610612741, 0}, + {"version", 1610653698, "Version"}, + {0, 1073741833, "v1"}, + {0, 2056, "0"}, + {"serialNumber", 1073741826, "CertificateSerialNumber"}, + {"signature", 1073741826, "AlgorithmIdentifier"}, + {"issuer", 1073741826, "Name"}, + {"validity", 1073741826, "Validity"}, + {"subject", 1073741826, "Name"}, + {"subjectPublicKeyInfo", 1073741826, "SubjectPublicKeyInfo"}, + {"issuerUniqueID", 1610637314, "UniqueIdentifier"}, + {0, 4104, "1"}, + {"subjectUniqueID", 1610637314, "UniqueIdentifier"}, + {0, 4104, "2"}, + {"extensions", 536895490, "Extensions"}, + {0, 2056, "3"}, + {"Version", 1610874883, 0}, + {"v1", 1073741825, "0"}, + {"v2", 1073741825, "1"}, + {"v3", 1, "2"}, + {"CertificateSerialNumber", 1073741827, 0}, + {"Validity", 1610612741, 0}, + {"notBefore", 1073741826, "Time"}, + {"notAfter", 2, "Time"}, + {"Time", 1610612754, 0}, + {"utcTime", 1090519057, 0}, + {"generalTime", 8388625, 0}, + {"UniqueIdentifier", 1073741830, 0}, + {"SubjectPublicKeyInfo", 1610612741, 0}, + {"algorithm", 1073741826, "AlgorithmIdentifier"}, + {"subjectPublicKey", 6, 0}, + {"Extensions", 1612709899, 0}, + {"MAX", 1074266122, "1"}, + {0, 2, "Extension"}, + {"Extension", 1610612741, 0}, + {"extnID", 1073741836, 0}, + {"critical", 1610645508, 0}, + {0, 131081, 0}, + {"extnValue", 7, 0}, + {"CertificateList", 1610612741, 0}, + {"tbsCertList", 1073741826, "TBSCertList"}, + {"signatureAlgorithm", 1073741826, "AlgorithmIdentifier"}, + {"signature", 6, 0}, + {"TBSCertList", 1610612741, 0}, + {"version", 1073758210, "Version"}, + {"signature", 1073741826, "AlgorithmIdentifier"}, + {"issuer", 1073741826, "Name"}, + {"thisUpdate", 1073741826, "Time"}, + {"nextUpdate", 1073758210, "Time"}, + {"revokedCertificates", 1610629131, 0}, + {0, 536870917, 0}, + {"userCertificate", 1073741826, "CertificateSerialNumber"}, + {"revocationDate", 1073741826, "Time"}, + {"crlEntryExtensions", 16386, "Extensions"}, + {"crlExtensions", 536895490, "Extensions"}, + {0, 2056, "0"}, + {"AlgorithmIdentifier", 1610612741, 0}, + {"algorithm", 1073741836, 0}, + {"parameters", 541081613, 0}, + {"algorithm", 1, 0}, + {"pkcs-1", 1879048204, 0}, + {0, 1073741825, "pkcs"}, + {0, 1, "1"}, + {"rsaEncryption", 1879048204, 0}, + {0, 1073741825, "pkcs-1"}, + {0, 1, "1"}, + {"md2WithRSAEncryption", 1879048204, 0}, + {0, 1073741825, "pkcs-1"}, + {0, 1, "2"}, + {"md5WithRSAEncryption", 1879048204, 0}, + {0, 1073741825, "pkcs-1"}, + {0, 1, "4"}, + {"sha1WithRSAEncryption", 1879048204, 0}, + {0, 1073741825, "pkcs-1"}, + {0, 1, "5"}, + {"id-dsa-with-sha1", 1879048204, 0}, + {"iso", 1073741825, "1"}, + {"member-body", 1073741825, "2"}, + {"us", 1073741825, "840"}, + {"x9-57", 1073741825, "10040"}, + {"x9algorithm", 1073741825, "4"}, + {0, 1, "3"}, + {"Dss-Sig-Value", 1610612741, 0}, + {"r", 1073741827, 0}, + {"s", 3, 0}, + {"dhpublicnumber", 1879048204, 0}, + {"iso", 1073741825, "1"}, + {"member-body", 1073741825, "2"}, + {"us", 1073741825, "840"}, + {"ansi-x942", 1073741825, "10046"}, + {"number-type", 1073741825, "2"}, + {0, 1, "1"}, + {"DomainParameters", 1610612741, 0}, + {"p", 1073741827, 0}, + {"g", 1073741827, 0}, + {"q", 1073741827, 0}, + {"j", 1073758211, 0}, + {"validationParms", 16386, "ValidationParms"}, + {"ValidationParms", 1610612741, 0}, + {"seed", 1073741830, 0}, + {"pgenCounter", 3, 0}, + {"id-dsa", 1879048204, 0}, + {"iso", 1073741825, "1"}, + {"member-body", 1073741825, "2"}, + {"us", 1073741825, "840"}, + {"x9-57", 1073741825, "10040"}, + {"x9algorithm", 1073741825, "4"}, + {0, 1, "1"}, + {"Dss-Parms", 1610612741, 0}, + {"p", 1073741827, 0}, + {"q", 1073741827, 0}, + {"g", 3, 0}, + {"ORAddress", 1610612741, 0}, + {"built-in-standard-attributes", 1073741826, "BuiltInStandardAttributes"}, + {"built-in-domain-defined-attributes", 1073758210, + "BuiltInDomainDefinedAttributes"}, + {"extension-attributes", 16386, "ExtensionAttributes"}, + {"BuiltInStandardAttributes", 1610612741, 0}, + {"country-name", 1073758210, "CountryName"}, + {"administration-domain-name", 1073758210, "AdministrationDomainName"}, + {"network-address", 1610637314, "NetworkAddress"}, + {0, 2056, "0"}, + {"terminal-identifier", 1610637314, "TerminalIdentifier"}, + {0, 2056, "1"}, + {"private-domain-name", 1610637314, "PrivateDomainName"}, + {0, 2056, "2"}, + {"organization-name", 1610637314, "OrganizationName"}, + {0, 2056, "3"}, + {"numeric-user-identifier", 1610637314, "NumericUserIdentifier"}, + {0, 2056, "4"}, + {"personal-name", 1610637314, "PersonalName"}, + {0, 2056, "5"}, + {"organizational-unit-names", 536895490, "OrganizationalUnitNames"}, + {0, 2056, "6"}, + {"CountryName", 1610620946, 0}, + {0, 1073746952, "1"}, + {"x121-dcc-code", 1612709890, "NumericString"}, + {0, 1048586, "ub-country-name-numeric-length"}, + {"iso-3166-alpha2-code", 538968066, "PrintableString"}, + {0, 1048586, "ub-country-name-alpha-length"}, + {"AdministrationDomainName", 1610620946, 0}, + {0, 1073744904, "2"}, + {"numeric", 1612709890, "NumericString"}, + {"ub-domain-name-length", 524298, "0"}, + {"printable", 538968066, "PrintableString"}, + {"ub-domain-name-length", 524298, "0"}, + {"NetworkAddress", 1073741826, "X121Address"}, + {"X121Address", 1612709890, "NumericString"}, + {"ub-x121-address-length", 524298, "1"}, + {"TerminalIdentifier", 1612709890, "PrintableString"}, + {"ub-terminal-id-length", 524298, "1"}, + {"PrivateDomainName", 1610612754, 0}, + {"numeric", 1612709890, "NumericString"}, + {"ub-domain-name-length", 524298, "1"}, + {"printable", 538968066, "PrintableString"}, + {"ub-domain-name-length", 524298, "1"}, + {"OrganizationName", 1612709890, "PrintableString"}, + {"ub-organization-name-length", 524298, "1"}, + {"NumericUserIdentifier", 1612709890, "NumericString"}, + {"ub-numeric-user-id-length", 524298, "1"}, + {"PersonalName", 1610612750, 0}, + {"surname", 1814044674, "PrintableString"}, + {0, 1073745928, "0"}, + {"ub-surname-length", 524298, "1"}, + {"given-name", 1814061058, "PrintableString"}, + {0, 1073745928, "1"}, + {"ub-given-name-length", 524298, "1"}, + {"initials", 1814061058, "PrintableString"}, + {0, 1073745928, "2"}, + {"ub-initials-length", 524298, "1"}, + {"generation-qualifier", 740319234, "PrintableString"}, + {0, 1073745928, "3"}, + {"ub-generation-qualifier-length", 524298, "1"}, + {"OrganizationalUnitNames", 1612709899, 0}, + {"ub-organizational-units", 1074266122, "1"}, + {0, 2, "OrganizationalUnitName"}, + {"OrganizationalUnitName", 1612709890, "PrintableString"}, + {"ub-organizational-unit-name-length", 524298, "1"}, + {"BuiltInDomainDefinedAttributes", 1612709899, 0}, + {"ub-domain-defined-attributes", 1074266122, "1"}, + {0, 2, "BuiltInDomainDefinedAttribute"}, + {"BuiltInDomainDefinedAttribute", 1610612741, 0}, + {"type", 1612709890, "PrintableString"}, + {"ub-domain-defined-attribute-type-length", 524298, "1"}, + {"value", 538968066, "PrintableString"}, + {"ub-domain-defined-attribute-value-length", 524298, "1"}, + {"ExtensionAttributes", 1612709903, 0}, + {"ub-extension-attributes", 1074266122, "1"}, + {0, 2, "ExtensionAttribute"}, + {"ExtensionAttribute", 1610612741, 0}, + {"extension-attribute-type", 1611145219, 0}, + {0, 1073743880, "0"}, + {"0", 10, "ub-extension-attributes"}, + {"extension-attribute-value", 541073421, 0}, + {0, 1073743880, "1"}, + {"extension-attribute-type", 1, 0}, + {"common-name", 1342177283, "1"}, + {"CommonName", 1612709890, "PrintableString"}, + {"ub-common-name-length", 524298, "1"}, + {"teletex-common-name", 1342177283, "2"}, + {"TeletexCommonName", 1612709890, "TeletexString"}, + {"ub-common-name-length", 524298, "1"}, + {"teletex-organization-name", 1342177283, "3"}, + {"TeletexOrganizationName", 1612709890, "TeletexString"}, + {"ub-organization-name-length", 524298, "1"}, + {"teletex-personal-name", 1342177283, "4"}, + {"TeletexPersonalName", 1610612750, 0}, + {"surname", 1814044674, "TeletexString"}, + {0, 1073743880, "0"}, + {"ub-surname-length", 524298, "1"}, + {"given-name", 1814061058, "TeletexString"}, + {0, 1073743880, "1"}, + {"ub-given-name-length", 524298, "1"}, + {"initials", 1814061058, "TeletexString"}, + {0, 1073743880, "2"}, + {"ub-initials-length", 524298, "1"}, + {"generation-qualifier", 740319234, "TeletexString"}, + {0, 1073743880, "3"}, + {"ub-generation-qualifier-length", 524298, "1"}, + {"teletex-organizational-unit-names", 1342177283, "5"}, + {"TeletexOrganizationalUnitNames", 1612709899, 0}, + {"ub-organizational-units", 1074266122, "1"}, + {0, 2, "TeletexOrganizationalUnitName"}, + {"TeletexOrganizationalUnitName", 1612709890, "TeletexString"}, + {"ub-organizational-unit-name-length", 524298, "1"}, + {"pds-name", 1342177283, "7"}, + {"PDSName", 1612709890, "PrintableString"}, + {"ub-pds-name-length", 524298, "1"}, + {"physical-delivery-country-name", 1342177283, "8"}, + {"PhysicalDeliveryCountryName", 1610612754, 0}, + {"x121-dcc-code", 1612709890, "NumericString"}, + {0, 1048586, "ub-country-name-numeric-length"}, + {"iso-3166-alpha2-code", 538968066, "PrintableString"}, + {0, 1048586, "ub-country-name-alpha-length"}, + {"postal-code", 1342177283, "9"}, + {"PostalCode", 1610612754, 0}, + {"numeric-code", 1612709890, "NumericString"}, + {"ub-postal-code-length", 524298, "1"}, + {"printable-code", 538968066, "PrintableString"}, + {"ub-postal-code-length", 524298, "1"}, + {"physical-delivery-office-name", 1342177283, "10"}, + {"PhysicalDeliveryOfficeName", 1073741826, "PDSParameter"}, + {"physical-delivery-office-number", 1342177283, "11"}, + {"PhysicalDeliveryOfficeNumber", 1073741826, "PDSParameter"}, + {"extension-OR-address-components", 1342177283, "12"}, + {"ExtensionORAddressComponents", 1073741826, "PDSParameter"}, + {"physical-delivery-personal-name", 1342177283, "13"}, + {"PhysicalDeliveryPersonalName", 1073741826, "PDSParameter"}, + {"physical-delivery-organization-name", 1342177283, "14"}, + {"PhysicalDeliveryOrganizationName", 1073741826, "PDSParameter"}, + {"extension-physical-delivery-address-components", 1342177283, "15"}, + {"ExtensionPhysicalDeliveryAddressComponents", 1073741826, "PDSParameter"}, + {"unformatted-postal-address", 1342177283, "16"}, + {"UnformattedPostalAddress", 1610612750, 0}, + {"printable-address", 1814052875, 0}, + {"ub-pds-physical-address-lines", 1074266122, "1"}, + {0, 538968066, "PrintableString"}, + {"ub-pds-parameter-length", 524298, "1"}, + {"teletex-string", 740311042, "TeletexString"}, + {"ub-unformatted-address-length", 524298, "1"}, + {"street-address", 1342177283, "17"}, + {"StreetAddress", 1073741826, "PDSParameter"}, + {"post-office-box-address", 1342177283, "18"}, + {"PostOfficeBoxAddress", 1073741826, "PDSParameter"}, + {"poste-restante-address", 1342177283, "19"}, + {"PosteRestanteAddress", 1073741826, "PDSParameter"}, + {"unique-postal-name", 1342177283, "20"}, + {"UniquePostalName", 1073741826, "PDSParameter"}, + {"local-postal-attributes", 1342177283, "21"}, + {"LocalPostalAttributes", 1073741826, "PDSParameter"}, + {"PDSParameter", 1610612750, 0}, + {"printable-string", 1814052866, "PrintableString"}, + {"ub-pds-parameter-length", 524298, "1"}, + {"teletex-string", 740311042, "TeletexString"}, + {"ub-pds-parameter-length", 524298, "1"}, + {"extended-network-address", 1342177283, "22"}, + {"ExtendedNetworkAddress", 1610612754, 0}, + {"e163-4-address", 1610612741, 0}, + {"number", 1612718082, "NumericString"}, + {0, 1073743880, "0"}, + {"ub-e163-4-number-length", 524298, "1"}, + {"sub-address", 538992642, "NumericString"}, + {0, 1073743880, "1"}, + {"ub-e163-4-sub-address-length", 524298, "1"}, + {"psap-address", 536879106, "PresentationAddress"}, + {0, 2056, "0"}, + {"PresentationAddress", 1610612741, 0}, + {"pSelector", 1610637319, 0}, + {0, 2056, "0"}, + {"sSelector", 1610637319, 0}, + {0, 2056, "1"}, + {"tSelector", 1610637319, 0}, + {0, 2056, "2"}, + {"nAddresses", 538976271, 0}, + {0, 1073743880, "3"}, + {"MAX", 1074266122, "1"}, + {0, 7, 0}, + {"terminal-type", 1342177283, "23"}, + {"TerminalType", 1610874883, 0}, + {"telex", 1073741825, "3"}, + {"teletex", 1073741825, "4"}, + {"g3-facsimile", 1073741825, "5"}, + {"g4-facsimile", 1073741825, "6"}, + {"ia5-terminal", 1073741825, "7"}, + {"videotex", 1, "8"}, + {"teletex-domain-defined-attributes", 1342177283, "6"}, + {"TeletexDomainDefinedAttributes", 1612709899, 0}, + {"ub-domain-defined-attributes", 1074266122, "1"}, + {0, 2, "TeletexDomainDefinedAttribute"}, + {"TeletexDomainDefinedAttribute", 1610612741, 0}, + {"type", 1612709890, "TeletexString"}, + {"ub-domain-defined-attribute-type-length", 524298, "1"}, + {"value", 538968066, "TeletexString"}, + {"ub-domain-defined-attribute-value-length", 524298, "1"}, + {"ub-name", 1342177283, "32768"}, + {"ub-common-name", 1342177283, "64"}, + {"ub-locality-name", 1342177283, "128"}, + {"ub-state-name", 1342177283, "128"}, + {"ub-organization-name", 1342177283, "64"}, + {"ub-organizational-unit-name", 1342177283, "64"}, + {"ub-title", 1342177283, "64"}, + {"ub-match", 1342177283, "128"}, + {"ub-emailaddress-length", 1342177283, "128"}, + {"ub-common-name-length", 1342177283, "64"}, + {"ub-country-name-alpha-length", 1342177283, "2"}, + {"ub-country-name-numeric-length", 1342177283, "3"}, + {"ub-domain-defined-attributes", 1342177283, "4"}, + {"ub-domain-defined-attribute-type-length", 1342177283, "8"}, + {"ub-domain-defined-attribute-value-length", 1342177283, "128"}, + {"ub-domain-name-length", 1342177283, "16"}, + {"ub-extension-attributes", 1342177283, "256"}, + {"ub-e163-4-number-length", 1342177283, "15"}, + {"ub-e163-4-sub-address-length", 1342177283, "40"}, + {"ub-generation-qualifier-length", 1342177283, "3"}, + {"ub-given-name-length", 1342177283, "16"}, + {"ub-initials-length", 1342177283, "5"}, + {"ub-integer-options", 1342177283, "256"}, + {"ub-numeric-user-id-length", 1342177283, "32"}, + {"ub-organization-name-length", 1342177283, "64"}, + {"ub-organizational-unit-name-length", 1342177283, "32"}, + {"ub-organizational-units", 1342177283, "4"}, + {"ub-pds-name-length", 1342177283, "16"}, + {"ub-pds-parameter-length", 1342177283, "30"}, + {"ub-pds-physical-address-lines", 1342177283, "6"}, + {"ub-postal-code-length", 1342177283, "16"}, + {"ub-surname-length", 1342177283, "40"}, + {"ub-terminal-id-length", 1342177283, "24"}, + {"ub-unformatted-address-length", 1342177283, "180"}, + {"ub-x121-address-length", 1342177283, "16"}, + {"pkcs-7-ContentInfo", 1610612741, 0}, + {"contentType", 1073741826, "pkcs-7-ContentType"}, + {"content", 541073421, 0}, + {0, 1073743880, "0"}, + {"contentType", 1, 0}, + {"pkcs-7-DigestInfo", 1610612741, 0}, + {"digestAlgorithm", 1073741826, "pkcs-7-DigestAlgorithmIdentifier"}, + {"digest", 2, "pkcs-7-Digest"}, + {"pkcs-7-Digest", 1073741831, 0}, + {"pkcs-7-ContentType", 1073741836, 0}, + {"pkcs-7-SignedData", 1610612741, 0}, + {"version", 1073741826, "pkcs-7-CMSVersion"}, + {"digestAlgorithms", 1073741826, "pkcs-7-DigestAlgorithmIdentifiers"}, + {"encapContentInfo", 1073741826, "pkcs-7-EncapsulatedContentInfo"}, + {"certificates", 1610637314, "pkcs-7-CertificateSet"}, + {0, 4104, "0"}, + {"crls", 1610637314, "pkcs-7-CertificateRevocationLists"}, + {0, 4104, "1"}, + {"signerInfos", 2, "pkcs-7-SignerInfos"}, + {"pkcs-7-CMSVersion", 1610874883, 0}, + {"v0", 1073741825, "0"}, + {"v1", 1073741825, "1"}, + {"v2", 1073741825, "2"}, + {"v3", 1073741825, "3"}, + {"v4", 1, "4"}, + {"pkcs-7-DigestAlgorithmIdentifiers", 1610612751, 0}, + {0, 2, "pkcs-7-DigestAlgorithmIdentifier"}, + {"pkcs-7-DigestAlgorithmIdentifier", 1073741826, "AlgorithmIdentifier"}, + {"pkcs-7-EncapsulatedContentInfo", 1610612741, 0}, + {"eContentType", 1073741826, "pkcs-7-ContentType"}, + {"eContent", 536895495, 0}, + {0, 2056, "0"}, + {"pkcs-7-CertificateRevocationLists", 1610612751, 0}, + {0, 13, 0}, + {"pkcs-7-CertificateChoices", 1610612754, 0}, + {"certificate", 13, 0}, + {"pkcs-7-CertificateSet", 1610612751, 0}, + {0, 2, "pkcs-7-CertificateChoices"}, + {"pkcs-7-SignerInfos", 1610612751, 0}, + {0, 13, 0}, + {"pkcs-10-CertificationRequestInfo", 1610612741, 0}, + {"version", 1610874883, 0}, + {"v1", 1, "0"}, + {"subject", 1073741826, "Name"}, + {"subjectPKInfo", 1073741826, "SubjectPublicKeyInfo"}, + {"attributes", 536879106, "Attributes"}, + {0, 4104, "0"}, + {"Attributes", 1610612751, 0}, + {0, 2, "Attribute"}, + {"pkcs-10-CertificationRequest", 1610612741, 0}, + {"certificationRequestInfo", 1073741826, "pkcs-10-CertificationRequestInfo"}, + {"signatureAlgorithm", 1073741826, "AlgorithmIdentifier"}, + {"signature", 6, 0}, + {"pkcs-9-ub-challengePassword", 1342177283, "255"}, + {"pkcs-9-certTypes", 1879048204, 0}, + {0, 1073741825, "pkcs-9"}, + {0, 1, "22"}, + {"pkcs-9-crlTypes", 1879048204, 0}, + {0, 1073741825, "pkcs-9"}, + {0, 1, "23"}, + {"pkcs-9-at-challengePassword", 1879048204, 0}, + {0, 1073741825, "pkcs-9"}, + {0, 1, "7"}, + {"pkcs-9-challengePassword", 1610612754, 0}, + {"printableString", 1612709890, "PrintableString"}, + {"pkcs-9-ub-challengePassword", 524298, "1"}, + {"utf8String", 538968066, "UTF8String"}, + {"pkcs-9-ub-challengePassword", 524298, "1"}, + {"pkcs-9-at-localKeyId", 1879048204, 0}, + {0, 1073741825, "pkcs-9"}, + {0, 1, "21"}, + {"pkcs-9-localKeyId", 1073741831, 0}, + {"pkcs-9-at-friendlyName", 1879048204, 0}, + {0, 1073741825, "pkcs-9"}, + {0, 1, "20"}, + {"pkcs-9-friendlyName", 1612709890, "BMPString"}, + {"255", 524298, "1"}, + {"pkcs-8-PrivateKeyInfo", 1610612741, 0}, + {"version", 1073741826, "pkcs-8-Version"}, + {"privateKeyAlgorithm", 1073741826, "AlgorithmIdentifier"}, + {"privateKey", 1073741826, "pkcs-8-PrivateKey"}, + {"attributes", 536895490, "Attributes"}, + {0, 4104, "0"}, + {"pkcs-8-Version", 1610874883, 0}, + {"v1", 1, "0"}, + {"pkcs-8-PrivateKey", 1073741831, 0}, + {"pkcs-8-Attributes", 1610612751, 0}, + {0, 2, "Attribute"}, + {"pkcs-8-EncryptedPrivateKeyInfo", 1610612741, 0}, + {"encryptionAlgorithm", 1073741826, "AlgorithmIdentifier"}, + {"encryptedData", 2, "pkcs-8-EncryptedData"}, + {"pkcs-8-EncryptedData", 1073741831, 0}, + {"pkcs-5", 1879048204, 0}, + {0, 1073741825, "pkcs"}, + {0, 1, "5"}, + {"pkcs-5-encryptionAlgorithm", 1879048204, 0}, + {"iso", 1073741825, "1"}, + {"member-body", 1073741825, "2"}, + {"us", 1073741825, "840"}, + {"rsadsi", 1073741825, "113549"}, + {0, 1, "3"}, + {"pkcs-5-des-EDE3-CBC", 1879048204, 0}, + {0, 1073741825, "pkcs-5-encryptionAlgorithm"}, + {0, 1, "7"}, + {"pkcs-5-des-EDE3-CBC-params", 1612709895, 0}, + {0, 1048586, "8"}, + {"pkcs-5-id-PBES2", 1879048204, 0}, + {0, 1073741825, "pkcs-5"}, + {0, 1, "13"}, + {"pkcs-5-PBES2-params", 1610612741, 0}, + {"keyDerivationFunc", 1073741826, "AlgorithmIdentifier"}, + {"encryptionScheme", 2, "AlgorithmIdentifier"}, + {"pkcs-5-id-PBKDF2", 1879048204, 0}, + {0, 1073741825, "pkcs-5"}, + {0, 1, "12"}, + {"pkcs-5-PBKDF2-params", 1610612741, 0}, + {"salt", 1610612754, 0}, + {"specified", 1073741831, 0}, + {"otherSource", 2, "AlgorithmIdentifier"}, + {"iterationCount", 1611137027, 0}, + {"1", 10, "MAX"}, + {"keyLength", 1611153411, 0}, + {"1", 10, "MAX"}, + {"prf", 16386, "AlgorithmIdentifier"}, + {"pkcs-12", 1879048204, 0}, + {0, 1073741825, "pkcs"}, + {0, 1, "12"}, + {"pkcs-12-PFX", 1610612741, 0}, + {"version", 1610874883, 0}, + {"v3", 1, "3"}, + {"authSafe", 1073741826, "pkcs-7-ContentInfo"}, + {"macData", 16386, "pkcs-12-MacData"}, + {"pkcs-12-PbeParams", 1610612741, 0}, + {"salt", 1073741831, 0}, + {"iterations", 3, 0}, + {"pkcs-12-MacData", 1610612741, 0}, + {"mac", 1073741826, "pkcs-7-DigestInfo"}, + {"macSalt", 1073741831, 0}, + {"iterations", 536903683, 0}, + {0, 9, "1"}, + {"pkcs-12-AuthenticatedSafe", 1610612747, 0}, + {0, 2, "pkcs-7-ContentInfo"}, + {"pkcs-12-SafeContents", 1610612747, 0}, + {0, 2, "pkcs-12-SafeBag"}, + {"pkcs-12-SafeBag", 1610612741, 0}, + {"bagId", 1073741836, 0}, + {"bagValue", 1614815245, 0}, + {0, 1073743880, "0"}, + {"badId", 1, 0}, + {"bagAttributes", 536887311, 0}, + {0, 2, "pkcs-12-PKCS12Attribute"}, + {"pkcs-12-bagtypes", 1879048204, 0}, + {0, 1073741825, "pkcs-12"}, + {0, 1073741825, "10"}, + {0, 1, "1"}, + {"pkcs-12-keyBag", 1879048204, 0}, + {0, 1073741825, "pkcs-12-bagtypes"}, + {0, 1, "1"}, + {"pkcs-12-pkcs8ShroudedKeyBag", 1879048204, 0}, + {0, 1073741825, "pkcs-12-bagtypes"}, + {0, 1, "2"}, + {"pkcs-12-certBag", 1879048204, 0}, + {0, 1073741825, "pkcs-12-bagtypes"}, + {0, 1, "3"}, + {"pkcs-12-crlBag", 1879048204, 0}, + {0, 1073741825, "pkcs-12-bagtypes"}, + {0, 1, "4"}, + {"pkcs-12-KeyBag", 1073741826, "pkcs-8-PrivateKeyInfo"}, + {"pkcs-12-PKCS8ShroudedKeyBag", 1073741826, "pkcs-8-EncryptedPrivateKeyInfo"}, + {"pkcs-12-CertBag", 1610612741, 0}, + {"certId", 1073741836, 0}, + {"certValue", 541073421, 0}, + {0, 1073743880, "0"}, + {"certId", 1, 0}, + {"pkcs-12-CRLBag", 1610612741, 0}, + {"crlId", 1073741836, 0}, + {"crlValue", 541073421, 0}, + {0, 1073743880, "0"}, + {"crlId", 1, 0}, + {"pkcs-12-PKCS12Attribute", 1073741826, "Attribute"}, + {"pkcs-7-data", 1879048204, 0}, + {"iso", 1073741825, "1"}, + {"member-body", 1073741825, "2"}, + {"us", 1073741825, "840"}, + {"rsadsi", 1073741825, "113549"}, + {"pkcs", 1073741825, "1"}, + {"pkcs7", 1073741825, "7"}, + {0, 1, "1"}, + {"pkcs-7-encryptedData", 1879048204, 0}, + {"iso", 1073741825, "1"}, + {"member-body", 1073741825, "2"}, + {"us", 1073741825, "840"}, + {"rsadsi", 1073741825, "113549"}, + {"pkcs", 1073741825, "1"}, + {"pkcs7", 1073741825, "7"}, + {0, 1, "6"}, + {"pkcs-7-Data", 1073741831, 0}, + {"pkcs-7-EncryptedData", 1610612741, 0}, + {"version", 1073741826, "pkcs-7-CMSVersion"}, + {"encryptedContentInfo", 1073741826, "pkcs-7-EncryptedContentInfo"}, + {"unprotectedAttrs", 536895490, "pkcs-7-UnprotectedAttributes"}, + {0, 4104, "1"}, + {"pkcs-7-EncryptedContentInfo", 1610612741, 0}, + {"contentType", 1073741826, "pkcs-7-ContentType"}, + {"contentEncryptionAlgorithm", 1073741826, + "pkcs-7-ContentEncryptionAlgorithmIdentifier"}, + {"encryptedContent", 536895490, "pkcs-7-EncryptedContent"}, + {0, 4104, "0"}, + {"pkcs-7-ContentEncryptionAlgorithmIdentifier", 1073741826, + "AlgorithmIdentifier"}, + {"pkcs-7-EncryptedContent", 1073741831, 0}, + {"pkcs-7-UnprotectedAttributes", 1612709903, 0}, + {"MAX", 1074266122, "1"}, + {0, 2, "Attribute"}, + {"id-at-ldap-DC", 1880096780, "AttributeType"}, + {0, 1073741825, "0"}, + {0, 1073741825, "9"}, + {0, 1073741825, "2342"}, + {0, 1073741825, "19200300"}, + {0, 1073741825, "100"}, + {0, 1073741825, "1"}, + {0, 1, "25"}, + {"ldap-DC", 1073741826, "IA5String"}, + {"id-at-ldap-UID", 1880096780, "AttributeType"}, + {0, 1073741825, "0"}, + {0, 1073741825, "9"}, + {0, 1073741825, "2342"}, + {0, 1073741825, "19200300"}, + {0, 1073741825, "100"}, + {0, 1073741825, "1"}, + {0, 1, "1"}, + {"ldap-UID", 1073741826, "DirectoryString"}, + {"id-pda", 1879048204, 0}, + {0, 1073741825, "id-pkix"}, + {0, 1, "9"}, + {"id-pda-dateOfBirth", 1880096780, "AttributeType"}, + {0, 1073741825, "id-pda"}, + {0, 1, "1"}, + {"DateOfBirth", 1082130449, 0}, + {"id-pda-placeOfBirth", 1880096780, "AttributeType"}, + {0, 1073741825, "id-pda"}, + {0, 1, "2"}, + {"PlaceOfBirth", 1073741826, "DirectoryString"}, + {"id-pda-gender", 1880096780, "AttributeType"}, + {0, 1073741825, "id-pda"}, + {0, 1, "3"}, + {"Gender", 1612709890, "PrintableString"}, + {0, 1048586, "1"}, + {"id-pda-countryOfCitizenship", 1880096780, "AttributeType"}, + {0, 1073741825, "id-pda"}, + {0, 1, "4"}, + {"CountryOfCitizenship", 1612709890, "PrintableString"}, + {0, 1048586, "2"}, + {"id-pda-countryOfResidence", 1880096780, "AttributeType"}, + {0, 1073741825, "id-pda"}, + {0, 1, "5"}, + {"CountryOfResidence", 538968066, "PrintableString"}, + {0, 1048586, "2"}, + {0, 0, 0} +}; +#endif /* QCRYPTO_HAVE_TLS_TEST_SUPPORT */ diff --git a/tests/unit/ptimer-test-stubs.c b/tests/unit/ptimer-test-stubs.c new file mode 100644 index 0000000000..7f801a4d09 --- /dev/null +++ b/tests/unit/ptimer-test-stubs.c @@ -0,0 +1,124 @@ +/* + * Stubs for the ptimer-test + * + * Copyright (c) 2016 Dmitry Osipenko + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "qemu/main-loop.h" +#include "sysemu/replay.h" +#include "migration/vmstate.h" +#include "sysemu/cpu-timers.h" + +#include "ptimer-test.h" + +const VMStateInfo vmstate_info_uint8; +const VMStateInfo vmstate_info_uint32; +const VMStateInfo vmstate_info_uint64; +const VMStateInfo vmstate_info_int64; +const VMStateInfo vmstate_info_timer; + +struct QEMUBH { + QEMUBHFunc *cb; + void *opaque; +}; + +QEMUTimerListGroup main_loop_tlg; + +int64_t ptimer_test_time_ns; + +/* under qtest_enabled(), will not artificially limit period - see hw/core/ptimer.c. */ +int use_icount; +bool qtest_allowed; + +void timer_init_full(QEMUTimer *ts, + QEMUTimerListGroup *timer_list_group, QEMUClockType type, + int scale, int attributes, + QEMUTimerCB *cb, void *opaque) +{ + if (!timer_list_group) { + timer_list_group = &main_loop_tlg; + } + ts->timer_list = timer_list_group->tl[type]; + ts->cb = cb; + ts->opaque = opaque; + ts->scale = scale; + ts->attributes = attributes; + ts->expire_time = -1; +} + +void timer_mod(QEMUTimer *ts, int64_t expire_time) +{ + QEMUTimerList *timer_list = ts->timer_list; + QEMUTimer *t = &timer_list->active_timers; + + while (t->next != NULL) { + if (t->next == ts) { + break; + } + + t = t->next; + } + + ts->expire_time = MAX(expire_time * ts->scale, 0); + ts->next = NULL; + t->next = ts; +} + +void timer_del(QEMUTimer *ts) +{ + QEMUTimerList *timer_list = ts->timer_list; + QEMUTimer *t = &timer_list->active_timers; + + while (t->next != NULL) { + if (t->next == ts) { + t->next = ts->next; + return; + } + + t = t->next; + } +} + +int64_t qemu_clock_get_ns(QEMUClockType type) +{ + return ptimer_test_time_ns; +} + +int64_t qemu_clock_deadline_ns_all(QEMUClockType type, int attr_mask) +{ + QEMUTimerList *timer_list = main_loop_tlg.tl[QEMU_CLOCK_VIRTUAL]; + QEMUTimer *t = timer_list->active_timers.next; + int64_t deadline = -1; + + while (t != NULL) { + if (deadline == -1) { + deadline = t->expire_time; + } else { + deadline = MIN(deadline, t->expire_time); + } + + t = t->next; + } + + return deadline; +} + +QEMUBH *qemu_bh_new(QEMUBHFunc *cb, void *opaque) +{ + QEMUBH *bh = g_new(QEMUBH, 1); + + bh->cb = cb; + bh->opaque = opaque; + + return bh; +} + +void qemu_bh_delete(QEMUBH *bh) +{ + g_free(bh); +} diff --git a/tests/unit/ptimer-test.c b/tests/unit/ptimer-test.c new file mode 100644 index 0000000000..9176b96c1c --- /dev/null +++ b/tests/unit/ptimer-test.c @@ -0,0 +1,892 @@ +/* + * QTest testcase for the ptimer + * + * Copyright (c) 2016 Dmitry Osipenko + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include + +#include "qemu/main-loop.h" +#include "hw/ptimer.h" + +#include "ptimer-test.h" + +static bool triggered; + +static void ptimer_trigger(void *opaque) +{ + triggered = true; +} + +static void ptimer_test_expire_qemu_timers(int64_t expire_time, + QEMUClockType type) +{ + QEMUTimerList *timer_list = main_loop_tlg.tl[type]; + QEMUTimer *t = timer_list->active_timers.next; + + while (t != NULL) { + if (t->expire_time == expire_time) { + timer_del(t); + + if (t->cb != NULL) { + t->cb(t->opaque); + } + } + + t = t->next; + } +} + +static void ptimer_test_set_qemu_time_ns(int64_t ns) +{ + ptimer_test_time_ns = ns; +} + +static void qemu_clock_step(uint64_t ns) +{ + int64_t deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL, + QEMU_TIMER_ATTR_ALL); + int64_t advanced_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + ns; + + while (deadline != -1 && deadline <= advanced_time) { + ptimer_test_set_qemu_time_ns(deadline); + ptimer_test_expire_qemu_timers(deadline, QEMU_CLOCK_VIRTUAL); + deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL, + QEMU_TIMER_ATTR_ALL); + } + + ptimer_test_set_qemu_time_ns(advanced_time); +} + +static void check_set_count(gconstpointer arg) +{ + const uint8_t *policy = arg; + ptimer_state *ptimer = ptimer_init(ptimer_trigger, NULL, *policy); + + triggered = false; + + ptimer_transaction_begin(ptimer); + ptimer_set_count(ptimer, 1000); + ptimer_transaction_commit(ptimer); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, 1000); + g_assert_false(triggered); + ptimer_free(ptimer); +} + +static void check_set_limit(gconstpointer arg) +{ + const uint8_t *policy = arg; + ptimer_state *ptimer = ptimer_init(ptimer_trigger, NULL, *policy); + + triggered = false; + + ptimer_transaction_begin(ptimer); + ptimer_set_limit(ptimer, 1000, 0); + ptimer_transaction_commit(ptimer); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); + g_assert_cmpuint(ptimer_get_limit(ptimer), ==, 1000); + g_assert_false(triggered); + + ptimer_transaction_begin(ptimer); + ptimer_set_limit(ptimer, 2000, 1); + ptimer_transaction_commit(ptimer); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, 2000); + g_assert_cmpuint(ptimer_get_limit(ptimer), ==, 2000); + g_assert_false(triggered); + ptimer_free(ptimer); +} + +static void check_oneshot(gconstpointer arg) +{ + const uint8_t *policy = arg; + ptimer_state *ptimer = ptimer_init(ptimer_trigger, NULL, *policy); + bool no_round_down = (*policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN); + + triggered = false; + + ptimer_transaction_begin(ptimer); + ptimer_set_period(ptimer, 2000000); + ptimer_set_count(ptimer, 10); + ptimer_run(ptimer, 1); + ptimer_transaction_commit(ptimer); + + qemu_clock_step(2000000 * 2 + 1); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 8 : 7); + g_assert_false(triggered); + + ptimer_transaction_begin(ptimer); + ptimer_stop(ptimer); + ptimer_transaction_commit(ptimer); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 8 : 7); + g_assert_false(triggered); + + qemu_clock_step(2000000 * 11); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 8 : 7); + g_assert_false(triggered); + + ptimer_transaction_begin(ptimer); + ptimer_run(ptimer, 1); + ptimer_transaction_commit(ptimer); + + qemu_clock_step(2000000 * 7 + 1); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 1 : 0); + + if (no_round_down) { + g_assert_false(triggered); + } else { + g_assert_true(triggered); + + triggered = false; + } + + qemu_clock_step(2000000); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); + + if (no_round_down) { + g_assert_true(triggered); + + triggered = false; + } else { + g_assert_false(triggered); + } + + qemu_clock_step(4000000); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); + g_assert_false(triggered); + + ptimer_transaction_begin(ptimer); + ptimer_set_count(ptimer, 10); + ptimer_transaction_commit(ptimer); + + qemu_clock_step(20000000 + 1); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, 10); + g_assert_false(triggered); + + ptimer_transaction_begin(ptimer); + ptimer_set_limit(ptimer, 9, 1); + ptimer_transaction_commit(ptimer); + + qemu_clock_step(20000000 + 1); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, 9); + g_assert_false(triggered); + + ptimer_transaction_begin(ptimer); + ptimer_run(ptimer, 1); + ptimer_transaction_commit(ptimer); + + qemu_clock_step(2000000 + 1); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 8 : 7); + g_assert_false(triggered); + + ptimer_transaction_begin(ptimer); + ptimer_set_count(ptimer, 20); + ptimer_transaction_commit(ptimer); + + qemu_clock_step(2000000 * 19 + 1); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 1 : 0); + g_assert_false(triggered); + + qemu_clock_step(2000000); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); + g_assert_true(triggered); + + ptimer_transaction_begin(ptimer); + ptimer_stop(ptimer); + ptimer_transaction_commit(ptimer); + + triggered = false; + + qemu_clock_step(2000000 * 12 + 1); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); + g_assert_false(triggered); + ptimer_free(ptimer); +} + +static void check_periodic(gconstpointer arg) +{ + const uint8_t *policy = arg; + ptimer_state *ptimer = ptimer_init(ptimer_trigger, NULL, *policy); + bool wrap_policy = (*policy & PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD); + bool no_immediate_trigger = (*policy & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER); + bool no_immediate_reload = (*policy & PTIMER_POLICY_NO_IMMEDIATE_RELOAD); + bool no_round_down = (*policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN); + bool trig_only_on_dec = (*policy & PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT); + + triggered = false; + + ptimer_transaction_begin(ptimer); + ptimer_set_period(ptimer, 2000000); + ptimer_set_limit(ptimer, 10, 1); + ptimer_run(ptimer, 0); + ptimer_transaction_commit(ptimer); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, 10); + g_assert_false(triggered); + + qemu_clock_step(1); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 10 : 9); + g_assert_false(triggered); + + qemu_clock_step(2000000 * 10 - 1); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, wrap_policy ? 0 : 10); + g_assert_true(triggered); + + qemu_clock_step(1); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, + wrap_policy ? 0 : (no_round_down ? 10 : 9)); + g_assert_true(triggered); + + triggered = false; + + qemu_clock_step(2000000); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, + (no_round_down ? 9 : 8) + (wrap_policy ? 1 : 0)); + g_assert_false(triggered); + + ptimer_transaction_begin(ptimer); + ptimer_set_count(ptimer, 20); + ptimer_transaction_commit(ptimer); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, 20); + g_assert_false(triggered); + + qemu_clock_step(1); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 20 : 19); + g_assert_false(triggered); + + qemu_clock_step(2000000 * 11 + 1); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 9 : 8); + g_assert_false(triggered); + + qemu_clock_step(2000000 * 10); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, + (no_round_down ? 9 : 8) + (wrap_policy ? 1 : 0)); + g_assert_true(triggered); + + triggered = false; + + ptimer_transaction_begin(ptimer); + ptimer_set_count(ptimer, 3); + ptimer_transaction_commit(ptimer); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, 3); + g_assert_false(triggered); + + qemu_clock_step(1); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 3 : 2); + g_assert_false(triggered); + + qemu_clock_step(2000000 * 4); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, + (no_round_down ? 9 : 8) + (wrap_policy ? 1 : 0)); + g_assert_true(triggered); + + ptimer_transaction_begin(ptimer); + ptimer_stop(ptimer); + ptimer_transaction_commit(ptimer); + triggered = false; + + qemu_clock_step(2000000); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, + (no_round_down ? 9 : 8) + (wrap_policy ? 1 : 0)); + g_assert_false(triggered); + + ptimer_transaction_begin(ptimer); + ptimer_set_count(ptimer, 3); + ptimer_run(ptimer, 0); + ptimer_transaction_commit(ptimer); + + qemu_clock_step(2000000 * 3 + 1); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, + wrap_policy ? 0 : (no_round_down ? 10 : 9)); + g_assert_true(triggered); + + triggered = false; + + qemu_clock_step(2000000); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, + (no_round_down ? 9 : 8) + (wrap_policy ? 1 : 0)); + g_assert_false(triggered); + + ptimer_transaction_begin(ptimer); + ptimer_set_count(ptimer, 0); + ptimer_transaction_commit(ptimer); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, + no_immediate_reload ? 0 : 10); + + if (no_immediate_trigger || trig_only_on_dec) { + g_assert_false(triggered); + } else { + g_assert_true(triggered); + } + + triggered = false; + + qemu_clock_step(1); + + if (no_immediate_reload) { + g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); + g_assert_false(triggered); + + qemu_clock_step(2000000); + + if (no_immediate_trigger) { + g_assert_true(triggered); + } else { + g_assert_false(triggered); + } + + triggered = false; + } + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 10 : 9); + g_assert_false(triggered); + + qemu_clock_step(2000000 * 12); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, + (no_round_down ? 8 : 7) + (wrap_policy ? 1 : 0)); + g_assert_true(triggered); + + ptimer_transaction_begin(ptimer); + ptimer_stop(ptimer); + ptimer_transaction_commit(ptimer); + + triggered = false; + + qemu_clock_step(2000000 * 10); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, + (no_round_down ? 8 : 7) + (wrap_policy ? 1 : 0)); + g_assert_false(triggered); + + ptimer_transaction_begin(ptimer); + ptimer_run(ptimer, 0); + ptimer_transaction_commit(ptimer); + + ptimer_transaction_begin(ptimer); + ptimer_set_period(ptimer, 0); + ptimer_transaction_commit(ptimer); + + qemu_clock_step(2000000 + 1); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, + (no_round_down ? 8 : 7) + (wrap_policy ? 1 : 0)); + g_assert_false(triggered); + ptimer_free(ptimer); +} + +static void check_on_the_fly_mode_change(gconstpointer arg) +{ + const uint8_t *policy = arg; + ptimer_state *ptimer = ptimer_init(ptimer_trigger, NULL, *policy); + bool wrap_policy = (*policy & PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD); + bool no_round_down = (*policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN); + + triggered = false; + + ptimer_transaction_begin(ptimer); + ptimer_set_period(ptimer, 2000000); + ptimer_set_limit(ptimer, 10, 1); + ptimer_run(ptimer, 1); + ptimer_transaction_commit(ptimer); + + qemu_clock_step(2000000 * 9 + 1); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 1 : 0); + g_assert_false(triggered); + + ptimer_transaction_begin(ptimer); + ptimer_run(ptimer, 0); + ptimer_transaction_commit(ptimer); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 1 : 0); + g_assert_false(triggered); + + qemu_clock_step(2000000); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, + wrap_policy ? 0 : (no_round_down ? 10 : 9)); + g_assert_true(triggered); + + triggered = false; + + qemu_clock_step(2000000 * 9); + + ptimer_transaction_begin(ptimer); + ptimer_run(ptimer, 1); + ptimer_transaction_commit(ptimer); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, + (no_round_down ? 1 : 0) + (wrap_policy ? 1 : 0)); + g_assert_false(triggered); + + qemu_clock_step(2000000 * 3); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); + g_assert_true(triggered); + ptimer_free(ptimer); +} + +static void check_on_the_fly_period_change(gconstpointer arg) +{ + const uint8_t *policy = arg; + ptimer_state *ptimer = ptimer_init(ptimer_trigger, NULL, *policy); + bool no_round_down = (*policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN); + + triggered = false; + + ptimer_transaction_begin(ptimer); + ptimer_set_period(ptimer, 2000000); + ptimer_set_limit(ptimer, 8, 1); + ptimer_run(ptimer, 1); + ptimer_transaction_commit(ptimer); + + qemu_clock_step(2000000 * 4 + 1); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 4 : 3); + g_assert_false(triggered); + + ptimer_transaction_begin(ptimer); + ptimer_set_period(ptimer, 4000000); + ptimer_transaction_commit(ptimer); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 4 : 3); + + qemu_clock_step(4000000 * 2 + 1); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 2 : 0); + g_assert_false(triggered); + + qemu_clock_step(4000000 * 2); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); + g_assert_true(triggered); + ptimer_free(ptimer); +} + +static void check_on_the_fly_freq_change(gconstpointer arg) +{ + const uint8_t *policy = arg; + ptimer_state *ptimer = ptimer_init(ptimer_trigger, NULL, *policy); + bool no_round_down = (*policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN); + + triggered = false; + + ptimer_transaction_begin(ptimer); + ptimer_set_freq(ptimer, 500); + ptimer_set_limit(ptimer, 8, 1); + ptimer_run(ptimer, 1); + ptimer_transaction_commit(ptimer); + + qemu_clock_step(2000000 * 4 + 1); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 4 : 3); + g_assert_false(triggered); + + ptimer_transaction_begin(ptimer); + ptimer_set_freq(ptimer, 250); + ptimer_transaction_commit(ptimer); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 4 : 3); + + qemu_clock_step(2000000 * 4 + 1); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 2 : 0); + g_assert_false(triggered); + + qemu_clock_step(2000000 * 4); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); + g_assert_true(triggered); + ptimer_free(ptimer); +} + +static void check_run_with_period_0(gconstpointer arg) +{ + const uint8_t *policy = arg; + ptimer_state *ptimer = ptimer_init(ptimer_trigger, NULL, *policy); + + triggered = false; + + ptimer_transaction_begin(ptimer); + ptimer_set_count(ptimer, 99); + ptimer_run(ptimer, 1); + ptimer_transaction_commit(ptimer); + + qemu_clock_step(10 * NANOSECONDS_PER_SECOND); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, 99); + g_assert_false(triggered); + ptimer_free(ptimer); +} + +static void check_run_with_delta_0(gconstpointer arg) +{ + const uint8_t *policy = arg; + ptimer_state *ptimer = ptimer_init(ptimer_trigger, NULL, *policy); + bool wrap_policy = (*policy & PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD); + bool no_immediate_trigger = (*policy & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER); + bool no_immediate_reload = (*policy & PTIMER_POLICY_NO_IMMEDIATE_RELOAD); + bool no_round_down = (*policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN); + bool trig_only_on_dec = (*policy & PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT); + + triggered = false; + + ptimer_transaction_begin(ptimer); + ptimer_set_period(ptimer, 2000000); + ptimer_set_limit(ptimer, 99, 0); + ptimer_run(ptimer, 1); + ptimer_transaction_commit(ptimer); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, + no_immediate_reload ? 0 : 99); + + if (no_immediate_trigger || trig_only_on_dec) { + g_assert_false(triggered); + } else { + g_assert_true(triggered); + } + + triggered = false; + + if (no_immediate_trigger || no_immediate_reload) { + qemu_clock_step(2000000 + 1); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, + no_immediate_reload ? 0 : (no_round_down ? 98 : 97)); + + if (no_immediate_trigger && no_immediate_reload) { + g_assert_true(triggered); + + triggered = false; + } else { + g_assert_false(triggered); + } + + ptimer_transaction_begin(ptimer); + ptimer_set_count(ptimer, 99); + ptimer_run(ptimer, 1); + ptimer_transaction_commit(ptimer); + } + + qemu_clock_step(2000000 + 1); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 98 : 97); + g_assert_false(triggered); + + qemu_clock_step(2000000 * 97); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 1 : 0); + g_assert_false(triggered); + + qemu_clock_step(2000000 * 2); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); + g_assert_true(triggered); + + triggered = false; + + ptimer_transaction_begin(ptimer); + ptimer_set_count(ptimer, 0); + ptimer_run(ptimer, 0); + ptimer_transaction_commit(ptimer); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, + no_immediate_reload ? 0 : 99); + + if (no_immediate_trigger || trig_only_on_dec) { + g_assert_false(triggered); + } else { + g_assert_true(triggered); + } + + triggered = false; + + qemu_clock_step(1); + + if (no_immediate_reload) { + qemu_clock_step(2000000); + } + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 99 : 98); + + if (no_immediate_reload && no_immediate_trigger) { + g_assert_true(triggered); + } else { + g_assert_false(triggered); + } + + triggered = false; + + qemu_clock_step(2000000); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 98 : 97); + g_assert_false(triggered); + + qemu_clock_step(2000000 * 98); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, + wrap_policy ? 0 : (no_round_down ? 99 : 98)); + g_assert_true(triggered); + + ptimer_transaction_begin(ptimer); + ptimer_stop(ptimer); + ptimer_transaction_commit(ptimer); + ptimer_free(ptimer); +} + +static void check_periodic_with_load_0(gconstpointer arg) +{ + const uint8_t *policy = arg; + ptimer_state *ptimer = ptimer_init(ptimer_trigger, NULL, *policy); + bool continuous_trigger = (*policy & PTIMER_POLICY_CONTINUOUS_TRIGGER); + bool no_immediate_trigger = (*policy & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER); + bool trig_only_on_dec = (*policy & PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT); + + triggered = false; + + ptimer_transaction_begin(ptimer); + ptimer_set_period(ptimer, 2000000); + ptimer_run(ptimer, 0); + ptimer_transaction_commit(ptimer); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); + + if (no_immediate_trigger || trig_only_on_dec) { + g_assert_false(triggered); + } else { + g_assert_true(triggered); + } + + triggered = false; + + qemu_clock_step(2000000 + 1); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); + + if (continuous_trigger || no_immediate_trigger) { + g_assert_true(triggered); + } else { + g_assert_false(triggered); + } + + triggered = false; + + ptimer_transaction_begin(ptimer); + ptimer_set_count(ptimer, 10); + ptimer_run(ptimer, 0); + ptimer_transaction_commit(ptimer); + + qemu_clock_step(2000000 * 10 + 1); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); + g_assert_true(triggered); + + triggered = false; + + qemu_clock_step(2000000 + 1); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); + + if (continuous_trigger) { + g_assert_true(triggered); + } else { + g_assert_false(triggered); + } + + ptimer_transaction_begin(ptimer); + ptimer_stop(ptimer); + ptimer_transaction_commit(ptimer); + ptimer_free(ptimer); +} + +static void check_oneshot_with_load_0(gconstpointer arg) +{ + const uint8_t *policy = arg; + ptimer_state *ptimer = ptimer_init(ptimer_trigger, NULL, *policy); + bool no_immediate_trigger = (*policy & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER); + bool trig_only_on_dec = (*policy & PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT); + + triggered = false; + + ptimer_transaction_begin(ptimer); + ptimer_set_period(ptimer, 2000000); + ptimer_run(ptimer, 1); + ptimer_transaction_commit(ptimer); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); + + if (no_immediate_trigger || trig_only_on_dec) { + g_assert_false(triggered); + } else { + g_assert_true(triggered); + } + + triggered = false; + + qemu_clock_step(2000000 + 1); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); + + if (no_immediate_trigger) { + g_assert_true(triggered); + } else { + g_assert_false(triggered); + } + + ptimer_free(ptimer); +} + +static void add_ptimer_tests(uint8_t policy) +{ + char policy_name[256] = ""; + char *tmp; + + if (policy == PTIMER_POLICY_DEFAULT) { + g_sprintf(policy_name, "default"); + } + + if (policy & PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD) { + g_strlcat(policy_name, "wrap_after_one_period,", 256); + } + + if (policy & PTIMER_POLICY_CONTINUOUS_TRIGGER) { + g_strlcat(policy_name, "continuous_trigger,", 256); + } + + if (policy & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER) { + g_strlcat(policy_name, "no_immediate_trigger,", 256); + } + + if (policy & PTIMER_POLICY_NO_IMMEDIATE_RELOAD) { + g_strlcat(policy_name, "no_immediate_reload,", 256); + } + + if (policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN) { + g_strlcat(policy_name, "no_counter_rounddown,", 256); + } + + if (policy & PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT) { + g_strlcat(policy_name, "trigger_only_on_decrement,", 256); + } + + g_test_add_data_func_full( + tmp = g_strdup_printf("/ptimer/set_count policy=%s", policy_name), + g_memdup(&policy, 1), check_set_count, g_free); + g_free(tmp); + + g_test_add_data_func_full( + tmp = g_strdup_printf("/ptimer/set_limit policy=%s", policy_name), + g_memdup(&policy, 1), check_set_limit, g_free); + g_free(tmp); + + g_test_add_data_func_full( + tmp = g_strdup_printf("/ptimer/oneshot policy=%s", policy_name), + g_memdup(&policy, 1), check_oneshot, g_free); + g_free(tmp); + + g_test_add_data_func_full( + tmp = g_strdup_printf("/ptimer/periodic policy=%s", policy_name), + g_memdup(&policy, 1), check_periodic, g_free); + g_free(tmp); + + g_test_add_data_func_full( + tmp = g_strdup_printf("/ptimer/on_the_fly_mode_change policy=%s", + policy_name), + g_memdup(&policy, 1), check_on_the_fly_mode_change, g_free); + g_free(tmp); + + g_test_add_data_func_full( + tmp = g_strdup_printf("/ptimer/on_the_fly_period_change policy=%s", + policy_name), + g_memdup(&policy, 1), check_on_the_fly_period_change, g_free); + g_free(tmp); + + g_test_add_data_func_full( + tmp = g_strdup_printf("/ptimer/on_the_fly_freq_change policy=%s", + policy_name), + g_memdup(&policy, 1), check_on_the_fly_freq_change, g_free); + g_free(tmp); + + g_test_add_data_func_full( + tmp = g_strdup_printf("/ptimer/run_with_period_0 policy=%s", + policy_name), + g_memdup(&policy, 1), check_run_with_period_0, g_free); + g_free(tmp); + + g_test_add_data_func_full( + tmp = g_strdup_printf("/ptimer/run_with_delta_0 policy=%s", + policy_name), + g_memdup(&policy, 1), check_run_with_delta_0, g_free); + g_free(tmp); + + g_test_add_data_func_full( + tmp = g_strdup_printf("/ptimer/periodic_with_load_0 policy=%s", + policy_name), + g_memdup(&policy, 1), check_periodic_with_load_0, g_free); + g_free(tmp); + + g_test_add_data_func_full( + tmp = g_strdup_printf("/ptimer/oneshot_with_load_0 policy=%s", + policy_name), + g_memdup(&policy, 1), check_oneshot_with_load_0, g_free); + g_free(tmp); +} + +static void add_all_ptimer_policies_comb_tests(void) +{ + int last_policy = PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT; + int policy = PTIMER_POLICY_DEFAULT; + + for (; policy < (last_policy << 1); policy++) { + if ((policy & PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT) && + (policy & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER)) { + /* Incompatible policy flag settings -- don't try to test them */ + continue; + } + add_ptimer_tests(policy); + } +} + +int main(int argc, char **argv) +{ + int i; + + g_test_init(&argc, &argv, NULL); + + for (i = 0; i < QEMU_CLOCK_MAX; i++) { + main_loop_tlg.tl[i] = g_new0(QEMUTimerList, 1); + } + + add_all_ptimer_policies_comb_tests(); + + qtest_allowed = true; + + return g_test_run(); +} diff --git a/tests/unit/ptimer-test.h b/tests/unit/ptimer-test.h new file mode 100644 index 0000000000..09ac56da9e --- /dev/null +++ b/tests/unit/ptimer-test.h @@ -0,0 +1,22 @@ +/* + * QTest testcase for the ptimer + * + * Copyright (c) 2016 Dmitry Osipenko + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#ifndef PTIMER_TEST_H +#define PTIMER_TEST_H + +extern bool qtest_allowed; + +extern int64_t ptimer_test_time_ns; + +struct QEMUTimerList { + QEMUTimer active_timers; +}; + +#endif diff --git a/tests/unit/rcutorture.c b/tests/unit/rcutorture.c new file mode 100644 index 0000000000..de6f649058 --- /dev/null +++ b/tests/unit/rcutorture.c @@ -0,0 +1,483 @@ +/* + * rcutorture.c: simple user-level performance/stress test of RCU. + * + * Usage: + * ./rcu rperf [ ] + * Run a read-side performance test with the specified + * number of readers for seconds. + * ./rcu uperf [ ] + * Run an update-side performance test with the specified + * number of updaters and specified duration. + * ./rcu perf [ ] + * Run a combined read/update performance test with the specified + * number of readers and one updater and specified duration. + * + * The above tests produce output as follows: + * + * n_reads: 46008000 n_updates: 146026 nreaders: 2 nupdaters: 1 duration: 1 + * ns/read: 43.4707 ns/update: 6848.1 + * + * The first line lists the total number of RCU reads and updates executed + * during the test, the number of reader threads, the number of updater + * threads, and the duration of the test in seconds. The second line + * lists the average duration of each type of operation in nanoseconds, + * or "nan" if the corresponding type of operation was not performed. + * + * ./rcu stress [ ] + * Run a stress test with the specified number of readers and + * one updater. + * + * This test produces output as follows: + * + * n_reads: 114633217 n_updates: 3903415 n_mberror: 0 + * rcu_stress_count: 114618391 14826 0 0 0 0 0 0 0 0 0 + * + * The first line lists the number of RCU read and update operations + * executed, followed by the number of memory-ordering violations + * (which will be zero in a correct RCU implementation). The second + * line lists the number of readers observing progressively more stale + * data. A correct RCU implementation will have all but the first two + * numbers non-zero. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (c) 2008 Paul E. McKenney, IBM Corporation. + */ + +/* + * Test variables. + */ + +#include "qemu/osdep.h" +#include "qemu/atomic.h" +#include "qemu/rcu.h" +#include "qemu/thread.h" + +int nthreadsrunning; + +#define GOFLAG_INIT 0 +#define GOFLAG_RUN 1 +#define GOFLAG_STOP 2 + +static volatile int goflag = GOFLAG_INIT; + +#define RCU_READ_RUN 1000 + +#define NR_THREADS 100 +static QemuThread threads[NR_THREADS]; +static struct rcu_reader_data *data[NR_THREADS]; +static int n_threads; + +/* + * Statistical counts + * + * These are the sum of local counters at the end of a run. + * Updates are protected by a mutex. + */ +static QemuMutex counts_mutex; +long long n_reads = 0LL; +long n_updates = 0L; + +static void create_thread(void *(*func)(void *)) +{ + if (n_threads >= NR_THREADS) { + fprintf(stderr, "Thread limit of %d exceeded!\n", NR_THREADS); + exit(-1); + } + qemu_thread_create(&threads[n_threads], "test", func, &data[n_threads], + QEMU_THREAD_JOINABLE); + n_threads++; +} + +static void wait_all_threads(void) +{ + int i; + + for (i = 0; i < n_threads; i++) { + qemu_thread_join(&threads[i]); + } + n_threads = 0; +} + +/* + * Performance test. + */ + +static void *rcu_read_perf_test(void *arg) +{ + int i; + long long n_reads_local = 0; + + rcu_register_thread(); + + *(struct rcu_reader_data **)arg = &rcu_reader; + qatomic_inc(&nthreadsrunning); + while (goflag == GOFLAG_INIT) { + g_usleep(1000); + } + while (goflag == GOFLAG_RUN) { + for (i = 0; i < RCU_READ_RUN; i++) { + rcu_read_lock(); + rcu_read_unlock(); + } + n_reads_local += RCU_READ_RUN; + } + qemu_mutex_lock(&counts_mutex); + n_reads += n_reads_local; + qemu_mutex_unlock(&counts_mutex); + + rcu_unregister_thread(); + return NULL; +} + +static void *rcu_update_perf_test(void *arg) +{ + long long n_updates_local = 0; + + rcu_register_thread(); + + *(struct rcu_reader_data **)arg = &rcu_reader; + qatomic_inc(&nthreadsrunning); + while (goflag == GOFLAG_INIT) { + g_usleep(1000); + } + while (goflag == GOFLAG_RUN) { + synchronize_rcu(); + n_updates_local++; + } + qemu_mutex_lock(&counts_mutex); + n_updates += n_updates_local; + qemu_mutex_unlock(&counts_mutex); + + rcu_unregister_thread(); + return NULL; +} + +static void perftestinit(void) +{ + nthreadsrunning = 0; +} + +static void perftestrun(int nthreads, int duration, int nreaders, int nupdaters) +{ + while (qatomic_read(&nthreadsrunning) < nthreads) { + g_usleep(1000); + } + goflag = GOFLAG_RUN; + g_usleep(duration * G_USEC_PER_SEC); + goflag = GOFLAG_STOP; + wait_all_threads(); + printf("n_reads: %lld n_updates: %ld nreaders: %d nupdaters: %d duration: %d\n", + n_reads, n_updates, nreaders, nupdaters, duration); + printf("ns/read: %g ns/update: %g\n", + ((duration * 1000*1000*1000.*(double)nreaders) / + (double)n_reads), + ((duration * 1000*1000*1000.*(double)nupdaters) / + (double)n_updates)); + exit(0); +} + +static void perftest(int nreaders, int duration) +{ + int i; + + perftestinit(); + for (i = 0; i < nreaders; i++) { + create_thread(rcu_read_perf_test); + } + create_thread(rcu_update_perf_test); + perftestrun(i + 1, duration, nreaders, 1); +} + +static void rperftest(int nreaders, int duration) +{ + int i; + + perftestinit(); + for (i = 0; i < nreaders; i++) { + create_thread(rcu_read_perf_test); + } + perftestrun(i, duration, nreaders, 0); +} + +static void uperftest(int nupdaters, int duration) +{ + int i; + + perftestinit(); + for (i = 0; i < nupdaters; i++) { + create_thread(rcu_update_perf_test); + } + perftestrun(i, duration, 0, nupdaters); +} + +/* + * Stress test. + */ + +#define RCU_STRESS_PIPE_LEN 10 + +struct rcu_stress { + int age; /* how many update cycles while not rcu_stress_current */ + int mbtest; +}; + +struct rcu_stress rcu_stress_array[RCU_STRESS_PIPE_LEN] = { { 0 } }; +struct rcu_stress *rcu_stress_current; +int n_mberror; + +/* Updates protected by counts_mutex */ +long long rcu_stress_count[RCU_STRESS_PIPE_LEN + 1]; + + +static void *rcu_read_stress_test(void *arg) +{ + int i; + struct rcu_stress *p; + int pc; + long long n_reads_local = 0; + long long rcu_stress_local[RCU_STRESS_PIPE_LEN + 1] = { 0 }; + volatile int garbage = 0; + + rcu_register_thread(); + + *(struct rcu_reader_data **)arg = &rcu_reader; + while (goflag == GOFLAG_INIT) { + g_usleep(1000); + } + while (goflag == GOFLAG_RUN) { + rcu_read_lock(); + p = qatomic_rcu_read(&rcu_stress_current); + if (qatomic_read(&p->mbtest) == 0) { + n_mberror++; + } + rcu_read_lock(); + for (i = 0; i < 100; i++) { + garbage++; + } + rcu_read_unlock(); + pc = qatomic_read(&p->age); + rcu_read_unlock(); + if ((pc > RCU_STRESS_PIPE_LEN) || (pc < 0)) { + pc = RCU_STRESS_PIPE_LEN; + } + rcu_stress_local[pc]++; + n_reads_local++; + } + qemu_mutex_lock(&counts_mutex); + n_reads += n_reads_local; + for (i = 0; i <= RCU_STRESS_PIPE_LEN; i++) { + rcu_stress_count[i] += rcu_stress_local[i]; + } + qemu_mutex_unlock(&counts_mutex); + + rcu_unregister_thread(); + return NULL; +} + +/* + * Stress Test Updater + * + * The updater cycles around updating rcu_stress_current to point at + * one of the rcu_stress_array_entries and resets it's age. It + * then increments the age of all the other entries. The age + * will be read under an rcu_read_lock() and distribution of values + * calculated. The final result gives an indication of how many + * previously current rcu_stress entries are in flight until the RCU + * cycle complete. + */ +static void *rcu_update_stress_test(void *arg) +{ + int i, rcu_stress_idx = 0; + struct rcu_stress *cp = qatomic_read(&rcu_stress_current); + + rcu_register_thread(); + *(struct rcu_reader_data **)arg = &rcu_reader; + + while (goflag == GOFLAG_INIT) { + g_usleep(1000); + } + + while (goflag == GOFLAG_RUN) { + struct rcu_stress *p; + rcu_stress_idx++; + if (rcu_stress_idx >= RCU_STRESS_PIPE_LEN) { + rcu_stress_idx = 0; + } + p = &rcu_stress_array[rcu_stress_idx]; + /* catching up with ourselves would be a bug */ + assert(p != cp); + qatomic_set(&p->mbtest, 0); + smp_mb(); + qatomic_set(&p->age, 0); + qatomic_set(&p->mbtest, 1); + qatomic_rcu_set(&rcu_stress_current, p); + cp = p; + /* + * New RCU structure is now live, update pipe counts on old + * ones. + */ + for (i = 0; i < RCU_STRESS_PIPE_LEN; i++) { + if (i != rcu_stress_idx) { + qatomic_set(&rcu_stress_array[i].age, + rcu_stress_array[i].age + 1); + } + } + synchronize_rcu(); + n_updates++; + } + + rcu_unregister_thread(); + return NULL; +} + +static void *rcu_fake_update_stress_test(void *arg) +{ + rcu_register_thread(); + + *(struct rcu_reader_data **)arg = &rcu_reader; + while (goflag == GOFLAG_INIT) { + g_usleep(1000); + } + while (goflag == GOFLAG_RUN) { + synchronize_rcu(); + g_usleep(1000); + } + + rcu_unregister_thread(); + return NULL; +} + +static void stresstest(int nreaders, int duration) +{ + int i; + + rcu_stress_current = &rcu_stress_array[0]; + rcu_stress_current->age = 0; + rcu_stress_current->mbtest = 1; + for (i = 0; i < nreaders; i++) { + create_thread(rcu_read_stress_test); + } + create_thread(rcu_update_stress_test); + for (i = 0; i < 5; i++) { + create_thread(rcu_fake_update_stress_test); + } + goflag = GOFLAG_RUN; + g_usleep(duration * G_USEC_PER_SEC); + goflag = GOFLAG_STOP; + wait_all_threads(); + printf("n_reads: %lld n_updates: %ld n_mberror: %d\n", + n_reads, n_updates, n_mberror); + printf("rcu_stress_count:"); + for (i = 0; i <= RCU_STRESS_PIPE_LEN; i++) { + printf(" %lld", rcu_stress_count[i]); + } + printf("\n"); + exit(0); +} + +/* GTest interface */ + +static void gtest_stress(int nreaders, int duration) +{ + int i; + + rcu_stress_current = &rcu_stress_array[0]; + rcu_stress_current->age = 0; + rcu_stress_current->mbtest = 1; + for (i = 0; i < nreaders; i++) { + create_thread(rcu_read_stress_test); + } + create_thread(rcu_update_stress_test); + for (i = 0; i < 5; i++) { + create_thread(rcu_fake_update_stress_test); + } + goflag = GOFLAG_RUN; + g_usleep(duration * G_USEC_PER_SEC); + goflag = GOFLAG_STOP; + wait_all_threads(); + g_assert_cmpint(n_mberror, ==, 0); + for (i = 2; i <= RCU_STRESS_PIPE_LEN; i++) { + g_assert_cmpint(rcu_stress_count[i], ==, 0); + } +} + +static void gtest_stress_1_1(void) +{ + gtest_stress(1, 1); +} + +static void gtest_stress_10_1(void) +{ + gtest_stress(10, 1); +} + +static void gtest_stress_1_5(void) +{ + gtest_stress(1, 5); +} + +static void gtest_stress_10_5(void) +{ + gtest_stress(10, 5); +} + +/* + * Mainprogram. + */ + +static void usage(int argc, char *argv[]) +{ + fprintf(stderr, "Usage: %s [nreaders [ [r|u]perf | stress [duration]]\n", + argv[0]); + exit(-1); +} + +int main(int argc, char *argv[]) +{ + int nreaders = 1; + int duration = 1; + + qemu_mutex_init(&counts_mutex); + if (argc >= 2 && argv[1][0] == '-') { + g_test_init(&argc, &argv, NULL); + if (g_test_quick()) { + g_test_add_func("/rcu/torture/1reader", gtest_stress_1_1); + g_test_add_func("/rcu/torture/10readers", gtest_stress_10_1); + } else { + g_test_add_func("/rcu/torture/1reader", gtest_stress_1_5); + g_test_add_func("/rcu/torture/10readers", gtest_stress_10_5); + } + return g_test_run(); + } + + if (argc >= 2) { + nreaders = strtoul(argv[1], NULL, 0); + } + if (argc > 3) { + duration = strtoul(argv[3], NULL, 0); + } + if (argc < 3 || strcmp(argv[2], "stress") == 0) { + stresstest(nreaders, duration); + } else if (strcmp(argv[2], "rperf") == 0) { + rperftest(nreaders, duration); + } else if (strcmp(argv[2], "uperf") == 0) { + uperftest(nreaders, duration); + } else if (strcmp(argv[2], "perf") == 0) { + perftest(nreaders, duration); + } + usage(argc, argv); + return 0; +} diff --git a/tests/unit/socket-helpers.c b/tests/unit/socket-helpers.c new file mode 100644 index 0000000000..f704fd1a69 --- /dev/null +++ b/tests/unit/socket-helpers.c @@ -0,0 +1,157 @@ +/* + * Helper functions for tests using sockets + * + * Copyright 2015-2018 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "qemu/sockets.h" +#include "socket-helpers.h" + +#ifndef AI_ADDRCONFIG +# define AI_ADDRCONFIG 0 +#endif +#ifndef EAI_ADDRFAMILY +# define EAI_ADDRFAMILY 0 +#endif + +/* + * @hostname: a DNS name or numeric IP address + * + * Check whether it is possible to bind & connect to ports + * on the DNS name or IP address @hostname. If an IP address + * is used, it must not be a wildcard address. + * + * Returns 0 on success, -1 on error with errno set + */ +static int socket_can_bind_connect(const char *hostname, int family) +{ + int lfd = -1, cfd = -1, afd = -1; + struct addrinfo ai, *res = NULL; + struct sockaddr_storage ss; + socklen_t sslen = sizeof(ss); + int soerr; + socklen_t soerrlen = sizeof(soerr); + bool check_soerr = false; + int rc; + int ret = -1; + + memset(&ai, 0, sizeof(ai)); + ai.ai_flags = AI_CANONNAME | AI_ADDRCONFIG; + ai.ai_family = family; + ai.ai_socktype = SOCK_STREAM; + + /* lookup */ + rc = getaddrinfo(hostname, NULL, &ai, &res); + if (rc != 0) { + if (rc == EAI_ADDRFAMILY || rc == EAI_FAMILY || rc == EAI_NONAME) { + errno = EADDRNOTAVAIL; + } else { + errno = EINVAL; + } + goto cleanup; + } + + lfd = qemu_socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (lfd < 0) { + goto cleanup; + } + + cfd = qemu_socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (cfd < 0) { + goto cleanup; + } + + if (bind(lfd, res->ai_addr, res->ai_addrlen) < 0) { + goto cleanup; + } + + if (listen(lfd, 1) < 0) { + goto cleanup; + } + + if (getsockname(lfd, (struct sockaddr *)&ss, &sslen) < 0) { + goto cleanup; + } + + qemu_set_nonblock(cfd); + if (connect(cfd, (struct sockaddr *)&ss, sslen) < 0) { + if (errno == EINPROGRESS) { + check_soerr = true; + } else { + goto cleanup; + } + } + + sslen = sizeof(ss); + afd = accept(lfd, (struct sockaddr *)&ss, &sslen); + if (afd < 0) { + goto cleanup; + } + + if (check_soerr) { + if (qemu_getsockopt(cfd, SOL_SOCKET, SO_ERROR, &soerr, &soerrlen) < 0) { + goto cleanup; + } + if (soerr) { + errno = soerr; + goto cleanup; + } + } + + ret = 0; + + cleanup: + if (afd != -1) { + close(afd); + } + if (cfd != -1) { + close(cfd); + } + if (lfd != -1) { + close(lfd); + } + if (res) { + freeaddrinfo(res); + } + return ret; +} + + +int socket_check_protocol_support(bool *has_ipv4, bool *has_ipv6) +{ + *has_ipv4 = *has_ipv6 = false; + + if (socket_can_bind_connect("127.0.0.1", PF_INET) < 0) { + if (errno != EADDRNOTAVAIL) { + return -1; + } + } else { + *has_ipv4 = true; + } + + if (socket_can_bind_connect("::1", PF_INET6) < 0) { + if (errno != EADDRNOTAVAIL) { + return -1; + } + } else { + *has_ipv6 = true; + } + + return 0; +} diff --git a/tests/unit/socket-helpers.h b/tests/unit/socket-helpers.h new file mode 100644 index 0000000000..512a004811 --- /dev/null +++ b/tests/unit/socket-helpers.h @@ -0,0 +1,35 @@ +/* + * Helper functions for tests using sockets + * + * Copyright 2015-2018 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef TESTS_SOCKET_HELPERS_H +#define TESTS_SOCKET_HELPERS_H + +/* + * @has_ipv4: set to true on return if IPv4 is available + * @has_ipv6: set to true on return if IPv6 is available + * + * Check whether IPv4 and/or IPv6 are available for use. + * On success, @has_ipv4 and @has_ipv6 will be set to + * indicate whether the respective protocols are available. + * + * Returns 0 on success, -1 on fatal error + */ +int socket_check_protocol_support(bool *has_ipv4, bool *has_ipv6); + +#endif diff --git a/tests/unit/test-aio-multithread.c b/tests/unit/test-aio-multithread.c new file mode 100644 index 0000000000..a555cc8835 --- /dev/null +++ b/tests/unit/test-aio-multithread.c @@ -0,0 +1,460 @@ +/* + * AioContext multithreading tests + * + * Copyright Red Hat, Inc. 2016 + * + * Authors: + * Paolo Bonzini + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "block/aio.h" +#include "qemu/coroutine.h" +#include "qemu/thread.h" +#include "qemu/error-report.h" +#include "iothread.h" + +/* AioContext management */ + +#define NUM_CONTEXTS 5 + +static IOThread *threads[NUM_CONTEXTS]; +static AioContext *ctx[NUM_CONTEXTS]; +static __thread int id = -1; + +static QemuEvent done_event; + +/* Run a function synchronously on a remote iothread. */ + +typedef struct CtxRunData { + QEMUBHFunc *cb; + void *arg; +} CtxRunData; + +static void ctx_run_bh_cb(void *opaque) +{ + CtxRunData *data = opaque; + + data->cb(data->arg); + qemu_event_set(&done_event); +} + +static void ctx_run(int i, QEMUBHFunc *cb, void *opaque) +{ + CtxRunData data = { + .cb = cb, + .arg = opaque + }; + + qemu_event_reset(&done_event); + aio_bh_schedule_oneshot(ctx[i], ctx_run_bh_cb, &data); + qemu_event_wait(&done_event); +} + +/* Starting the iothreads. */ + +static void set_id_cb(void *opaque) +{ + int *i = opaque; + + id = *i; +} + +static void create_aio_contexts(void) +{ + int i; + + for (i = 0; i < NUM_CONTEXTS; i++) { + threads[i] = iothread_new(); + ctx[i] = iothread_get_aio_context(threads[i]); + } + + qemu_event_init(&done_event, false); + for (i = 0; i < NUM_CONTEXTS; i++) { + ctx_run(i, set_id_cb, &i); + } +} + +/* Stopping the iothreads. */ + +static void join_aio_contexts(void) +{ + int i; + + for (i = 0; i < NUM_CONTEXTS; i++) { + aio_context_ref(ctx[i]); + } + for (i = 0; i < NUM_CONTEXTS; i++) { + iothread_join(threads[i]); + } + for (i = 0; i < NUM_CONTEXTS; i++) { + aio_context_unref(ctx[i]); + } + qemu_event_destroy(&done_event); +} + +/* Basic test for the stuff above. */ + +static void test_lifecycle(void) +{ + create_aio_contexts(); + join_aio_contexts(); +} + +/* aio_co_schedule test. */ + +static Coroutine *to_schedule[NUM_CONTEXTS]; + +static bool now_stopping; + +static int count_retry; +static int count_here; +static int count_other; + +static bool schedule_next(int n) +{ + Coroutine *co; + + co = qatomic_xchg(&to_schedule[n], NULL); + if (!co) { + qatomic_inc(&count_retry); + return false; + } + + if (n == id) { + qatomic_inc(&count_here); + } else { + qatomic_inc(&count_other); + } + + aio_co_schedule(ctx[n], co); + return true; +} + +static void finish_cb(void *opaque) +{ + schedule_next(id); +} + +static coroutine_fn void test_multi_co_schedule_entry(void *opaque) +{ + g_assert(to_schedule[id] == NULL); + + while (!qatomic_mb_read(&now_stopping)) { + int n; + + n = g_test_rand_int_range(0, NUM_CONTEXTS); + schedule_next(n); + + qatomic_mb_set(&to_schedule[id], qemu_coroutine_self()); + qemu_coroutine_yield(); + g_assert(to_schedule[id] == NULL); + } +} + + +static void test_multi_co_schedule(int seconds) +{ + int i; + + count_here = count_other = count_retry = 0; + now_stopping = false; + + create_aio_contexts(); + for (i = 0; i < NUM_CONTEXTS; i++) { + Coroutine *co1 = qemu_coroutine_create(test_multi_co_schedule_entry, NULL); + aio_co_schedule(ctx[i], co1); + } + + g_usleep(seconds * 1000000); + + qatomic_mb_set(&now_stopping, true); + for (i = 0; i < NUM_CONTEXTS; i++) { + ctx_run(i, finish_cb, NULL); + to_schedule[i] = NULL; + } + + join_aio_contexts(); + g_test_message("scheduled %d, queued %d, retry %d, total %d", + count_other, count_here, count_retry, + count_here + count_other + count_retry); +} + +static void test_multi_co_schedule_1(void) +{ + test_multi_co_schedule(1); +} + +static void test_multi_co_schedule_10(void) +{ + test_multi_co_schedule(10); +} + +/* CoMutex thread-safety. */ + +static uint32_t atomic_counter; +static uint32_t running; +static uint32_t counter; +static CoMutex comutex; + +static void coroutine_fn test_multi_co_mutex_entry(void *opaque) +{ + while (!qatomic_mb_read(&now_stopping)) { + qemu_co_mutex_lock(&comutex); + counter++; + qemu_co_mutex_unlock(&comutex); + + /* Increase atomic_counter *after* releasing the mutex. Otherwise + * there is a chance (it happens about 1 in 3 runs) that the iothread + * exits before the coroutine is woken up, causing a spurious + * assertion failure. + */ + qatomic_inc(&atomic_counter); + } + qatomic_dec(&running); +} + +static void test_multi_co_mutex(int threads, int seconds) +{ + int i; + + qemu_co_mutex_init(&comutex); + counter = 0; + atomic_counter = 0; + now_stopping = false; + + create_aio_contexts(); + assert(threads <= NUM_CONTEXTS); + running = threads; + for (i = 0; i < threads; i++) { + Coroutine *co1 = qemu_coroutine_create(test_multi_co_mutex_entry, NULL); + aio_co_schedule(ctx[i], co1); + } + + g_usleep(seconds * 1000000); + + qatomic_mb_set(&now_stopping, true); + while (running > 0) { + g_usleep(100000); + } + + join_aio_contexts(); + g_test_message("%d iterations/second", counter / seconds); + g_assert_cmpint(counter, ==, atomic_counter); +} + +/* Testing with NUM_CONTEXTS threads focuses on the queue. The mutex however + * is too contended (and the threads spend too much time in aio_poll) + * to actually stress the handoff protocol. + */ +static void test_multi_co_mutex_1(void) +{ + test_multi_co_mutex(NUM_CONTEXTS, 1); +} + +static void test_multi_co_mutex_10(void) +{ + test_multi_co_mutex(NUM_CONTEXTS, 10); +} + +/* Testing with fewer threads stresses the handoff protocol too. Still, the + * case where the locker _can_ pick up a handoff is very rare, happening + * about 10 times in 1 million, so increase the runtime a bit compared to + * other "quick" testcases that only run for 1 second. + */ +static void test_multi_co_mutex_2_3(void) +{ + test_multi_co_mutex(2, 3); +} + +static void test_multi_co_mutex_2_30(void) +{ + test_multi_co_mutex(2, 30); +} + +/* Same test with fair mutexes, for performance comparison. */ + +#ifdef CONFIG_LINUX +#include "qemu/futex.h" + +/* The nodes for the mutex reside in this structure (on which we try to avoid + * false sharing). The head of the mutex is in the "mutex_head" variable. + */ +static struct { + int next, locked; + int padding[14]; +} nodes[NUM_CONTEXTS] __attribute__((__aligned__(64))); + +static int mutex_head = -1; + +static void mcs_mutex_lock(void) +{ + int prev; + + nodes[id].next = -1; + nodes[id].locked = 1; + prev = qatomic_xchg(&mutex_head, id); + if (prev != -1) { + qatomic_set(&nodes[prev].next, id); + qemu_futex_wait(&nodes[id].locked, 1); + } +} + +static void mcs_mutex_unlock(void) +{ + int next; + if (qatomic_read(&nodes[id].next) == -1) { + if (qatomic_read(&mutex_head) == id && + qatomic_cmpxchg(&mutex_head, id, -1) == id) { + /* Last item in the list, exit. */ + return; + } + while (qatomic_read(&nodes[id].next) == -1) { + /* mcs_mutex_lock did the xchg, but has not updated + * nodes[prev].next yet. + */ + } + } + + /* Wake up the next in line. */ + next = qatomic_read(&nodes[id].next); + nodes[next].locked = 0; + qemu_futex_wake(&nodes[next].locked, 1); +} + +static void test_multi_fair_mutex_entry(void *opaque) +{ + while (!qatomic_mb_read(&now_stopping)) { + mcs_mutex_lock(); + counter++; + mcs_mutex_unlock(); + qatomic_inc(&atomic_counter); + } + qatomic_dec(&running); +} + +static void test_multi_fair_mutex(int threads, int seconds) +{ + int i; + + assert(mutex_head == -1); + counter = 0; + atomic_counter = 0; + now_stopping = false; + + create_aio_contexts(); + assert(threads <= NUM_CONTEXTS); + running = threads; + for (i = 0; i < threads; i++) { + Coroutine *co1 = qemu_coroutine_create(test_multi_fair_mutex_entry, NULL); + aio_co_schedule(ctx[i], co1); + } + + g_usleep(seconds * 1000000); + + qatomic_mb_set(&now_stopping, true); + while (running > 0) { + g_usleep(100000); + } + + join_aio_contexts(); + g_test_message("%d iterations/second", counter / seconds); + g_assert_cmpint(counter, ==, atomic_counter); +} + +static void test_multi_fair_mutex_1(void) +{ + test_multi_fair_mutex(NUM_CONTEXTS, 1); +} + +static void test_multi_fair_mutex_10(void) +{ + test_multi_fair_mutex(NUM_CONTEXTS, 10); +} +#endif + +/* Same test with pthread mutexes, for performance comparison and + * portability. */ + +static QemuMutex mutex; + +static void test_multi_mutex_entry(void *opaque) +{ + while (!qatomic_mb_read(&now_stopping)) { + qemu_mutex_lock(&mutex); + counter++; + qemu_mutex_unlock(&mutex); + qatomic_inc(&atomic_counter); + } + qatomic_dec(&running); +} + +static void test_multi_mutex(int threads, int seconds) +{ + int i; + + qemu_mutex_init(&mutex); + counter = 0; + atomic_counter = 0; + now_stopping = false; + + create_aio_contexts(); + assert(threads <= NUM_CONTEXTS); + running = threads; + for (i = 0; i < threads; i++) { + Coroutine *co1 = qemu_coroutine_create(test_multi_mutex_entry, NULL); + aio_co_schedule(ctx[i], co1); + } + + g_usleep(seconds * 1000000); + + qatomic_mb_set(&now_stopping, true); + while (running > 0) { + g_usleep(100000); + } + + join_aio_contexts(); + g_test_message("%d iterations/second", counter / seconds); + g_assert_cmpint(counter, ==, atomic_counter); +} + +static void test_multi_mutex_1(void) +{ + test_multi_mutex(NUM_CONTEXTS, 1); +} + +static void test_multi_mutex_10(void) +{ + test_multi_mutex(NUM_CONTEXTS, 10); +} + +/* End of tests. */ + +int main(int argc, char **argv) +{ + init_clocks(NULL); + + g_test_init(&argc, &argv, NULL); + g_test_add_func("/aio/multi/lifecycle", test_lifecycle); + if (g_test_quick()) { + g_test_add_func("/aio/multi/schedule", test_multi_co_schedule_1); + g_test_add_func("/aio/multi/mutex/contended", test_multi_co_mutex_1); + g_test_add_func("/aio/multi/mutex/handoff", test_multi_co_mutex_2_3); +#ifdef CONFIG_LINUX + g_test_add_func("/aio/multi/mutex/mcs", test_multi_fair_mutex_1); +#endif + g_test_add_func("/aio/multi/mutex/pthread", test_multi_mutex_1); + } else { + g_test_add_func("/aio/multi/schedule", test_multi_co_schedule_10); + g_test_add_func("/aio/multi/mutex/contended", test_multi_co_mutex_10); + g_test_add_func("/aio/multi/mutex/handoff", test_multi_co_mutex_2_30); +#ifdef CONFIG_LINUX + g_test_add_func("/aio/multi/mutex/mcs", test_multi_fair_mutex_10); +#endif + g_test_add_func("/aio/multi/mutex/pthread", test_multi_mutex_10); + } + return g_test_run(); +} diff --git a/tests/unit/test-aio.c b/tests/unit/test-aio.c new file mode 100644 index 0000000000..8a46078463 --- /dev/null +++ b/tests/unit/test-aio.c @@ -0,0 +1,921 @@ +/* + * AioContext tests + * + * Copyright Red Hat, Inc. 2012 + * + * Authors: + * Paolo Bonzini + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "block/aio.h" +#include "qapi/error.h" +#include "qemu/timer.h" +#include "qemu/sockets.h" +#include "qemu/error-report.h" +#include "qemu/coroutine.h" +#include "qemu/main-loop.h" + +static AioContext *ctx; + +typedef struct { + EventNotifier e; + int n; + int active; + bool auto_set; +} EventNotifierTestData; + +/* Wait until event notifier becomes inactive */ +static void wait_until_inactive(EventNotifierTestData *data) +{ + while (data->active > 0) { + aio_poll(ctx, true); + } +} + +/* Simple callbacks for testing. */ + +typedef struct { + QEMUBH *bh; + int n; + int max; +} BHTestData; + +typedef struct { + QEMUTimer timer; + QEMUClockType clock_type; + int n; + int max; + int64_t ns; + AioContext *ctx; +} TimerTestData; + +static void bh_test_cb(void *opaque) +{ + BHTestData *data = opaque; + if (++data->n < data->max) { + qemu_bh_schedule(data->bh); + } +} + +static void timer_test_cb(void *opaque) +{ + TimerTestData *data = opaque; + if (++data->n < data->max) { + timer_mod(&data->timer, + qemu_clock_get_ns(data->clock_type) + data->ns); + } +} + +static void dummy_io_handler_read(EventNotifier *e) +{ +} + +static void bh_delete_cb(void *opaque) +{ + BHTestData *data = opaque; + if (++data->n < data->max) { + qemu_bh_schedule(data->bh); + } else { + qemu_bh_delete(data->bh); + data->bh = NULL; + } +} + +static void event_ready_cb(EventNotifier *e) +{ + EventNotifierTestData *data = container_of(e, EventNotifierTestData, e); + g_assert(event_notifier_test_and_clear(e)); + data->n++; + if (data->active > 0) { + data->active--; + } + if (data->auto_set && data->active) { + event_notifier_set(e); + } +} + +/* Tests using aio_*. */ + +typedef struct { + QemuMutex start_lock; + EventNotifier notifier; + bool thread_acquired; +} AcquireTestData; + +static void *test_acquire_thread(void *opaque) +{ + AcquireTestData *data = opaque; + + /* Wait for other thread to let us start */ + qemu_mutex_lock(&data->start_lock); + qemu_mutex_unlock(&data->start_lock); + + /* event_notifier_set might be called either before or after + * the main thread's call to poll(). The test case's outcome + * should be the same in either case. + */ + event_notifier_set(&data->notifier); + aio_context_acquire(ctx); + aio_context_release(ctx); + + data->thread_acquired = true; /* success, we got here */ + + return NULL; +} + +static void set_event_notifier(AioContext *ctx, EventNotifier *notifier, + EventNotifierHandler *handler) +{ + aio_set_event_notifier(ctx, notifier, false, handler, NULL); +} + +static void dummy_notifier_read(EventNotifier *n) +{ + event_notifier_test_and_clear(n); +} + +static void test_acquire(void) +{ + QemuThread thread; + AcquireTestData data; + + /* Dummy event notifier ensures aio_poll() will block */ + event_notifier_init(&data.notifier, false); + set_event_notifier(ctx, &data.notifier, dummy_notifier_read); + g_assert(!aio_poll(ctx, false)); /* consume aio_notify() */ + + qemu_mutex_init(&data.start_lock); + qemu_mutex_lock(&data.start_lock); + data.thread_acquired = false; + + qemu_thread_create(&thread, "test_acquire_thread", + test_acquire_thread, + &data, QEMU_THREAD_JOINABLE); + + /* Block in aio_poll(), let other thread kick us and acquire context */ + aio_context_acquire(ctx); + qemu_mutex_unlock(&data.start_lock); /* let the thread run */ + g_assert(aio_poll(ctx, true)); + g_assert(!data.thread_acquired); + aio_context_release(ctx); + + qemu_thread_join(&thread); + set_event_notifier(ctx, &data.notifier, NULL); + event_notifier_cleanup(&data.notifier); + + g_assert(data.thread_acquired); +} + +static void test_bh_schedule(void) +{ + BHTestData data = { .n = 0 }; + data.bh = aio_bh_new(ctx, bh_test_cb, &data); + + qemu_bh_schedule(data.bh); + g_assert_cmpint(data.n, ==, 0); + + g_assert(aio_poll(ctx, true)); + g_assert_cmpint(data.n, ==, 1); + + g_assert(!aio_poll(ctx, false)); + g_assert_cmpint(data.n, ==, 1); + qemu_bh_delete(data.bh); +} + +static void test_bh_schedule10(void) +{ + BHTestData data = { .n = 0, .max = 10 }; + data.bh = aio_bh_new(ctx, bh_test_cb, &data); + + qemu_bh_schedule(data.bh); + g_assert_cmpint(data.n, ==, 0); + + g_assert(aio_poll(ctx, false)); + g_assert_cmpint(data.n, ==, 1); + + g_assert(aio_poll(ctx, true)); + g_assert_cmpint(data.n, ==, 2); + + while (data.n < 10) { + aio_poll(ctx, true); + } + g_assert_cmpint(data.n, ==, 10); + + g_assert(!aio_poll(ctx, false)); + g_assert_cmpint(data.n, ==, 10); + qemu_bh_delete(data.bh); +} + +static void test_bh_cancel(void) +{ + BHTestData data = { .n = 0 }; + data.bh = aio_bh_new(ctx, bh_test_cb, &data); + + qemu_bh_schedule(data.bh); + g_assert_cmpint(data.n, ==, 0); + + qemu_bh_cancel(data.bh); + g_assert_cmpint(data.n, ==, 0); + + g_assert(!aio_poll(ctx, false)); + g_assert_cmpint(data.n, ==, 0); + qemu_bh_delete(data.bh); +} + +static void test_bh_delete(void) +{ + BHTestData data = { .n = 0 }; + data.bh = aio_bh_new(ctx, bh_test_cb, &data); + + qemu_bh_schedule(data.bh); + g_assert_cmpint(data.n, ==, 0); + + qemu_bh_delete(data.bh); + g_assert_cmpint(data.n, ==, 0); + + g_assert(!aio_poll(ctx, false)); + g_assert_cmpint(data.n, ==, 0); +} + +static void test_bh_delete_from_cb(void) +{ + BHTestData data1 = { .n = 0, .max = 1 }; + + data1.bh = aio_bh_new(ctx, bh_delete_cb, &data1); + + qemu_bh_schedule(data1.bh); + g_assert_cmpint(data1.n, ==, 0); + + while (data1.n < data1.max) { + aio_poll(ctx, true); + } + g_assert_cmpint(data1.n, ==, data1.max); + g_assert(data1.bh == NULL); + + g_assert(!aio_poll(ctx, false)); +} + +static void test_bh_delete_from_cb_many(void) +{ + BHTestData data1 = { .n = 0, .max = 1 }; + BHTestData data2 = { .n = 0, .max = 3 }; + BHTestData data3 = { .n = 0, .max = 2 }; + BHTestData data4 = { .n = 0, .max = 4 }; + + data1.bh = aio_bh_new(ctx, bh_delete_cb, &data1); + data2.bh = aio_bh_new(ctx, bh_delete_cb, &data2); + data3.bh = aio_bh_new(ctx, bh_delete_cb, &data3); + data4.bh = aio_bh_new(ctx, bh_delete_cb, &data4); + + qemu_bh_schedule(data1.bh); + qemu_bh_schedule(data2.bh); + qemu_bh_schedule(data3.bh); + qemu_bh_schedule(data4.bh); + g_assert_cmpint(data1.n, ==, 0); + g_assert_cmpint(data2.n, ==, 0); + g_assert_cmpint(data3.n, ==, 0); + g_assert_cmpint(data4.n, ==, 0); + + g_assert(aio_poll(ctx, false)); + g_assert_cmpint(data1.n, ==, 1); + g_assert_cmpint(data2.n, ==, 1); + g_assert_cmpint(data3.n, ==, 1); + g_assert_cmpint(data4.n, ==, 1); + g_assert(data1.bh == NULL); + + while (data1.n < data1.max || + data2.n < data2.max || + data3.n < data3.max || + data4.n < data4.max) { + aio_poll(ctx, true); + } + g_assert_cmpint(data1.n, ==, data1.max); + g_assert_cmpint(data2.n, ==, data2.max); + g_assert_cmpint(data3.n, ==, data3.max); + g_assert_cmpint(data4.n, ==, data4.max); + g_assert(data1.bh == NULL); + g_assert(data2.bh == NULL); + g_assert(data3.bh == NULL); + g_assert(data4.bh == NULL); +} + +static void test_bh_flush(void) +{ + BHTestData data = { .n = 0 }; + data.bh = aio_bh_new(ctx, bh_test_cb, &data); + + qemu_bh_schedule(data.bh); + g_assert_cmpint(data.n, ==, 0); + + g_assert(aio_poll(ctx, true)); + g_assert_cmpint(data.n, ==, 1); + + g_assert(!aio_poll(ctx, false)); + g_assert_cmpint(data.n, ==, 1); + qemu_bh_delete(data.bh); +} + +static void test_set_event_notifier(void) +{ + EventNotifierTestData data = { .n = 0, .active = 0 }; + event_notifier_init(&data.e, false); + set_event_notifier(ctx, &data.e, event_ready_cb); + g_assert(!aio_poll(ctx, false)); + g_assert_cmpint(data.n, ==, 0); + + set_event_notifier(ctx, &data.e, NULL); + g_assert(!aio_poll(ctx, false)); + g_assert_cmpint(data.n, ==, 0); + event_notifier_cleanup(&data.e); +} + +static void test_wait_event_notifier(void) +{ + EventNotifierTestData data = { .n = 0, .active = 1 }; + event_notifier_init(&data.e, false); + set_event_notifier(ctx, &data.e, event_ready_cb); + while (aio_poll(ctx, false)); + g_assert_cmpint(data.n, ==, 0); + g_assert_cmpint(data.active, ==, 1); + + event_notifier_set(&data.e); + g_assert(aio_poll(ctx, false)); + g_assert_cmpint(data.n, ==, 1); + g_assert_cmpint(data.active, ==, 0); + + g_assert(!aio_poll(ctx, false)); + g_assert_cmpint(data.n, ==, 1); + g_assert_cmpint(data.active, ==, 0); + + set_event_notifier(ctx, &data.e, NULL); + g_assert(!aio_poll(ctx, false)); + g_assert_cmpint(data.n, ==, 1); + + event_notifier_cleanup(&data.e); +} + +static void test_flush_event_notifier(void) +{ + EventNotifierTestData data = { .n = 0, .active = 10, .auto_set = true }; + event_notifier_init(&data.e, false); + set_event_notifier(ctx, &data.e, event_ready_cb); + while (aio_poll(ctx, false)); + g_assert_cmpint(data.n, ==, 0); + g_assert_cmpint(data.active, ==, 10); + + event_notifier_set(&data.e); + g_assert(aio_poll(ctx, false)); + g_assert_cmpint(data.n, ==, 1); + g_assert_cmpint(data.active, ==, 9); + g_assert(aio_poll(ctx, false)); + + wait_until_inactive(&data); + g_assert_cmpint(data.n, ==, 10); + g_assert_cmpint(data.active, ==, 0); + g_assert(!aio_poll(ctx, false)); + + set_event_notifier(ctx, &data.e, NULL); + g_assert(!aio_poll(ctx, false)); + event_notifier_cleanup(&data.e); +} + +static void test_aio_external_client(void) +{ + int i, j; + + for (i = 1; i < 3; i++) { + EventNotifierTestData data = { .n = 0, .active = 10, .auto_set = true }; + event_notifier_init(&data.e, false); + aio_set_event_notifier(ctx, &data.e, true, event_ready_cb, NULL); + event_notifier_set(&data.e); + for (j = 0; j < i; j++) { + aio_disable_external(ctx); + } + for (j = 0; j < i; j++) { + assert(!aio_poll(ctx, false)); + assert(event_notifier_test_and_clear(&data.e)); + event_notifier_set(&data.e); + aio_enable_external(ctx); + } + assert(aio_poll(ctx, false)); + set_event_notifier(ctx, &data.e, NULL); + event_notifier_cleanup(&data.e); + } +} + +static void test_wait_event_notifier_noflush(void) +{ + EventNotifierTestData data = { .n = 0 }; + EventNotifierTestData dummy = { .n = 0, .active = 1 }; + + event_notifier_init(&data.e, false); + set_event_notifier(ctx, &data.e, event_ready_cb); + + g_assert(!aio_poll(ctx, false)); + g_assert_cmpint(data.n, ==, 0); + + /* Until there is an active descriptor, aio_poll may or may not call + * event_ready_cb. Still, it must not block. */ + event_notifier_set(&data.e); + g_assert(aio_poll(ctx, true)); + data.n = 0; + + /* An active event notifier forces aio_poll to look at EventNotifiers. */ + event_notifier_init(&dummy.e, false); + set_event_notifier(ctx, &dummy.e, event_ready_cb); + + event_notifier_set(&data.e); + g_assert(aio_poll(ctx, false)); + g_assert_cmpint(data.n, ==, 1); + g_assert(!aio_poll(ctx, false)); + g_assert_cmpint(data.n, ==, 1); + + event_notifier_set(&data.e); + g_assert(aio_poll(ctx, false)); + g_assert_cmpint(data.n, ==, 2); + g_assert(!aio_poll(ctx, false)); + g_assert_cmpint(data.n, ==, 2); + + event_notifier_set(&dummy.e); + wait_until_inactive(&dummy); + g_assert_cmpint(data.n, ==, 2); + g_assert_cmpint(dummy.n, ==, 1); + g_assert_cmpint(dummy.active, ==, 0); + + set_event_notifier(ctx, &dummy.e, NULL); + event_notifier_cleanup(&dummy.e); + + set_event_notifier(ctx, &data.e, NULL); + g_assert(!aio_poll(ctx, false)); + g_assert_cmpint(data.n, ==, 2); + + event_notifier_cleanup(&data.e); +} + +static void test_timer_schedule(void) +{ + TimerTestData data = { .n = 0, .ctx = ctx, .ns = SCALE_MS * 750LL, + .max = 2, + .clock_type = QEMU_CLOCK_REALTIME }; + EventNotifier e; + + /* aio_poll will not block to wait for timers to complete unless it has + * an fd to wait on. Fixing this breaks other tests. So create a dummy one. + */ + event_notifier_init(&e, false); + set_event_notifier(ctx, &e, dummy_io_handler_read); + aio_poll(ctx, false); + + aio_timer_init(ctx, &data.timer, data.clock_type, + SCALE_NS, timer_test_cb, &data); + timer_mod(&data.timer, + qemu_clock_get_ns(data.clock_type) + + data.ns); + + g_assert_cmpint(data.n, ==, 0); + + /* timer_mod may well cause an event notifer to have gone off, + * so clear that + */ + do {} while (aio_poll(ctx, false)); + + g_assert(!aio_poll(ctx, false)); + g_assert_cmpint(data.n, ==, 0); + + g_usleep(1 * G_USEC_PER_SEC); + g_assert_cmpint(data.n, ==, 0); + + g_assert(aio_poll(ctx, false)); + g_assert_cmpint(data.n, ==, 1); + + /* timer_mod called by our callback */ + do {} while (aio_poll(ctx, false)); + + g_assert(!aio_poll(ctx, false)); + g_assert_cmpint(data.n, ==, 1); + + g_assert(aio_poll(ctx, true)); + g_assert_cmpint(data.n, ==, 2); + + /* As max is now 2, an event notifier should not have gone off */ + + g_assert(!aio_poll(ctx, false)); + g_assert_cmpint(data.n, ==, 2); + + set_event_notifier(ctx, &e, NULL); + event_notifier_cleanup(&e); + + timer_del(&data.timer); +} + +/* Now the same tests, using the context as a GSource. They are + * very similar to the ones above, with g_main_context_iteration + * replacing aio_poll. However: + * - sometimes both the AioContext and the glib main loop wake + * themselves up. Hence, some "g_assert(!aio_poll(ctx, false));" + * are replaced by "while (g_main_context_iteration(NULL, false));". + * - there is no exact replacement for a blocking wait. + * "while (g_main_context_iteration(NULL, true)" seems to work, + * but it is not documented _why_ it works. For these tests a + * non-blocking loop like "while (g_main_context_iteration(NULL, false)" + * works well, and that's what I am using. + */ + +static void test_source_flush(void) +{ + g_assert(!g_main_context_iteration(NULL, false)); + aio_notify(ctx); + while (g_main_context_iteration(NULL, false)); + g_assert(!g_main_context_iteration(NULL, false)); +} + +static void test_source_bh_schedule(void) +{ + BHTestData data = { .n = 0 }; + data.bh = aio_bh_new(ctx, bh_test_cb, &data); + + qemu_bh_schedule(data.bh); + g_assert_cmpint(data.n, ==, 0); + + g_assert(g_main_context_iteration(NULL, true)); + g_assert_cmpint(data.n, ==, 1); + + g_assert(!g_main_context_iteration(NULL, false)); + g_assert_cmpint(data.n, ==, 1); + qemu_bh_delete(data.bh); +} + +static void test_source_bh_schedule10(void) +{ + BHTestData data = { .n = 0, .max = 10 }; + data.bh = aio_bh_new(ctx, bh_test_cb, &data); + + qemu_bh_schedule(data.bh); + g_assert_cmpint(data.n, ==, 0); + + g_assert(g_main_context_iteration(NULL, false)); + g_assert_cmpint(data.n, ==, 1); + + g_assert(g_main_context_iteration(NULL, true)); + g_assert_cmpint(data.n, ==, 2); + + while (g_main_context_iteration(NULL, false)); + g_assert_cmpint(data.n, ==, 10); + + g_assert(!g_main_context_iteration(NULL, false)); + g_assert_cmpint(data.n, ==, 10); + qemu_bh_delete(data.bh); +} + +static void test_source_bh_cancel(void) +{ + BHTestData data = { .n = 0 }; + data.bh = aio_bh_new(ctx, bh_test_cb, &data); + + qemu_bh_schedule(data.bh); + g_assert_cmpint(data.n, ==, 0); + + qemu_bh_cancel(data.bh); + g_assert_cmpint(data.n, ==, 0); + + while (g_main_context_iteration(NULL, false)); + g_assert_cmpint(data.n, ==, 0); + qemu_bh_delete(data.bh); +} + +static void test_source_bh_delete(void) +{ + BHTestData data = { .n = 0 }; + data.bh = aio_bh_new(ctx, bh_test_cb, &data); + + qemu_bh_schedule(data.bh); + g_assert_cmpint(data.n, ==, 0); + + qemu_bh_delete(data.bh); + g_assert_cmpint(data.n, ==, 0); + + while (g_main_context_iteration(NULL, false)); + g_assert_cmpint(data.n, ==, 0); +} + +static void test_source_bh_delete_from_cb(void) +{ + BHTestData data1 = { .n = 0, .max = 1 }; + + data1.bh = aio_bh_new(ctx, bh_delete_cb, &data1); + + qemu_bh_schedule(data1.bh); + g_assert_cmpint(data1.n, ==, 0); + + g_main_context_iteration(NULL, true); + g_assert_cmpint(data1.n, ==, data1.max); + g_assert(data1.bh == NULL); + + assert(g_main_context_iteration(NULL, false)); + assert(!g_main_context_iteration(NULL, false)); +} + +static void test_source_bh_delete_from_cb_many(void) +{ + BHTestData data1 = { .n = 0, .max = 1 }; + BHTestData data2 = { .n = 0, .max = 3 }; + BHTestData data3 = { .n = 0, .max = 2 }; + BHTestData data4 = { .n = 0, .max = 4 }; + + data1.bh = aio_bh_new(ctx, bh_delete_cb, &data1); + data2.bh = aio_bh_new(ctx, bh_delete_cb, &data2); + data3.bh = aio_bh_new(ctx, bh_delete_cb, &data3); + data4.bh = aio_bh_new(ctx, bh_delete_cb, &data4); + + qemu_bh_schedule(data1.bh); + qemu_bh_schedule(data2.bh); + qemu_bh_schedule(data3.bh); + qemu_bh_schedule(data4.bh); + g_assert_cmpint(data1.n, ==, 0); + g_assert_cmpint(data2.n, ==, 0); + g_assert_cmpint(data3.n, ==, 0); + g_assert_cmpint(data4.n, ==, 0); + + g_assert(g_main_context_iteration(NULL, false)); + g_assert_cmpint(data1.n, ==, 1); + g_assert_cmpint(data2.n, ==, 1); + g_assert_cmpint(data3.n, ==, 1); + g_assert_cmpint(data4.n, ==, 1); + g_assert(data1.bh == NULL); + + while (g_main_context_iteration(NULL, false)); + g_assert_cmpint(data1.n, ==, data1.max); + g_assert_cmpint(data2.n, ==, data2.max); + g_assert_cmpint(data3.n, ==, data3.max); + g_assert_cmpint(data4.n, ==, data4.max); + g_assert(data1.bh == NULL); + g_assert(data2.bh == NULL); + g_assert(data3.bh == NULL); + g_assert(data4.bh == NULL); +} + +static void test_source_bh_flush(void) +{ + BHTestData data = { .n = 0 }; + data.bh = aio_bh_new(ctx, bh_test_cb, &data); + + qemu_bh_schedule(data.bh); + g_assert_cmpint(data.n, ==, 0); + + g_assert(g_main_context_iteration(NULL, true)); + g_assert_cmpint(data.n, ==, 1); + + g_assert(!g_main_context_iteration(NULL, false)); + g_assert_cmpint(data.n, ==, 1); + qemu_bh_delete(data.bh); +} + +static void test_source_set_event_notifier(void) +{ + EventNotifierTestData data = { .n = 0, .active = 0 }; + event_notifier_init(&data.e, false); + set_event_notifier(ctx, &data.e, event_ready_cb); + while (g_main_context_iteration(NULL, false)); + g_assert_cmpint(data.n, ==, 0); + + set_event_notifier(ctx, &data.e, NULL); + while (g_main_context_iteration(NULL, false)); + g_assert_cmpint(data.n, ==, 0); + event_notifier_cleanup(&data.e); +} + +static void test_source_wait_event_notifier(void) +{ + EventNotifierTestData data = { .n = 0, .active = 1 }; + event_notifier_init(&data.e, false); + set_event_notifier(ctx, &data.e, event_ready_cb); + while (g_main_context_iteration(NULL, false)); + g_assert_cmpint(data.n, ==, 0); + g_assert_cmpint(data.active, ==, 1); + + event_notifier_set(&data.e); + g_assert(g_main_context_iteration(NULL, false)); + g_assert_cmpint(data.n, ==, 1); + g_assert_cmpint(data.active, ==, 0); + + while (g_main_context_iteration(NULL, false)); + g_assert_cmpint(data.n, ==, 1); + g_assert_cmpint(data.active, ==, 0); + + set_event_notifier(ctx, &data.e, NULL); + while (g_main_context_iteration(NULL, false)); + g_assert_cmpint(data.n, ==, 1); + + event_notifier_cleanup(&data.e); +} + +static void test_source_flush_event_notifier(void) +{ + EventNotifierTestData data = { .n = 0, .active = 10, .auto_set = true }; + event_notifier_init(&data.e, false); + set_event_notifier(ctx, &data.e, event_ready_cb); + while (g_main_context_iteration(NULL, false)); + g_assert_cmpint(data.n, ==, 0); + g_assert_cmpint(data.active, ==, 10); + + event_notifier_set(&data.e); + g_assert(g_main_context_iteration(NULL, false)); + g_assert_cmpint(data.n, ==, 1); + g_assert_cmpint(data.active, ==, 9); + g_assert(g_main_context_iteration(NULL, false)); + + while (g_main_context_iteration(NULL, false)); + g_assert_cmpint(data.n, ==, 10); + g_assert_cmpint(data.active, ==, 0); + g_assert(!g_main_context_iteration(NULL, false)); + + set_event_notifier(ctx, &data.e, NULL); + while (g_main_context_iteration(NULL, false)); + event_notifier_cleanup(&data.e); +} + +static void test_source_wait_event_notifier_noflush(void) +{ + EventNotifierTestData data = { .n = 0 }; + EventNotifierTestData dummy = { .n = 0, .active = 1 }; + + event_notifier_init(&data.e, false); + set_event_notifier(ctx, &data.e, event_ready_cb); + + while (g_main_context_iteration(NULL, false)); + g_assert_cmpint(data.n, ==, 0); + + /* Until there is an active descriptor, glib may or may not call + * event_ready_cb. Still, it must not block. */ + event_notifier_set(&data.e); + g_main_context_iteration(NULL, true); + data.n = 0; + + /* An active event notifier forces aio_poll to look at EventNotifiers. */ + event_notifier_init(&dummy.e, false); + set_event_notifier(ctx, &dummy.e, event_ready_cb); + + event_notifier_set(&data.e); + g_assert(g_main_context_iteration(NULL, false)); + g_assert_cmpint(data.n, ==, 1); + g_assert(!g_main_context_iteration(NULL, false)); + g_assert_cmpint(data.n, ==, 1); + + event_notifier_set(&data.e); + g_assert(g_main_context_iteration(NULL, false)); + g_assert_cmpint(data.n, ==, 2); + g_assert(!g_main_context_iteration(NULL, false)); + g_assert_cmpint(data.n, ==, 2); + + event_notifier_set(&dummy.e); + while (g_main_context_iteration(NULL, false)); + g_assert_cmpint(data.n, ==, 2); + g_assert_cmpint(dummy.n, ==, 1); + g_assert_cmpint(dummy.active, ==, 0); + + set_event_notifier(ctx, &dummy.e, NULL); + event_notifier_cleanup(&dummy.e); + + set_event_notifier(ctx, &data.e, NULL); + while (g_main_context_iteration(NULL, false)); + g_assert_cmpint(data.n, ==, 2); + + event_notifier_cleanup(&data.e); +} + +static void test_source_timer_schedule(void) +{ + TimerTestData data = { .n = 0, .ctx = ctx, .ns = SCALE_MS * 750LL, + .max = 2, + .clock_type = QEMU_CLOCK_REALTIME }; + EventNotifier e; + int64_t expiry; + + /* aio_poll will not block to wait for timers to complete unless it has + * an fd to wait on. Fixing this breaks other tests. So create a dummy one. + */ + event_notifier_init(&e, false); + set_event_notifier(ctx, &e, dummy_io_handler_read); + do {} while (g_main_context_iteration(NULL, false)); + + aio_timer_init(ctx, &data.timer, data.clock_type, + SCALE_NS, timer_test_cb, &data); + expiry = qemu_clock_get_ns(data.clock_type) + + data.ns; + timer_mod(&data.timer, expiry); + + g_assert_cmpint(data.n, ==, 0); + + g_usleep(1 * G_USEC_PER_SEC); + g_assert_cmpint(data.n, ==, 0); + + g_assert(g_main_context_iteration(NULL, true)); + g_assert_cmpint(data.n, ==, 1); + expiry += data.ns; + + while (data.n < 2) { + g_main_context_iteration(NULL, true); + } + + g_assert_cmpint(data.n, ==, 2); + g_assert(qemu_clock_get_ns(data.clock_type) > expiry); + + set_event_notifier(ctx, &e, NULL); + event_notifier_cleanup(&e); + + timer_del(&data.timer); +} + +/* + * Check that aio_co_enter() can chain many times + * + * Two coroutines should be able to invoke each other via aio_co_enter() many + * times without hitting a limit like stack exhaustion. In other words, the + * calls should be chained instead of nested. + */ + +typedef struct { + Coroutine *other; + unsigned i; + unsigned max; +} ChainData; + +static void coroutine_fn chain(void *opaque) +{ + ChainData *data = opaque; + + for (data->i = 0; data->i < data->max; data->i++) { + /* Queue up the other coroutine... */ + aio_co_enter(ctx, data->other); + + /* ...and give control to it */ + qemu_coroutine_yield(); + } +} + +static void test_queue_chaining(void) +{ + /* This number of iterations hit stack exhaustion in the past: */ + ChainData data_a = { .max = 25000 }; + ChainData data_b = { .max = 25000 }; + + data_b.other = qemu_coroutine_create(chain, &data_a); + data_a.other = qemu_coroutine_create(chain, &data_b); + + qemu_coroutine_enter(data_b.other); + + g_assert_cmpint(data_a.i, ==, data_a.max); + g_assert_cmpint(data_b.i, ==, data_b.max - 1); + + /* Allow the second coroutine to terminate */ + qemu_coroutine_enter(data_a.other); + + g_assert_cmpint(data_b.i, ==, data_b.max); +} + +/* End of tests. */ + +int main(int argc, char **argv) +{ + qemu_init_main_loop(&error_fatal); + ctx = qemu_get_aio_context(); + + while (g_main_context_iteration(NULL, false)); + + g_test_init(&argc, &argv, NULL); + g_test_add_func("/aio/acquire", test_acquire); + g_test_add_func("/aio/bh/schedule", test_bh_schedule); + g_test_add_func("/aio/bh/schedule10", test_bh_schedule10); + g_test_add_func("/aio/bh/cancel", test_bh_cancel); + g_test_add_func("/aio/bh/delete", test_bh_delete); + g_test_add_func("/aio/bh/callback-delete/one", test_bh_delete_from_cb); + g_test_add_func("/aio/bh/callback-delete/many", test_bh_delete_from_cb_many); + g_test_add_func("/aio/bh/flush", test_bh_flush); + g_test_add_func("/aio/event/add-remove", test_set_event_notifier); + g_test_add_func("/aio/event/wait", test_wait_event_notifier); + g_test_add_func("/aio/event/wait/no-flush-cb", test_wait_event_notifier_noflush); + g_test_add_func("/aio/event/flush", test_flush_event_notifier); + g_test_add_func("/aio/external-client", test_aio_external_client); + g_test_add_func("/aio/timer/schedule", test_timer_schedule); + + g_test_add_func("/aio/coroutine/queue-chaining", test_queue_chaining); + + g_test_add_func("/aio-gsource/flush", test_source_flush); + g_test_add_func("/aio-gsource/bh/schedule", test_source_bh_schedule); + g_test_add_func("/aio-gsource/bh/schedule10", test_source_bh_schedule10); + g_test_add_func("/aio-gsource/bh/cancel", test_source_bh_cancel); + g_test_add_func("/aio-gsource/bh/delete", test_source_bh_delete); + g_test_add_func("/aio-gsource/bh/callback-delete/one", test_source_bh_delete_from_cb); + g_test_add_func("/aio-gsource/bh/callback-delete/many", test_source_bh_delete_from_cb_many); + g_test_add_func("/aio-gsource/bh/flush", test_source_bh_flush); + g_test_add_func("/aio-gsource/event/add-remove", test_source_set_event_notifier); + g_test_add_func("/aio-gsource/event/wait", test_source_wait_event_notifier); + g_test_add_func("/aio-gsource/event/wait/no-flush-cb", test_source_wait_event_notifier_noflush); + g_test_add_func("/aio-gsource/event/flush", test_source_flush_event_notifier); + g_test_add_func("/aio-gsource/timer/schedule", test_source_timer_schedule); + return g_test_run(); +} diff --git a/tests/unit/test-authz-list.c b/tests/unit/test-authz-list.c new file mode 100644 index 0000000000..5351992a01 --- /dev/null +++ b/tests/unit/test-authz-list.c @@ -0,0 +1,160 @@ +/* + * QEMU list file authorization object tests + * + * Copyright (c) 2018 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + * + */ + +#include "qemu/osdep.h" + +#include "authz/list.h" +#include "qemu/module.h" + +static void test_authz_default_deny(void) +{ + QAuthZList *auth = qauthz_list_new("auth0", + QAUTHZ_LIST_POLICY_DENY, + &error_abort); + + g_assert(!qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort)); + + object_unparent(OBJECT(auth)); +} + +static void test_authz_default_allow(void) +{ + QAuthZList *auth = qauthz_list_new("auth0", + QAUTHZ_LIST_POLICY_ALLOW, + &error_abort); + + g_assert(qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort)); + + object_unparent(OBJECT(auth)); +} + +static void test_authz_explicit_deny(void) +{ + QAuthZList *auth = qauthz_list_new("auth0", + QAUTHZ_LIST_POLICY_ALLOW, + &error_abort); + + qauthz_list_append_rule(auth, "fred", QAUTHZ_LIST_POLICY_DENY, + QAUTHZ_LIST_FORMAT_EXACT, &error_abort); + + g_assert(!qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort)); + + object_unparent(OBJECT(auth)); +} + +static void test_authz_explicit_allow(void) +{ + QAuthZList *auth = qauthz_list_new("auth0", + QAUTHZ_LIST_POLICY_DENY, + &error_abort); + + qauthz_list_append_rule(auth, "fred", QAUTHZ_LIST_POLICY_ALLOW, + QAUTHZ_LIST_FORMAT_EXACT, &error_abort); + + g_assert(qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort)); + + object_unparent(OBJECT(auth)); +} + + +static void test_authz_complex(void) +{ + QAuthZList *auth = qauthz_list_new("auth0", + QAUTHZ_LIST_POLICY_DENY, + &error_abort); + + qauthz_list_append_rule(auth, "fred", QAUTHZ_LIST_POLICY_ALLOW, + QAUTHZ_LIST_FORMAT_EXACT, &error_abort); + qauthz_list_append_rule(auth, "bob", QAUTHZ_LIST_POLICY_ALLOW, + QAUTHZ_LIST_FORMAT_EXACT, &error_abort); + qauthz_list_append_rule(auth, "dan", QAUTHZ_LIST_POLICY_DENY, + QAUTHZ_LIST_FORMAT_EXACT, &error_abort); + qauthz_list_append_rule(auth, "dan*", QAUTHZ_LIST_POLICY_ALLOW, + QAUTHZ_LIST_FORMAT_GLOB, &error_abort); + + g_assert(qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort)); + g_assert(qauthz_is_allowed(QAUTHZ(auth), "bob", &error_abort)); + g_assert(!qauthz_is_allowed(QAUTHZ(auth), "dan", &error_abort)); + g_assert(qauthz_is_allowed(QAUTHZ(auth), "danb", &error_abort)); + + object_unparent(OBJECT(auth)); +} + +static void test_authz_add_remove(void) +{ + QAuthZList *auth = qauthz_list_new("auth0", + QAUTHZ_LIST_POLICY_ALLOW, + &error_abort); + + g_assert_cmpint(qauthz_list_append_rule(auth, "fred", + QAUTHZ_LIST_POLICY_ALLOW, + QAUTHZ_LIST_FORMAT_EXACT, + &error_abort), + ==, 0); + g_assert_cmpint(qauthz_list_append_rule(auth, "bob", + QAUTHZ_LIST_POLICY_ALLOW, + QAUTHZ_LIST_FORMAT_EXACT, + &error_abort), + ==, 1); + g_assert_cmpint(qauthz_list_append_rule(auth, "dan", + QAUTHZ_LIST_POLICY_DENY, + QAUTHZ_LIST_FORMAT_EXACT, + &error_abort), + ==, 2); + g_assert_cmpint(qauthz_list_append_rule(auth, "frank", + QAUTHZ_LIST_POLICY_DENY, + QAUTHZ_LIST_FORMAT_EXACT, + &error_abort), + ==, 3); + + g_assert(!qauthz_is_allowed(QAUTHZ(auth), "dan", &error_abort)); + + g_assert_cmpint(qauthz_list_delete_rule(auth, "dan"), + ==, 2); + + g_assert(qauthz_is_allowed(QAUTHZ(auth), "dan", &error_abort)); + + g_assert_cmpint(qauthz_list_insert_rule(auth, "dan", + QAUTHZ_LIST_POLICY_DENY, + QAUTHZ_LIST_FORMAT_EXACT, + 2, + &error_abort), + ==, 2); + + g_assert(!qauthz_is_allowed(QAUTHZ(auth), "dan", &error_abort)); + + object_unparent(OBJECT(auth)); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + module_call_init(MODULE_INIT_QOM); + + g_test_add_func("/auth/list/default/deny", test_authz_default_deny); + g_test_add_func("/auth/list/default/allow", test_authz_default_allow); + g_test_add_func("/auth/list/explicit/deny", test_authz_explicit_deny); + g_test_add_func("/auth/list/explicit/allow", test_authz_explicit_allow); + g_test_add_func("/auth/list/complex", test_authz_complex); + g_test_add_func("/auth/list/add-remove", test_authz_add_remove); + + return g_test_run(); +} diff --git a/tests/unit/test-authz-listfile.c b/tests/unit/test-authz-listfile.c new file mode 100644 index 0000000000..64d0e1500f --- /dev/null +++ b/tests/unit/test-authz-listfile.c @@ -0,0 +1,196 @@ +/* + * QEMU list authorization object tests + * + * Copyright (c) 2018 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + * + */ + +#include "qemu/osdep.h" +#include "qemu/main-loop.h" +#include "qemu/module.h" +#include "authz/listfile.h" + +static char *workdir; + +static gchar *qemu_authz_listfile_test_save(const gchar *name, + const gchar *cfg) +{ + gchar *path = g_strdup_printf("%s/default-deny.cfg", workdir); + GError *gerr = NULL; + + if (!g_file_set_contents(path, cfg, -1, &gerr)) { + g_printerr("Unable to save config %s: %s\n", + path, gerr->message); + g_error_free(gerr); + g_free(path); + rmdir(workdir); + abort(); + } + + return path; +} + +static void test_authz_default_deny(void) +{ + gchar *file = qemu_authz_listfile_test_save( + "default-deny.cfg", + "{ \"policy\": \"deny\" }"); + Error *local_err = NULL; + + QAuthZListFile *auth = qauthz_list_file_new("auth0", + file, false, + &local_err); + unlink(file); + g_free(file); + g_assert(local_err == NULL); + g_assert(!qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort)); + + object_unparent(OBJECT(auth)); +} + +static void test_authz_default_allow(void) +{ + gchar *file = qemu_authz_listfile_test_save( + "default-allow.cfg", + "{ \"policy\": \"allow\" }"); + Error *local_err = NULL; + + QAuthZListFile *auth = qauthz_list_file_new("auth0", + file, false, + &local_err); + unlink(file); + g_free(file); + g_assert(local_err == NULL); + g_assert(qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort)); + + object_unparent(OBJECT(auth)); +} + +static void test_authz_explicit_deny(void) +{ + gchar *file = qemu_authz_listfile_test_save( + "explicit-deny.cfg", + "{ \"rules\": [ " + " { \"match\": \"fred\"," + " \"policy\": \"deny\"," + " \"format\": \"exact\" } ]," + " \"policy\": \"allow\" }"); + Error *local_err = NULL; + + QAuthZListFile *auth = qauthz_list_file_new("auth0", + file, false, + &local_err); + unlink(file); + g_free(file); + g_assert(local_err == NULL); + + g_assert(!qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort)); + + object_unparent(OBJECT(auth)); +} + +static void test_authz_explicit_allow(void) +{ + gchar *file = qemu_authz_listfile_test_save( + "explicit-allow.cfg", + "{ \"rules\": [ " + " { \"match\": \"fred\"," + " \"policy\": \"allow\"," + " \"format\": \"exact\" } ]," + " \"policy\": \"deny\" }"); + Error *local_err = NULL; + + QAuthZListFile *auth = qauthz_list_file_new("auth0", + file, false, + &local_err); + unlink(file); + g_free(file); + g_assert(local_err == NULL); + + g_assert(qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort)); + + object_unparent(OBJECT(auth)); +} + + +static void test_authz_complex(void) +{ + gchar *file = qemu_authz_listfile_test_save( + "complex.cfg", + "{ \"rules\": [ " + " { \"match\": \"fred\"," + " \"policy\": \"allow\"," + " \"format\": \"exact\" }," + " { \"match\": \"bob\"," + " \"policy\": \"allow\"," + " \"format\": \"exact\" }," + " { \"match\": \"dan\"," + " \"policy\": \"deny\"," + " \"format\": \"exact\" }," + " { \"match\": \"dan*\"," + " \"policy\": \"allow\"," + " \"format\": \"glob\" } ]," + " \"policy\": \"deny\" }"); + + Error *local_err = NULL; + + QAuthZListFile *auth = qauthz_list_file_new("auth0", + file, false, + &local_err); + unlink(file); + g_free(file); + g_assert(local_err == NULL); + + g_assert(qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort)); + g_assert(qauthz_is_allowed(QAUTHZ(auth), "bob", &error_abort)); + g_assert(!qauthz_is_allowed(QAUTHZ(auth), "dan", &error_abort)); + g_assert(qauthz_is_allowed(QAUTHZ(auth), "danb", &error_abort)); + + object_unparent(OBJECT(auth)); +} + + +int main(int argc, char **argv) +{ + int ret; + GError *gerr = NULL; + + g_test_init(&argc, &argv, NULL); + + module_call_init(MODULE_INIT_QOM); + + workdir = g_dir_make_tmp("qemu-test-authz-listfile-XXXXXX", + &gerr); + if (!workdir) { + g_printerr("Unable to create temporary dir: %s\n", + gerr->message); + g_error_free(gerr); + abort(); + } + + g_test_add_func("/auth/list/default/deny", test_authz_default_deny); + g_test_add_func("/auth/list/default/allow", test_authz_default_allow); + g_test_add_func("/auth/list/explicit/deny", test_authz_explicit_deny); + g_test_add_func("/auth/list/explicit/allow", test_authz_explicit_allow); + g_test_add_func("/auth/list/complex", test_authz_complex); + + ret = g_test_run(); + + rmdir(workdir); + g_free(workdir); + + return ret; +} diff --git a/tests/unit/test-authz-pam.c b/tests/unit/test-authz-pam.c new file mode 100644 index 0000000000..4fe1ef2603 --- /dev/null +++ b/tests/unit/test-authz-pam.c @@ -0,0 +1,133 @@ +/* + * QEMU PAM authorization object tests + * + * Copyright (c) 2018 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + * + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/module.h" +#include "authz/pamacct.h" + +#include + +static bool failauth; + +/* + * These three functions are exported by libpam.so. + * + * By defining them again here, our impls are resolved + * by the linker instead of those in libpam.so + * + * The test suite is thus isolated from the host system + * PAM setup, so we can do predictable test scenarios + */ +int +pam_start(const char *service_name, const char *user, + const struct pam_conv *pam_conversation, + pam_handle_t **pamh) +{ + failauth = true; + if (!g_str_equal(service_name, "qemu-vnc")) { + return PAM_AUTH_ERR; + } + + if (g_str_equal(user, "fred")) { + failauth = false; + } + + *pamh = (pam_handle_t *)0xbadeaffe; + return PAM_SUCCESS; +} + + +int +pam_acct_mgmt(pam_handle_t *pamh, int flags) +{ + if (failauth) { + return PAM_AUTH_ERR; + } + + return PAM_SUCCESS; +} + + +int +pam_end(pam_handle_t *pamh, int status) +{ + return PAM_SUCCESS; +} + + +static void test_authz_unknown_service(void) +{ + Error *local_err = NULL; + QAuthZPAM *auth = qauthz_pam_new("auth0", + "qemu-does-not-exist", + &error_abort); + + g_assert_nonnull(auth); + + g_assert_false(qauthz_is_allowed(QAUTHZ(auth), "fred", &local_err)); + + error_free_or_abort(&local_err); + object_unparent(OBJECT(auth)); +} + + +static void test_authz_good_user(void) +{ + QAuthZPAM *auth = qauthz_pam_new("auth0", + "qemu-vnc", + &error_abort); + + g_assert_nonnull(auth); + + g_assert_true(qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort)); + + object_unparent(OBJECT(auth)); +} + + +static void test_authz_bad_user(void) +{ + Error *local_err = NULL; + QAuthZPAM *auth = qauthz_pam_new("auth0", + "qemu-vnc", + &error_abort); + + g_assert_nonnull(auth); + + g_assert_false(qauthz_is_allowed(QAUTHZ(auth), "bob", &local_err)); + + error_free_or_abort(&local_err); + object_unparent(OBJECT(auth)); +} + + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + module_call_init(MODULE_INIT_QOM); + + g_test_add_func("/auth/pam/unknown-service", test_authz_unknown_service); + g_test_add_func("/auth/pam/good-user", test_authz_good_user); + g_test_add_func("/auth/pam/bad-user", test_authz_bad_user); + + return g_test_run(); +} diff --git a/tests/unit/test-authz-simple.c b/tests/unit/test-authz-simple.c new file mode 100644 index 0000000000..6f9034d8ff --- /dev/null +++ b/tests/unit/test-authz-simple.c @@ -0,0 +1,51 @@ +/* + * QEMU simple authorization object testing + * + * Copyright (c) 2018 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + * + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/module.h" + +#include "authz/simple.h" + + +static void test_authz_simple(void) +{ + QAuthZSimple *authz = qauthz_simple_new("authz0", + "cthulu", + &error_abort); + + g_assert(!qauthz_is_allowed(QAUTHZ(authz), "cthul", &error_abort)); + g_assert(qauthz_is_allowed(QAUTHZ(authz), "cthulu", &error_abort)); + g_assert(!qauthz_is_allowed(QAUTHZ(authz), "cthuluu", &error_abort)); + g_assert(!qauthz_is_allowed(QAUTHZ(authz), "fred", &error_abort)); + + object_unparent(OBJECT(authz)); +} + + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + module_call_init(MODULE_INIT_QOM); + + g_test_add_func("/authz/simple", test_authz_simple); + + return g_test_run(); +} diff --git a/tests/unit/test-base64.c b/tests/unit/test-base64.c new file mode 100644 index 0000000000..3012d7be26 --- /dev/null +++ b/tests/unit/test-base64.c @@ -0,0 +1,108 @@ +/* + * QEMU base64 helper test + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + * + */ + +#include "qemu/osdep.h" + +#include "qapi/error.h" +#include "qemu/base64.h" + +static void test_base64_good(void) +{ + const char input[] = + "QmVjYXVzZSB3ZSBmb2N1c2VkIG9uIHRoZSBzbmFrZSwgd2UgbW\n" + "lzc2VkIHRoZSBzY29ycGlvbi4="; + const char expect[] = "Because we focused on the snake, " + "we missed the scorpion."; + + size_t len; + uint8_t *actual = qbase64_decode(input, + -1, + &len, + &error_abort); + + g_assert(actual != NULL); + g_assert_cmpint(len, ==, strlen(expect)); + g_assert_cmpstr((char *)actual, ==, expect); + g_free(actual); +} + + +static void test_base64_bad(const char *input, + size_t input_len) +{ + size_t len; + Error *err = NULL; + uint8_t *actual = qbase64_decode(input, + input_len, + &len, + &err); + + error_free_or_abort(&err); + g_assert(actual == NULL); + g_assert_cmpint(len, ==, 0); +} + + +static void test_base64_embedded_nul(void) +{ + /* We put a NUL character in the middle of the base64 + * text which is invalid data, given the expected length */ + const char input[] = + "QmVjYXVzZSB3ZSBmb2N1c2VkIG9uIHRoZSBzbmFrZSwgd2UgbW\0" + "lzc2VkIHRoZSBzY29ycGlvbi4="; + + test_base64_bad(input, G_N_ELEMENTS(input) - 1); +} + + +static void test_base64_not_nul_terminated(void) +{ + const char input[] = + "QmVjYXVzZSB3ZSBmb2N1c2VkIG9uIHRoZSBzbmFrZSwgd2UgbW\n" + "lzc2VkIHRoZSBzY29ycGlvbi4="; + + /* Using '-2' to make us drop the trailing NUL, thus + * creating an invalid base64 sequence for decoding */ + test_base64_bad(input, G_N_ELEMENTS(input) - 2); +} + + +static void test_base64_invalid_chars(void) +{ + /* We put a single quote character in the middle + * of the base64 text which is invalid data */ + const char input[] = + "QmVjYXVzZSB3ZSBmb2N1c2VkIG9uIHRoZSBzbmFrZSwgd2UgbW'" + "lzc2VkIHRoZSBzY29ycGlvbi4="; + + test_base64_bad(input, strlen(input)); +} + + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + g_test_add_func("/util/base64/good", test_base64_good); + g_test_add_func("/util/base64/embedded-nul", test_base64_embedded_nul); + g_test_add_func("/util/base64/not-nul-terminated", + test_base64_not_nul_terminated); + g_test_add_func("/util/base64/invalid-chars", test_base64_invalid_chars); + return g_test_run(); +} diff --git a/tests/unit/test-bdrv-drain.c b/tests/unit/test-bdrv-drain.c new file mode 100644 index 0000000000..8a29e33e00 --- /dev/null +++ b/tests/unit/test-bdrv-drain.c @@ -0,0 +1,2230 @@ +/* + * Block node draining tests + * + * Copyright (c) 2017 Kevin Wolf + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "block/block.h" +#include "block/blockjob_int.h" +#include "sysemu/block-backend.h" +#include "qapi/error.h" +#include "qemu/main-loop.h" +#include "iothread.h" + +static QemuEvent done_event; + +typedef struct BDRVTestState { + int drain_count; + AioContext *bh_indirection_ctx; + bool sleep_in_drain_begin; +} BDRVTestState; + +static void coroutine_fn bdrv_test_co_drain_begin(BlockDriverState *bs) +{ + BDRVTestState *s = bs->opaque; + s->drain_count++; + if (s->sleep_in_drain_begin) { + qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 100000); + } +} + +static void coroutine_fn bdrv_test_co_drain_end(BlockDriverState *bs) +{ + BDRVTestState *s = bs->opaque; + s->drain_count--; +} + +static void bdrv_test_close(BlockDriverState *bs) +{ + BDRVTestState *s = bs->opaque; + g_assert_cmpint(s->drain_count, >, 0); +} + +static void co_reenter_bh(void *opaque) +{ + aio_co_wake(opaque); +} + +static int coroutine_fn bdrv_test_co_preadv(BlockDriverState *bs, + uint64_t offset, uint64_t bytes, + QEMUIOVector *qiov, int flags) +{ + BDRVTestState *s = bs->opaque; + + /* We want this request to stay until the polling loop in drain waits for + * it to complete. We need to sleep a while as bdrv_drain_invoke() comes + * first and polls its result, too, but it shouldn't accidentally complete + * this request yet. */ + qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 100000); + + if (s->bh_indirection_ctx) { + aio_bh_schedule_oneshot(s->bh_indirection_ctx, co_reenter_bh, + qemu_coroutine_self()); + qemu_coroutine_yield(); + } + + return 0; +} + +static int bdrv_test_change_backing_file(BlockDriverState *bs, + const char *backing_file, + const char *backing_fmt) +{ + return 0; +} + +static BlockDriver bdrv_test = { + .format_name = "test", + .instance_size = sizeof(BDRVTestState), + + .bdrv_close = bdrv_test_close, + .bdrv_co_preadv = bdrv_test_co_preadv, + + .bdrv_co_drain_begin = bdrv_test_co_drain_begin, + .bdrv_co_drain_end = bdrv_test_co_drain_end, + + .bdrv_child_perm = bdrv_default_perms, + + .bdrv_change_backing_file = bdrv_test_change_backing_file, +}; + +static void aio_ret_cb(void *opaque, int ret) +{ + int *aio_ret = opaque; + *aio_ret = ret; +} + +typedef struct CallInCoroutineData { + void (*entry)(void); + bool done; +} CallInCoroutineData; + +static coroutine_fn void call_in_coroutine_entry(void *opaque) +{ + CallInCoroutineData *data = opaque; + + data->entry(); + data->done = true; +} + +static void call_in_coroutine(void (*entry)(void)) +{ + Coroutine *co; + CallInCoroutineData data = { + .entry = entry, + .done = false, + }; + + co = qemu_coroutine_create(call_in_coroutine_entry, &data); + qemu_coroutine_enter(co); + while (!data.done) { + aio_poll(qemu_get_aio_context(), true); + } +} + +enum drain_type { + BDRV_DRAIN_ALL, + BDRV_DRAIN, + BDRV_SUBTREE_DRAIN, + DRAIN_TYPE_MAX, +}; + +static void do_drain_begin(enum drain_type drain_type, BlockDriverState *bs) +{ + switch (drain_type) { + case BDRV_DRAIN_ALL: bdrv_drain_all_begin(); break; + case BDRV_DRAIN: bdrv_drained_begin(bs); break; + case BDRV_SUBTREE_DRAIN: bdrv_subtree_drained_begin(bs); break; + default: g_assert_not_reached(); + } +} + +static void do_drain_end(enum drain_type drain_type, BlockDriverState *bs) +{ + switch (drain_type) { + case BDRV_DRAIN_ALL: bdrv_drain_all_end(); break; + case BDRV_DRAIN: bdrv_drained_end(bs); break; + case BDRV_SUBTREE_DRAIN: bdrv_subtree_drained_end(bs); break; + default: g_assert_not_reached(); + } +} + +static void do_drain_begin_unlocked(enum drain_type drain_type, BlockDriverState *bs) +{ + if (drain_type != BDRV_DRAIN_ALL) { + aio_context_acquire(bdrv_get_aio_context(bs)); + } + do_drain_begin(drain_type, bs); + if (drain_type != BDRV_DRAIN_ALL) { + aio_context_release(bdrv_get_aio_context(bs)); + } +} + +static void do_drain_end_unlocked(enum drain_type drain_type, BlockDriverState *bs) +{ + if (drain_type != BDRV_DRAIN_ALL) { + aio_context_acquire(bdrv_get_aio_context(bs)); + } + do_drain_end(drain_type, bs); + if (drain_type != BDRV_DRAIN_ALL) { + aio_context_release(bdrv_get_aio_context(bs)); + } +} + +static void test_drv_cb_common(enum drain_type drain_type, bool recursive) +{ + BlockBackend *blk; + BlockDriverState *bs, *backing; + BDRVTestState *s, *backing_s; + BlockAIOCB *acb; + int aio_ret; + + QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, NULL, 0); + + blk = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL); + bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR, + &error_abort); + s = bs->opaque; + blk_insert_bs(blk, bs, &error_abort); + + backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort); + backing_s = backing->opaque; + bdrv_set_backing_hd(bs, backing, &error_abort); + + /* Simple bdrv_drain_all_begin/end pair, check that CBs are called */ + g_assert_cmpint(s->drain_count, ==, 0); + g_assert_cmpint(backing_s->drain_count, ==, 0); + + do_drain_begin(drain_type, bs); + + g_assert_cmpint(s->drain_count, ==, 1); + g_assert_cmpint(backing_s->drain_count, ==, !!recursive); + + do_drain_end(drain_type, bs); + + g_assert_cmpint(s->drain_count, ==, 0); + g_assert_cmpint(backing_s->drain_count, ==, 0); + + /* Now do the same while a request is pending */ + aio_ret = -EINPROGRESS; + acb = blk_aio_preadv(blk, 0, &qiov, 0, aio_ret_cb, &aio_ret); + g_assert(acb != NULL); + g_assert_cmpint(aio_ret, ==, -EINPROGRESS); + + g_assert_cmpint(s->drain_count, ==, 0); + g_assert_cmpint(backing_s->drain_count, ==, 0); + + do_drain_begin(drain_type, bs); + + g_assert_cmpint(aio_ret, ==, 0); + g_assert_cmpint(s->drain_count, ==, 1); + g_assert_cmpint(backing_s->drain_count, ==, !!recursive); + + do_drain_end(drain_type, bs); + + g_assert_cmpint(s->drain_count, ==, 0); + g_assert_cmpint(backing_s->drain_count, ==, 0); + + bdrv_unref(backing); + bdrv_unref(bs); + blk_unref(blk); +} + +static void test_drv_cb_drain_all(void) +{ + test_drv_cb_common(BDRV_DRAIN_ALL, true); +} + +static void test_drv_cb_drain(void) +{ + test_drv_cb_common(BDRV_DRAIN, false); +} + +static void test_drv_cb_drain_subtree(void) +{ + test_drv_cb_common(BDRV_SUBTREE_DRAIN, true); +} + +static void test_drv_cb_co_drain_all(void) +{ + call_in_coroutine(test_drv_cb_drain_all); +} + +static void test_drv_cb_co_drain(void) +{ + call_in_coroutine(test_drv_cb_drain); +} + +static void test_drv_cb_co_drain_subtree(void) +{ + call_in_coroutine(test_drv_cb_drain_subtree); +} + +static void test_quiesce_common(enum drain_type drain_type, bool recursive) +{ + BlockBackend *blk; + BlockDriverState *bs, *backing; + + blk = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL); + bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR, + &error_abort); + blk_insert_bs(blk, bs, &error_abort); + + backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort); + bdrv_set_backing_hd(bs, backing, &error_abort); + + g_assert_cmpint(bs->quiesce_counter, ==, 0); + g_assert_cmpint(backing->quiesce_counter, ==, 0); + + do_drain_begin(drain_type, bs); + + g_assert_cmpint(bs->quiesce_counter, ==, 1); + g_assert_cmpint(backing->quiesce_counter, ==, !!recursive); + + do_drain_end(drain_type, bs); + + g_assert_cmpint(bs->quiesce_counter, ==, 0); + g_assert_cmpint(backing->quiesce_counter, ==, 0); + + bdrv_unref(backing); + bdrv_unref(bs); + blk_unref(blk); +} + +static void test_quiesce_drain_all(void) +{ + test_quiesce_common(BDRV_DRAIN_ALL, true); +} + +static void test_quiesce_drain(void) +{ + test_quiesce_common(BDRV_DRAIN, false); +} + +static void test_quiesce_drain_subtree(void) +{ + test_quiesce_common(BDRV_SUBTREE_DRAIN, true); +} + +static void test_quiesce_co_drain_all(void) +{ + call_in_coroutine(test_quiesce_drain_all); +} + +static void test_quiesce_co_drain(void) +{ + call_in_coroutine(test_quiesce_drain); +} + +static void test_quiesce_co_drain_subtree(void) +{ + call_in_coroutine(test_quiesce_drain_subtree); +} + +static void test_nested(void) +{ + BlockBackend *blk; + BlockDriverState *bs, *backing; + BDRVTestState *s, *backing_s; + enum drain_type outer, inner; + + blk = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL); + bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR, + &error_abort); + s = bs->opaque; + blk_insert_bs(blk, bs, &error_abort); + + backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort); + backing_s = backing->opaque; + bdrv_set_backing_hd(bs, backing, &error_abort); + + for (outer = 0; outer < DRAIN_TYPE_MAX; outer++) { + for (inner = 0; inner < DRAIN_TYPE_MAX; inner++) { + int backing_quiesce = (outer != BDRV_DRAIN) + + (inner != BDRV_DRAIN); + + g_assert_cmpint(bs->quiesce_counter, ==, 0); + g_assert_cmpint(backing->quiesce_counter, ==, 0); + g_assert_cmpint(s->drain_count, ==, 0); + g_assert_cmpint(backing_s->drain_count, ==, 0); + + do_drain_begin(outer, bs); + do_drain_begin(inner, bs); + + g_assert_cmpint(bs->quiesce_counter, ==, 2); + g_assert_cmpint(backing->quiesce_counter, ==, backing_quiesce); + g_assert_cmpint(s->drain_count, ==, 2); + g_assert_cmpint(backing_s->drain_count, ==, backing_quiesce); + + do_drain_end(inner, bs); + do_drain_end(outer, bs); + + g_assert_cmpint(bs->quiesce_counter, ==, 0); + g_assert_cmpint(backing->quiesce_counter, ==, 0); + g_assert_cmpint(s->drain_count, ==, 0); + g_assert_cmpint(backing_s->drain_count, ==, 0); + } + } + + bdrv_unref(backing); + bdrv_unref(bs); + blk_unref(blk); +} + +static void test_multiparent(void) +{ + BlockBackend *blk_a, *blk_b; + BlockDriverState *bs_a, *bs_b, *backing; + BDRVTestState *a_s, *b_s, *backing_s; + + blk_a = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL); + bs_a = bdrv_new_open_driver(&bdrv_test, "test-node-a", BDRV_O_RDWR, + &error_abort); + a_s = bs_a->opaque; + blk_insert_bs(blk_a, bs_a, &error_abort); + + blk_b = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL); + bs_b = bdrv_new_open_driver(&bdrv_test, "test-node-b", BDRV_O_RDWR, + &error_abort); + b_s = bs_b->opaque; + blk_insert_bs(blk_b, bs_b, &error_abort); + + backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort); + backing_s = backing->opaque; + bdrv_set_backing_hd(bs_a, backing, &error_abort); + bdrv_set_backing_hd(bs_b, backing, &error_abort); + + g_assert_cmpint(bs_a->quiesce_counter, ==, 0); + g_assert_cmpint(bs_b->quiesce_counter, ==, 0); + g_assert_cmpint(backing->quiesce_counter, ==, 0); + g_assert_cmpint(a_s->drain_count, ==, 0); + g_assert_cmpint(b_s->drain_count, ==, 0); + g_assert_cmpint(backing_s->drain_count, ==, 0); + + do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a); + + g_assert_cmpint(bs_a->quiesce_counter, ==, 1); + g_assert_cmpint(bs_b->quiesce_counter, ==, 1); + g_assert_cmpint(backing->quiesce_counter, ==, 1); + g_assert_cmpint(a_s->drain_count, ==, 1); + g_assert_cmpint(b_s->drain_count, ==, 1); + g_assert_cmpint(backing_s->drain_count, ==, 1); + + do_drain_begin(BDRV_SUBTREE_DRAIN, bs_b); + + g_assert_cmpint(bs_a->quiesce_counter, ==, 2); + g_assert_cmpint(bs_b->quiesce_counter, ==, 2); + g_assert_cmpint(backing->quiesce_counter, ==, 2); + g_assert_cmpint(a_s->drain_count, ==, 2); + g_assert_cmpint(b_s->drain_count, ==, 2); + g_assert_cmpint(backing_s->drain_count, ==, 2); + + do_drain_end(BDRV_SUBTREE_DRAIN, bs_b); + + g_assert_cmpint(bs_a->quiesce_counter, ==, 1); + g_assert_cmpint(bs_b->quiesce_counter, ==, 1); + g_assert_cmpint(backing->quiesce_counter, ==, 1); + g_assert_cmpint(a_s->drain_count, ==, 1); + g_assert_cmpint(b_s->drain_count, ==, 1); + g_assert_cmpint(backing_s->drain_count, ==, 1); + + do_drain_end(BDRV_SUBTREE_DRAIN, bs_a); + + g_assert_cmpint(bs_a->quiesce_counter, ==, 0); + g_assert_cmpint(bs_b->quiesce_counter, ==, 0); + g_assert_cmpint(backing->quiesce_counter, ==, 0); + g_assert_cmpint(a_s->drain_count, ==, 0); + g_assert_cmpint(b_s->drain_count, ==, 0); + g_assert_cmpint(backing_s->drain_count, ==, 0); + + bdrv_unref(backing); + bdrv_unref(bs_a); + bdrv_unref(bs_b); + blk_unref(blk_a); + blk_unref(blk_b); +} + +static void test_graph_change_drain_subtree(void) +{ + BlockBackend *blk_a, *blk_b; + BlockDriverState *bs_a, *bs_b, *backing; + BDRVTestState *a_s, *b_s, *backing_s; + + blk_a = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL); + bs_a = bdrv_new_open_driver(&bdrv_test, "test-node-a", BDRV_O_RDWR, + &error_abort); + a_s = bs_a->opaque; + blk_insert_bs(blk_a, bs_a, &error_abort); + + blk_b = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL); + bs_b = bdrv_new_open_driver(&bdrv_test, "test-node-b", BDRV_O_RDWR, + &error_abort); + b_s = bs_b->opaque; + blk_insert_bs(blk_b, bs_b, &error_abort); + + backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort); + backing_s = backing->opaque; + bdrv_set_backing_hd(bs_a, backing, &error_abort); + + g_assert_cmpint(bs_a->quiesce_counter, ==, 0); + g_assert_cmpint(bs_b->quiesce_counter, ==, 0); + g_assert_cmpint(backing->quiesce_counter, ==, 0); + g_assert_cmpint(a_s->drain_count, ==, 0); + g_assert_cmpint(b_s->drain_count, ==, 0); + g_assert_cmpint(backing_s->drain_count, ==, 0); + + do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a); + do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a); + do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a); + do_drain_begin(BDRV_SUBTREE_DRAIN, bs_b); + do_drain_begin(BDRV_SUBTREE_DRAIN, bs_b); + + bdrv_set_backing_hd(bs_b, backing, &error_abort); + g_assert_cmpint(bs_a->quiesce_counter, ==, 5); + g_assert_cmpint(bs_b->quiesce_counter, ==, 5); + g_assert_cmpint(backing->quiesce_counter, ==, 5); + g_assert_cmpint(a_s->drain_count, ==, 5); + g_assert_cmpint(b_s->drain_count, ==, 5); + g_assert_cmpint(backing_s->drain_count, ==, 5); + + bdrv_set_backing_hd(bs_b, NULL, &error_abort); + g_assert_cmpint(bs_a->quiesce_counter, ==, 3); + g_assert_cmpint(bs_b->quiesce_counter, ==, 2); + g_assert_cmpint(backing->quiesce_counter, ==, 3); + g_assert_cmpint(a_s->drain_count, ==, 3); + g_assert_cmpint(b_s->drain_count, ==, 2); + g_assert_cmpint(backing_s->drain_count, ==, 3); + + bdrv_set_backing_hd(bs_b, backing, &error_abort); + g_assert_cmpint(bs_a->quiesce_counter, ==, 5); + g_assert_cmpint(bs_b->quiesce_counter, ==, 5); + g_assert_cmpint(backing->quiesce_counter, ==, 5); + g_assert_cmpint(a_s->drain_count, ==, 5); + g_assert_cmpint(b_s->drain_count, ==, 5); + g_assert_cmpint(backing_s->drain_count, ==, 5); + + do_drain_end(BDRV_SUBTREE_DRAIN, bs_b); + do_drain_end(BDRV_SUBTREE_DRAIN, bs_b); + do_drain_end(BDRV_SUBTREE_DRAIN, bs_a); + do_drain_end(BDRV_SUBTREE_DRAIN, bs_a); + do_drain_end(BDRV_SUBTREE_DRAIN, bs_a); + + g_assert_cmpint(bs_a->quiesce_counter, ==, 0); + g_assert_cmpint(bs_b->quiesce_counter, ==, 0); + g_assert_cmpint(backing->quiesce_counter, ==, 0); + g_assert_cmpint(a_s->drain_count, ==, 0); + g_assert_cmpint(b_s->drain_count, ==, 0); + g_assert_cmpint(backing_s->drain_count, ==, 0); + + bdrv_unref(backing); + bdrv_unref(bs_a); + bdrv_unref(bs_b); + blk_unref(blk_a); + blk_unref(blk_b); +} + +static void test_graph_change_drain_all(void) +{ + BlockBackend *blk_a, *blk_b; + BlockDriverState *bs_a, *bs_b; + BDRVTestState *a_s, *b_s; + + /* Create node A with a BlockBackend */ + blk_a = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL); + bs_a = bdrv_new_open_driver(&bdrv_test, "test-node-a", BDRV_O_RDWR, + &error_abort); + a_s = bs_a->opaque; + blk_insert_bs(blk_a, bs_a, &error_abort); + + g_assert_cmpint(bs_a->quiesce_counter, ==, 0); + g_assert_cmpint(a_s->drain_count, ==, 0); + + /* Call bdrv_drain_all_begin() */ + bdrv_drain_all_begin(); + + g_assert_cmpint(bs_a->quiesce_counter, ==, 1); + g_assert_cmpint(a_s->drain_count, ==, 1); + + /* Create node B with a BlockBackend */ + blk_b = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL); + bs_b = bdrv_new_open_driver(&bdrv_test, "test-node-b", BDRV_O_RDWR, + &error_abort); + b_s = bs_b->opaque; + blk_insert_bs(blk_b, bs_b, &error_abort); + + g_assert_cmpint(bs_a->quiesce_counter, ==, 1); + g_assert_cmpint(bs_b->quiesce_counter, ==, 1); + g_assert_cmpint(a_s->drain_count, ==, 1); + g_assert_cmpint(b_s->drain_count, ==, 1); + + /* Unref and finally delete node A */ + blk_unref(blk_a); + + g_assert_cmpint(bs_a->quiesce_counter, ==, 1); + g_assert_cmpint(bs_b->quiesce_counter, ==, 1); + g_assert_cmpint(a_s->drain_count, ==, 1); + g_assert_cmpint(b_s->drain_count, ==, 1); + + bdrv_unref(bs_a); + + g_assert_cmpint(bs_b->quiesce_counter, ==, 1); + g_assert_cmpint(b_s->drain_count, ==, 1); + + /* End the drained section */ + bdrv_drain_all_end(); + + g_assert_cmpint(bs_b->quiesce_counter, ==, 0); + g_assert_cmpint(b_s->drain_count, ==, 0); + g_assert_cmpint(qemu_get_aio_context()->external_disable_cnt, ==, 0); + + bdrv_unref(bs_b); + blk_unref(blk_b); +} + +struct test_iothread_data { + BlockDriverState *bs; + enum drain_type drain_type; + int *aio_ret; +}; + +static void test_iothread_drain_entry(void *opaque) +{ + struct test_iothread_data *data = opaque; + + aio_context_acquire(bdrv_get_aio_context(data->bs)); + do_drain_begin(data->drain_type, data->bs); + g_assert_cmpint(*data->aio_ret, ==, 0); + do_drain_end(data->drain_type, data->bs); + aio_context_release(bdrv_get_aio_context(data->bs)); + + qemu_event_set(&done_event); +} + +static void test_iothread_aio_cb(void *opaque, int ret) +{ + int *aio_ret = opaque; + *aio_ret = ret; + qemu_event_set(&done_event); +} + +static void test_iothread_main_thread_bh(void *opaque) +{ + struct test_iothread_data *data = opaque; + + /* Test that the AioContext is not yet locked in a random BH that is + * executed during drain, otherwise this would deadlock. */ + aio_context_acquire(bdrv_get_aio_context(data->bs)); + bdrv_flush(data->bs); + aio_context_release(bdrv_get_aio_context(data->bs)); +} + +/* + * Starts an AIO request on a BDS that runs in the AioContext of iothread 1. + * The request involves a BH on iothread 2 before it can complete. + * + * @drain_thread = 0 means that do_drain_begin/end are called from the main + * thread, @drain_thread = 1 means that they are called from iothread 1. Drain + * for this BDS cannot be called from iothread 2 because only the main thread + * may do cross-AioContext polling. + */ +static void test_iothread_common(enum drain_type drain_type, int drain_thread) +{ + BlockBackend *blk; + BlockDriverState *bs; + BDRVTestState *s; + BlockAIOCB *acb; + int aio_ret; + struct test_iothread_data data; + + IOThread *a = iothread_new(); + IOThread *b = iothread_new(); + AioContext *ctx_a = iothread_get_aio_context(a); + AioContext *ctx_b = iothread_get_aio_context(b); + + QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, NULL, 0); + + /* bdrv_drain_all() may only be called from the main loop thread */ + if (drain_type == BDRV_DRAIN_ALL && drain_thread != 0) { + goto out; + } + + blk = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL); + bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR, + &error_abort); + s = bs->opaque; + blk_insert_bs(blk, bs, &error_abort); + blk_set_disable_request_queuing(blk, true); + + blk_set_aio_context(blk, ctx_a, &error_abort); + aio_context_acquire(ctx_a); + + s->bh_indirection_ctx = ctx_b; + + aio_ret = -EINPROGRESS; + qemu_event_reset(&done_event); + + if (drain_thread == 0) { + acb = blk_aio_preadv(blk, 0, &qiov, 0, test_iothread_aio_cb, &aio_ret); + } else { + acb = blk_aio_preadv(blk, 0, &qiov, 0, aio_ret_cb, &aio_ret); + } + g_assert(acb != NULL); + g_assert_cmpint(aio_ret, ==, -EINPROGRESS); + + aio_context_release(ctx_a); + + data = (struct test_iothread_data) { + .bs = bs, + .drain_type = drain_type, + .aio_ret = &aio_ret, + }; + + switch (drain_thread) { + case 0: + if (drain_type != BDRV_DRAIN_ALL) { + aio_context_acquire(ctx_a); + } + + aio_bh_schedule_oneshot(ctx_a, test_iothread_main_thread_bh, &data); + + /* The request is running on the IOThread a. Draining its block device + * will make sure that it has completed as far as the BDS is concerned, + * but the drain in this thread can continue immediately after + * bdrv_dec_in_flight() and aio_ret might be assigned only slightly + * later. */ + do_drain_begin(drain_type, bs); + g_assert_cmpint(bs->in_flight, ==, 0); + + if (drain_type != BDRV_DRAIN_ALL) { + aio_context_release(ctx_a); + } + qemu_event_wait(&done_event); + if (drain_type != BDRV_DRAIN_ALL) { + aio_context_acquire(ctx_a); + } + + g_assert_cmpint(aio_ret, ==, 0); + do_drain_end(drain_type, bs); + + if (drain_type != BDRV_DRAIN_ALL) { + aio_context_release(ctx_a); + } + break; + case 1: + aio_bh_schedule_oneshot(ctx_a, test_iothread_drain_entry, &data); + qemu_event_wait(&done_event); + break; + default: + g_assert_not_reached(); + } + + aio_context_acquire(ctx_a); + blk_set_aio_context(blk, qemu_get_aio_context(), &error_abort); + aio_context_release(ctx_a); + + bdrv_unref(bs); + blk_unref(blk); + +out: + iothread_join(a); + iothread_join(b); +} + +static void test_iothread_drain_all(void) +{ + test_iothread_common(BDRV_DRAIN_ALL, 0); + test_iothread_common(BDRV_DRAIN_ALL, 1); +} + +static void test_iothread_drain(void) +{ + test_iothread_common(BDRV_DRAIN, 0); + test_iothread_common(BDRV_DRAIN, 1); +} + +static void test_iothread_drain_subtree(void) +{ + test_iothread_common(BDRV_SUBTREE_DRAIN, 0); + test_iothread_common(BDRV_SUBTREE_DRAIN, 1); +} + + +typedef struct TestBlockJob { + BlockJob common; + int run_ret; + int prepare_ret; + bool running; + bool should_complete; +} TestBlockJob; + +static int test_job_prepare(Job *job) +{ + TestBlockJob *s = container_of(job, TestBlockJob, common.job); + + /* Provoke an AIO_WAIT_WHILE() call to verify there is no deadlock */ + blk_flush(s->common.blk); + return s->prepare_ret; +} + +static void test_job_commit(Job *job) +{ + TestBlockJob *s = container_of(job, TestBlockJob, common.job); + + /* Provoke an AIO_WAIT_WHILE() call to verify there is no deadlock */ + blk_flush(s->common.blk); +} + +static void test_job_abort(Job *job) +{ + TestBlockJob *s = container_of(job, TestBlockJob, common.job); + + /* Provoke an AIO_WAIT_WHILE() call to verify there is no deadlock */ + blk_flush(s->common.blk); +} + +static int coroutine_fn test_job_run(Job *job, Error **errp) +{ + TestBlockJob *s = container_of(job, TestBlockJob, common.job); + + /* We are running the actual job code past the pause point in + * job_co_entry(). */ + s->running = true; + + job_transition_to_ready(&s->common.job); + while (!s->should_complete) { + /* Avoid job_sleep_ns() because it marks the job as !busy. We want to + * emulate some actual activity (probably some I/O) here so that drain + * has to wait for this activity to stop. */ + qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 1000000); + + job_pause_point(&s->common.job); + } + + return s->run_ret; +} + +static void test_job_complete(Job *job, Error **errp) +{ + TestBlockJob *s = container_of(job, TestBlockJob, common.job); + s->should_complete = true; +} + +BlockJobDriver test_job_driver = { + .job_driver = { + .instance_size = sizeof(TestBlockJob), + .free = block_job_free, + .user_resume = block_job_user_resume, + .run = test_job_run, + .complete = test_job_complete, + .prepare = test_job_prepare, + .commit = test_job_commit, + .abort = test_job_abort, + }, +}; + +enum test_job_result { + TEST_JOB_SUCCESS, + TEST_JOB_FAIL_RUN, + TEST_JOB_FAIL_PREPARE, +}; + +enum test_job_drain_node { + TEST_JOB_DRAIN_SRC, + TEST_JOB_DRAIN_SRC_CHILD, + TEST_JOB_DRAIN_SRC_PARENT, +}; + +static void test_blockjob_common_drain_node(enum drain_type drain_type, + bool use_iothread, + enum test_job_result result, + enum test_job_drain_node drain_node) +{ + BlockBackend *blk_src, *blk_target; + BlockDriverState *src, *src_backing, *src_overlay, *target, *drain_bs; + BlockJob *job; + TestBlockJob *tjob; + IOThread *iothread = NULL; + AioContext *ctx; + int ret; + + src = bdrv_new_open_driver(&bdrv_test, "source", BDRV_O_RDWR, + &error_abort); + src_backing = bdrv_new_open_driver(&bdrv_test, "source-backing", + BDRV_O_RDWR, &error_abort); + src_overlay = bdrv_new_open_driver(&bdrv_test, "source-overlay", + BDRV_O_RDWR, &error_abort); + + bdrv_set_backing_hd(src_overlay, src, &error_abort); + bdrv_unref(src); + bdrv_set_backing_hd(src, src_backing, &error_abort); + bdrv_unref(src_backing); + + blk_src = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL); + blk_insert_bs(blk_src, src_overlay, &error_abort); + + switch (drain_node) { + case TEST_JOB_DRAIN_SRC: + drain_bs = src; + break; + case TEST_JOB_DRAIN_SRC_CHILD: + drain_bs = src_backing; + break; + case TEST_JOB_DRAIN_SRC_PARENT: + drain_bs = src_overlay; + break; + default: + g_assert_not_reached(); + } + + if (use_iothread) { + iothread = iothread_new(); + ctx = iothread_get_aio_context(iothread); + blk_set_aio_context(blk_src, ctx, &error_abort); + } else { + ctx = qemu_get_aio_context(); + } + + target = bdrv_new_open_driver(&bdrv_test, "target", BDRV_O_RDWR, + &error_abort); + blk_target = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL); + blk_insert_bs(blk_target, target, &error_abort); + blk_set_allow_aio_context_change(blk_target, true); + + aio_context_acquire(ctx); + tjob = block_job_create("job0", &test_job_driver, NULL, src, + 0, BLK_PERM_ALL, + 0, 0, NULL, NULL, &error_abort); + job = &tjob->common; + block_job_add_bdrv(job, "target", target, 0, BLK_PERM_ALL, &error_abort); + + switch (result) { + case TEST_JOB_SUCCESS: + break; + case TEST_JOB_FAIL_RUN: + tjob->run_ret = -EIO; + break; + case TEST_JOB_FAIL_PREPARE: + tjob->prepare_ret = -EIO; + break; + } + + job_start(&job->job); + aio_context_release(ctx); + + if (use_iothread) { + /* job_co_entry() is run in the I/O thread, wait for the actual job + * code to start (we don't want to catch the job in the pause point in + * job_co_entry(). */ + while (!tjob->running) { + aio_poll(qemu_get_aio_context(), false); + } + } + + g_assert_cmpint(job->job.pause_count, ==, 0); + g_assert_false(job->job.paused); + g_assert_true(tjob->running); + g_assert_true(job->job.busy); /* We're in qemu_co_sleep_ns() */ + + do_drain_begin_unlocked(drain_type, drain_bs); + + if (drain_type == BDRV_DRAIN_ALL) { + /* bdrv_drain_all() drains both src and target */ + g_assert_cmpint(job->job.pause_count, ==, 2); + } else { + g_assert_cmpint(job->job.pause_count, ==, 1); + } + g_assert_true(job->job.paused); + g_assert_false(job->job.busy); /* The job is paused */ + + do_drain_end_unlocked(drain_type, drain_bs); + + if (use_iothread) { + /* paused is reset in the I/O thread, wait for it */ + while (job->job.paused) { + aio_poll(qemu_get_aio_context(), false); + } + } + + g_assert_cmpint(job->job.pause_count, ==, 0); + g_assert_false(job->job.paused); + g_assert_true(job->job.busy); /* We're in qemu_co_sleep_ns() */ + + do_drain_begin_unlocked(drain_type, target); + + if (drain_type == BDRV_DRAIN_ALL) { + /* bdrv_drain_all() drains both src and target */ + g_assert_cmpint(job->job.pause_count, ==, 2); + } else { + g_assert_cmpint(job->job.pause_count, ==, 1); + } + g_assert_true(job->job.paused); + g_assert_false(job->job.busy); /* The job is paused */ + + do_drain_end_unlocked(drain_type, target); + + if (use_iothread) { + /* paused is reset in the I/O thread, wait for it */ + while (job->job.paused) { + aio_poll(qemu_get_aio_context(), false); + } + } + + g_assert_cmpint(job->job.pause_count, ==, 0); + g_assert_false(job->job.paused); + g_assert_true(job->job.busy); /* We're in qemu_co_sleep_ns() */ + + aio_context_acquire(ctx); + ret = job_complete_sync(&job->job, &error_abort); + g_assert_cmpint(ret, ==, (result == TEST_JOB_SUCCESS ? 0 : -EIO)); + + if (use_iothread) { + blk_set_aio_context(blk_src, qemu_get_aio_context(), &error_abort); + assert(blk_get_aio_context(blk_target) == qemu_get_aio_context()); + } + aio_context_release(ctx); + + blk_unref(blk_src); + blk_unref(blk_target); + bdrv_unref(src_overlay); + bdrv_unref(target); + + if (iothread) { + iothread_join(iothread); + } +} + +static void test_blockjob_common(enum drain_type drain_type, bool use_iothread, + enum test_job_result result) +{ + test_blockjob_common_drain_node(drain_type, use_iothread, result, + TEST_JOB_DRAIN_SRC); + test_blockjob_common_drain_node(drain_type, use_iothread, result, + TEST_JOB_DRAIN_SRC_CHILD); + if (drain_type == BDRV_SUBTREE_DRAIN) { + test_blockjob_common_drain_node(drain_type, use_iothread, result, + TEST_JOB_DRAIN_SRC_PARENT); + } +} + +static void test_blockjob_drain_all(void) +{ + test_blockjob_common(BDRV_DRAIN_ALL, false, TEST_JOB_SUCCESS); +} + +static void test_blockjob_drain(void) +{ + test_blockjob_common(BDRV_DRAIN, false, TEST_JOB_SUCCESS); +} + +static void test_blockjob_drain_subtree(void) +{ + test_blockjob_common(BDRV_SUBTREE_DRAIN, false, TEST_JOB_SUCCESS); +} + +static void test_blockjob_error_drain_all(void) +{ + test_blockjob_common(BDRV_DRAIN_ALL, false, TEST_JOB_FAIL_RUN); + test_blockjob_common(BDRV_DRAIN_ALL, false, TEST_JOB_FAIL_PREPARE); +} + +static void test_blockjob_error_drain(void) +{ + test_blockjob_common(BDRV_DRAIN, false, TEST_JOB_FAIL_RUN); + test_blockjob_common(BDRV_DRAIN, false, TEST_JOB_FAIL_PREPARE); +} + +static void test_blockjob_error_drain_subtree(void) +{ + test_blockjob_common(BDRV_SUBTREE_DRAIN, false, TEST_JOB_FAIL_RUN); + test_blockjob_common(BDRV_SUBTREE_DRAIN, false, TEST_JOB_FAIL_PREPARE); +} + +static void test_blockjob_iothread_drain_all(void) +{ + test_blockjob_common(BDRV_DRAIN_ALL, true, TEST_JOB_SUCCESS); +} + +static void test_blockjob_iothread_drain(void) +{ + test_blockjob_common(BDRV_DRAIN, true, TEST_JOB_SUCCESS); +} + +static void test_blockjob_iothread_drain_subtree(void) +{ + test_blockjob_common(BDRV_SUBTREE_DRAIN, true, TEST_JOB_SUCCESS); +} + +static void test_blockjob_iothread_error_drain_all(void) +{ + test_blockjob_common(BDRV_DRAIN_ALL, true, TEST_JOB_FAIL_RUN); + test_blockjob_common(BDRV_DRAIN_ALL, true, TEST_JOB_FAIL_PREPARE); +} + +static void test_blockjob_iothread_error_drain(void) +{ + test_blockjob_common(BDRV_DRAIN, true, TEST_JOB_FAIL_RUN); + test_blockjob_common(BDRV_DRAIN, true, TEST_JOB_FAIL_PREPARE); +} + +static void test_blockjob_iothread_error_drain_subtree(void) +{ + test_blockjob_common(BDRV_SUBTREE_DRAIN, true, TEST_JOB_FAIL_RUN); + test_blockjob_common(BDRV_SUBTREE_DRAIN, true, TEST_JOB_FAIL_PREPARE); +} + + +typedef struct BDRVTestTopState { + BdrvChild *wait_child; +} BDRVTestTopState; + +static void bdrv_test_top_close(BlockDriverState *bs) +{ + BdrvChild *c, *next_c; + QLIST_FOREACH_SAFE(c, &bs->children, next, next_c) { + bdrv_unref_child(bs, c); + } +} + +static int coroutine_fn bdrv_test_top_co_preadv(BlockDriverState *bs, + uint64_t offset, uint64_t bytes, + QEMUIOVector *qiov, int flags) +{ + BDRVTestTopState *tts = bs->opaque; + return bdrv_co_preadv(tts->wait_child, offset, bytes, qiov, flags); +} + +static BlockDriver bdrv_test_top_driver = { + .format_name = "test_top_driver", + .instance_size = sizeof(BDRVTestTopState), + + .bdrv_close = bdrv_test_top_close, + .bdrv_co_preadv = bdrv_test_top_co_preadv, + + .bdrv_child_perm = bdrv_default_perms, +}; + +typedef struct TestCoDeleteByDrainData { + BlockBackend *blk; + bool detach_instead_of_delete; + bool done; +} TestCoDeleteByDrainData; + +static void coroutine_fn test_co_delete_by_drain(void *opaque) +{ + TestCoDeleteByDrainData *dbdd = opaque; + BlockBackend *blk = dbdd->blk; + BlockDriverState *bs = blk_bs(blk); + BDRVTestTopState *tts = bs->opaque; + void *buffer = g_malloc(65536); + QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buffer, 65536); + + /* Pretend some internal write operation from parent to child. + * Important: We have to read from the child, not from the parent! + * Draining works by first propagating it all up the tree to the + * root and then waiting for drainage from root to the leaves + * (protocol nodes). If we have a request waiting on the root, + * everything will be drained before we go back down the tree, but + * we do not want that. We want to be in the middle of draining + * when this following requests returns. */ + bdrv_co_preadv(tts->wait_child, 0, 65536, &qiov, 0); + + g_assert_cmpint(bs->refcnt, ==, 1); + + if (!dbdd->detach_instead_of_delete) { + blk_unref(blk); + } else { + BdrvChild *c, *next_c; + QLIST_FOREACH_SAFE(c, &bs->children, next, next_c) { + bdrv_unref_child(bs, c); + } + } + + dbdd->done = true; + g_free(buffer); +} + +/** + * Test what happens when some BDS has some children, you drain one of + * them and this results in the BDS being deleted. + * + * If @detach_instead_of_delete is set, the BDS is not going to be + * deleted but will only detach all of its children. + */ +static void do_test_delete_by_drain(bool detach_instead_of_delete, + enum drain_type drain_type) +{ + BlockBackend *blk; + BlockDriverState *bs, *child_bs, *null_bs; + BDRVTestTopState *tts; + TestCoDeleteByDrainData dbdd; + Coroutine *co; + + bs = bdrv_new_open_driver(&bdrv_test_top_driver, "top", BDRV_O_RDWR, + &error_abort); + bs->total_sectors = 65536 >> BDRV_SECTOR_BITS; + tts = bs->opaque; + + null_bs = bdrv_open("null-co://", NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL, + &error_abort); + bdrv_attach_child(bs, null_bs, "null-child", &child_of_bds, + BDRV_CHILD_DATA, &error_abort); + + /* This child will be the one to pass to requests through to, and + * it will stall until a drain occurs */ + child_bs = bdrv_new_open_driver(&bdrv_test, "child", BDRV_O_RDWR, + &error_abort); + child_bs->total_sectors = 65536 >> BDRV_SECTOR_BITS; + /* Takes our reference to child_bs */ + tts->wait_child = bdrv_attach_child(bs, child_bs, "wait-child", + &child_of_bds, + BDRV_CHILD_DATA | BDRV_CHILD_PRIMARY, + &error_abort); + + /* This child is just there to be deleted + * (for detach_instead_of_delete == true) */ + null_bs = bdrv_open("null-co://", NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL, + &error_abort); + bdrv_attach_child(bs, null_bs, "null-child", &child_of_bds, BDRV_CHILD_DATA, + &error_abort); + + blk = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL); + blk_insert_bs(blk, bs, &error_abort); + + /* Referenced by blk now */ + bdrv_unref(bs); + + g_assert_cmpint(bs->refcnt, ==, 1); + g_assert_cmpint(child_bs->refcnt, ==, 1); + g_assert_cmpint(null_bs->refcnt, ==, 1); + + + dbdd = (TestCoDeleteByDrainData){ + .blk = blk, + .detach_instead_of_delete = detach_instead_of_delete, + .done = false, + }; + co = qemu_coroutine_create(test_co_delete_by_drain, &dbdd); + qemu_coroutine_enter(co); + + /* Drain the child while the read operation is still pending. + * This should result in the operation finishing and + * test_co_delete_by_drain() resuming. Thus, @bs will be deleted + * and the coroutine will exit while this drain operation is still + * in progress. */ + switch (drain_type) { + case BDRV_DRAIN: + bdrv_ref(child_bs); + bdrv_drain(child_bs); + bdrv_unref(child_bs); + break; + case BDRV_SUBTREE_DRAIN: + /* Would have to ref/unref bs here for !detach_instead_of_delete, but + * then the whole test becomes pointless because the graph changes + * don't occur during the drain any more. */ + assert(detach_instead_of_delete); + bdrv_subtree_drained_begin(bs); + bdrv_subtree_drained_end(bs); + break; + case BDRV_DRAIN_ALL: + bdrv_drain_all_begin(); + bdrv_drain_all_end(); + break; + default: + g_assert_not_reached(); + } + + while (!dbdd.done) { + aio_poll(qemu_get_aio_context(), true); + } + + if (detach_instead_of_delete) { + /* Here, the reference has not passed over to the coroutine, + * so we have to delete the BB ourselves */ + blk_unref(blk); + } +} + +static void test_delete_by_drain(void) +{ + do_test_delete_by_drain(false, BDRV_DRAIN); +} + +static void test_detach_by_drain_all(void) +{ + do_test_delete_by_drain(true, BDRV_DRAIN_ALL); +} + +static void test_detach_by_drain(void) +{ + do_test_delete_by_drain(true, BDRV_DRAIN); +} + +static void test_detach_by_drain_subtree(void) +{ + do_test_delete_by_drain(true, BDRV_SUBTREE_DRAIN); +} + + +struct detach_by_parent_data { + BlockDriverState *parent_b; + BdrvChild *child_b; + BlockDriverState *c; + BdrvChild *child_c; + bool by_parent_cb; +}; +static struct detach_by_parent_data detach_by_parent_data; + +static void detach_indirect_bh(void *opaque) +{ + struct detach_by_parent_data *data = opaque; + + bdrv_unref_child(data->parent_b, data->child_b); + + bdrv_ref(data->c); + data->child_c = bdrv_attach_child(data->parent_b, data->c, "PB-C", + &child_of_bds, BDRV_CHILD_DATA, + &error_abort); +} + +static void detach_by_parent_aio_cb(void *opaque, int ret) +{ + struct detach_by_parent_data *data = &detach_by_parent_data; + + g_assert_cmpint(ret, ==, 0); + if (data->by_parent_cb) { + detach_indirect_bh(data); + } +} + +static void detach_by_driver_cb_drained_begin(BdrvChild *child) +{ + aio_bh_schedule_oneshot(qemu_get_current_aio_context(), + detach_indirect_bh, &detach_by_parent_data); + child_of_bds.drained_begin(child); +} + +static BdrvChildClass detach_by_driver_cb_class; + +/* + * Initial graph: + * + * PA PB + * \ / \ + * A B C + * + * by_parent_cb == true: Test that parent callbacks don't poll + * + * PA has a pending write request whose callback changes the child nodes of + * PB: It removes B and adds C instead. The subtree of PB is drained, which + * will indirectly drain the write request, too. + * + * by_parent_cb == false: Test that bdrv_drain_invoke() doesn't poll + * + * PA's BdrvChildClass has a .drained_begin callback that schedules a BH + * that does the same graph change. If bdrv_drain_invoke() calls it, the + * state is messed up, but if it is only polled in the single + * BDRV_POLL_WHILE() at the end of the drain, this should work fine. + */ +static void test_detach_indirect(bool by_parent_cb) +{ + BlockBackend *blk; + BlockDriverState *parent_a, *parent_b, *a, *b, *c; + BdrvChild *child_a, *child_b; + BlockAIOCB *acb; + + QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, NULL, 0); + + if (!by_parent_cb) { + detach_by_driver_cb_class = child_of_bds; + detach_by_driver_cb_class.drained_begin = + detach_by_driver_cb_drained_begin; + } + + /* Create all involved nodes */ + parent_a = bdrv_new_open_driver(&bdrv_test, "parent-a", BDRV_O_RDWR, + &error_abort); + parent_b = bdrv_new_open_driver(&bdrv_test, "parent-b", 0, + &error_abort); + + a = bdrv_new_open_driver(&bdrv_test, "a", BDRV_O_RDWR, &error_abort); + b = bdrv_new_open_driver(&bdrv_test, "b", BDRV_O_RDWR, &error_abort); + c = bdrv_new_open_driver(&bdrv_test, "c", BDRV_O_RDWR, &error_abort); + + /* blk is a BB for parent-a */ + blk = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL); + blk_insert_bs(blk, parent_a, &error_abort); + bdrv_unref(parent_a); + + /* If we want to get bdrv_drain_invoke() to call aio_poll(), the driver + * callback must not return immediately. */ + if (!by_parent_cb) { + BDRVTestState *s = parent_a->opaque; + s->sleep_in_drain_begin = true; + } + + /* Set child relationships */ + bdrv_ref(b); + bdrv_ref(a); + child_b = bdrv_attach_child(parent_b, b, "PB-B", &child_of_bds, + BDRV_CHILD_DATA, &error_abort); + child_a = bdrv_attach_child(parent_b, a, "PB-A", &child_of_bds, + BDRV_CHILD_COW, &error_abort); + + bdrv_ref(a); + bdrv_attach_child(parent_a, a, "PA-A", + by_parent_cb ? &child_of_bds : &detach_by_driver_cb_class, + BDRV_CHILD_DATA, &error_abort); + + g_assert_cmpint(parent_a->refcnt, ==, 1); + g_assert_cmpint(parent_b->refcnt, ==, 1); + g_assert_cmpint(a->refcnt, ==, 3); + g_assert_cmpint(b->refcnt, ==, 2); + g_assert_cmpint(c->refcnt, ==, 1); + + g_assert(QLIST_FIRST(&parent_b->children) == child_a); + g_assert(QLIST_NEXT(child_a, next) == child_b); + g_assert(QLIST_NEXT(child_b, next) == NULL); + + /* Start the evil write request */ + detach_by_parent_data = (struct detach_by_parent_data) { + .parent_b = parent_b, + .child_b = child_b, + .c = c, + .by_parent_cb = by_parent_cb, + }; + acb = blk_aio_preadv(blk, 0, &qiov, 0, detach_by_parent_aio_cb, NULL); + g_assert(acb != NULL); + + /* Drain and check the expected result */ + bdrv_subtree_drained_begin(parent_b); + + g_assert(detach_by_parent_data.child_c != NULL); + + g_assert_cmpint(parent_a->refcnt, ==, 1); + g_assert_cmpint(parent_b->refcnt, ==, 1); + g_assert_cmpint(a->refcnt, ==, 3); + g_assert_cmpint(b->refcnt, ==, 1); + g_assert_cmpint(c->refcnt, ==, 2); + + g_assert(QLIST_FIRST(&parent_b->children) == detach_by_parent_data.child_c); + g_assert(QLIST_NEXT(detach_by_parent_data.child_c, next) == child_a); + g_assert(QLIST_NEXT(child_a, next) == NULL); + + g_assert_cmpint(parent_a->quiesce_counter, ==, 1); + g_assert_cmpint(parent_b->quiesce_counter, ==, 1); + g_assert_cmpint(a->quiesce_counter, ==, 1); + g_assert_cmpint(b->quiesce_counter, ==, 0); + g_assert_cmpint(c->quiesce_counter, ==, 1); + + bdrv_subtree_drained_end(parent_b); + + bdrv_unref(parent_b); + blk_unref(blk); + + g_assert_cmpint(a->refcnt, ==, 1); + g_assert_cmpint(b->refcnt, ==, 1); + g_assert_cmpint(c->refcnt, ==, 1); + bdrv_unref(a); + bdrv_unref(b); + bdrv_unref(c); +} + +static void test_detach_by_parent_cb(void) +{ + test_detach_indirect(true); +} + +static void test_detach_by_driver_cb(void) +{ + test_detach_indirect(false); +} + +static void test_append_to_drained(void) +{ + BlockBackend *blk; + BlockDriverState *base, *overlay; + BDRVTestState *base_s, *overlay_s; + + blk = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL); + base = bdrv_new_open_driver(&bdrv_test, "base", BDRV_O_RDWR, &error_abort); + base_s = base->opaque; + blk_insert_bs(blk, base, &error_abort); + + overlay = bdrv_new_open_driver(&bdrv_test, "overlay", BDRV_O_RDWR, + &error_abort); + overlay_s = overlay->opaque; + + do_drain_begin(BDRV_DRAIN, base); + g_assert_cmpint(base->quiesce_counter, ==, 1); + g_assert_cmpint(base_s->drain_count, ==, 1); + g_assert_cmpint(base->in_flight, ==, 0); + + /* Takes ownership of overlay, so we don't have to unref it later */ + bdrv_append(overlay, base, &error_abort); + g_assert_cmpint(base->in_flight, ==, 0); + g_assert_cmpint(overlay->in_flight, ==, 0); + + g_assert_cmpint(base->quiesce_counter, ==, 1); + g_assert_cmpint(base_s->drain_count, ==, 1); + g_assert_cmpint(overlay->quiesce_counter, ==, 1); + g_assert_cmpint(overlay_s->drain_count, ==, 1); + + do_drain_end(BDRV_DRAIN, base); + + g_assert_cmpint(base->quiesce_counter, ==, 0); + g_assert_cmpint(base_s->drain_count, ==, 0); + g_assert_cmpint(overlay->quiesce_counter, ==, 0); + g_assert_cmpint(overlay_s->drain_count, ==, 0); + + bdrv_unref(base); + blk_unref(blk); +} + +static void test_set_aio_context(void) +{ + BlockDriverState *bs; + IOThread *a = iothread_new(); + IOThread *b = iothread_new(); + AioContext *ctx_a = iothread_get_aio_context(a); + AioContext *ctx_b = iothread_get_aio_context(b); + + bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR, + &error_abort); + + bdrv_drained_begin(bs); + bdrv_try_set_aio_context(bs, ctx_a, &error_abort); + + aio_context_acquire(ctx_a); + bdrv_drained_end(bs); + + bdrv_drained_begin(bs); + bdrv_try_set_aio_context(bs, ctx_b, &error_abort); + aio_context_release(ctx_a); + aio_context_acquire(ctx_b); + bdrv_try_set_aio_context(bs, qemu_get_aio_context(), &error_abort); + aio_context_release(ctx_b); + bdrv_drained_end(bs); + + bdrv_unref(bs); + iothread_join(a); + iothread_join(b); +} + + +typedef struct TestDropBackingBlockJob { + BlockJob common; + bool should_complete; + bool *did_complete; + BlockDriverState *detach_also; +} TestDropBackingBlockJob; + +static int coroutine_fn test_drop_backing_job_run(Job *job, Error **errp) +{ + TestDropBackingBlockJob *s = + container_of(job, TestDropBackingBlockJob, common.job); + + while (!s->should_complete) { + job_sleep_ns(job, 0); + } + + return 0; +} + +static void test_drop_backing_job_commit(Job *job) +{ + TestDropBackingBlockJob *s = + container_of(job, TestDropBackingBlockJob, common.job); + + bdrv_set_backing_hd(blk_bs(s->common.blk), NULL, &error_abort); + bdrv_set_backing_hd(s->detach_also, NULL, &error_abort); + + *s->did_complete = true; +} + +static const BlockJobDriver test_drop_backing_job_driver = { + .job_driver = { + .instance_size = sizeof(TestDropBackingBlockJob), + .free = block_job_free, + .user_resume = block_job_user_resume, + .run = test_drop_backing_job_run, + .commit = test_drop_backing_job_commit, + } +}; + +/** + * Creates a child node with three parent nodes on it, and then runs a + * block job on the final one, parent-node-2. + * + * The job is then asked to complete before a section where the child + * is drained. + * + * Ending this section will undrain the child's parents, first + * parent-node-2, then parent-node-1, then parent-node-0 -- the parent + * list is in reverse order of how they were added. Ending the drain + * on parent-node-2 will resume the job, thus completing it and + * scheduling job_exit(). + * + * Ending the drain on parent-node-1 will poll the AioContext, which + * lets job_exit() and thus test_drop_backing_job_commit() run. That + * function first removes the child as parent-node-2's backing file. + * + * In old (and buggy) implementations, there are two problems with + * that: + * (A) bdrv_drain_invoke() polls for every node that leaves the + * drained section. This means that job_exit() is scheduled + * before the child has left the drained section. Its + * quiesce_counter is therefore still 1 when it is removed from + * parent-node-2. + * + * (B) bdrv_replace_child_noperm() calls drained_end() on the old + * child's parents as many times as the child is quiesced. This + * means it will call drained_end() on parent-node-2 once. + * Because parent-node-2 is no longer quiesced at this point, this + * will fail. + * + * bdrv_replace_child_noperm() therefore must call drained_end() on + * the parent only if it really is still drained because the child is + * drained. + * + * If removing child from parent-node-2 was successful (as it should + * be), test_drop_backing_job_commit() will then also remove the child + * from parent-node-0. + * + * With an old version of our drain infrastructure ((A) above), that + * resulted in the following flow: + * + * 1. child attempts to leave its drained section. The call recurses + * to its parents. + * + * 2. parent-node-2 leaves the drained section. Polling in + * bdrv_drain_invoke() will schedule job_exit(). + * + * 3. parent-node-1 leaves the drained section. Polling in + * bdrv_drain_invoke() will run job_exit(), thus disconnecting + * parent-node-0 from the child node. + * + * 4. bdrv_parent_drained_end() uses a QLIST_FOREACH_SAFE() loop to + * iterate over the parents. Thus, it now accesses the BdrvChild + * object that used to connect parent-node-0 and the child node. + * However, that object no longer exists, so it accesses a dangling + * pointer. + * + * The solution is to only poll once when running a bdrv_drained_end() + * operation, specifically at the end when all drained_end() + * operations for all involved nodes have been scheduled. + * Note that this also solves (A) above, thus hiding (B). + */ +static void test_blockjob_commit_by_drained_end(void) +{ + BlockDriverState *bs_child, *bs_parents[3]; + TestDropBackingBlockJob *job; + bool job_has_completed = false; + int i; + + bs_child = bdrv_new_open_driver(&bdrv_test, "child-node", BDRV_O_RDWR, + &error_abort); + + for (i = 0; i < 3; i++) { + char name[32]; + snprintf(name, sizeof(name), "parent-node-%i", i); + bs_parents[i] = bdrv_new_open_driver(&bdrv_test, name, BDRV_O_RDWR, + &error_abort); + bdrv_set_backing_hd(bs_parents[i], bs_child, &error_abort); + } + + job = block_job_create("job", &test_drop_backing_job_driver, NULL, + bs_parents[2], 0, BLK_PERM_ALL, 0, 0, NULL, NULL, + &error_abort); + + job->detach_also = bs_parents[0]; + job->did_complete = &job_has_completed; + + job_start(&job->common.job); + + job->should_complete = true; + bdrv_drained_begin(bs_child); + g_assert(!job_has_completed); + bdrv_drained_end(bs_child); + g_assert(job_has_completed); + + bdrv_unref(bs_parents[0]); + bdrv_unref(bs_parents[1]); + bdrv_unref(bs_parents[2]); + bdrv_unref(bs_child); +} + + +typedef struct TestSimpleBlockJob { + BlockJob common; + bool should_complete; + bool *did_complete; +} TestSimpleBlockJob; + +static int coroutine_fn test_simple_job_run(Job *job, Error **errp) +{ + TestSimpleBlockJob *s = container_of(job, TestSimpleBlockJob, common.job); + + while (!s->should_complete) { + job_sleep_ns(job, 0); + } + + return 0; +} + +static void test_simple_job_clean(Job *job) +{ + TestSimpleBlockJob *s = container_of(job, TestSimpleBlockJob, common.job); + *s->did_complete = true; +} + +static const BlockJobDriver test_simple_job_driver = { + .job_driver = { + .instance_size = sizeof(TestSimpleBlockJob), + .free = block_job_free, + .user_resume = block_job_user_resume, + .run = test_simple_job_run, + .clean = test_simple_job_clean, + }, +}; + +static int drop_intermediate_poll_update_filename(BdrvChild *child, + BlockDriverState *new_base, + const char *filename, + Error **errp) +{ + /* + * We are free to poll here, which may change the block graph, if + * it is not drained. + */ + + /* If the job is not drained: Complete it, schedule job_exit() */ + aio_poll(qemu_get_current_aio_context(), false); + /* If the job is not drained: Run job_exit(), finish the job */ + aio_poll(qemu_get_current_aio_context(), false); + + return 0; +} + +/** + * Test a poll in the midst of bdrv_drop_intermediate(). + * + * bdrv_drop_intermediate() calls BdrvChildClass.update_filename(), + * which can yield or poll. This may lead to graph changes, unless + * the whole subtree in question is drained. + * + * We test this on the following graph: + * + * Job + * + * | + * job-node + * | + * v + * + * job-node + * + * | + * backing + * | + * v + * + * node-2 --chain--> node-1 --chain--> node-0 + * + * We drop node-1 with bdrv_drop_intermediate(top=node-1, base=node-0). + * + * This first updates node-2's backing filename by invoking + * drop_intermediate_poll_update_filename(), which polls twice. This + * causes the job to finish, which in turns causes the job-node to be + * deleted. + * + * bdrv_drop_intermediate() uses a QLIST_FOREACH_SAFE() loop, so it + * already has a pointer to the BdrvChild edge between job-node and + * node-1. When it tries to handle that edge, we probably get a + * segmentation fault because the object no longer exists. + * + * + * The solution is for bdrv_drop_intermediate() to drain top's + * subtree. This prevents graph changes from happening just because + * BdrvChildClass.update_filename() yields or polls. Thus, the block + * job is paused during that drained section and must finish before or + * after. + * + * (In addition, bdrv_replace_child() must keep the job paused.) + */ +static void test_drop_intermediate_poll(void) +{ + static BdrvChildClass chain_child_class; + BlockDriverState *chain[3]; + TestSimpleBlockJob *job; + BlockDriverState *job_node; + bool job_has_completed = false; + int i; + int ret; + + chain_child_class = child_of_bds; + chain_child_class.update_filename = drop_intermediate_poll_update_filename; + + for (i = 0; i < 3; i++) { + char name[32]; + snprintf(name, 32, "node-%i", i); + + chain[i] = bdrv_new_open_driver(&bdrv_test, name, 0, &error_abort); + } + + job_node = bdrv_new_open_driver(&bdrv_test, "job-node", BDRV_O_RDWR, + &error_abort); + bdrv_set_backing_hd(job_node, chain[1], &error_abort); + + /* + * Establish the chain last, so the chain links are the first + * elements in the BDS.parents lists + */ + for (i = 0; i < 3; i++) { + if (i) { + /* Takes the reference to chain[i - 1] */ + chain[i]->backing = bdrv_attach_child(chain[i], chain[i - 1], + "chain", &chain_child_class, + BDRV_CHILD_COW, &error_abort); + } + } + + job = block_job_create("job", &test_simple_job_driver, NULL, job_node, + 0, BLK_PERM_ALL, 0, 0, NULL, NULL, &error_abort); + + /* The job has a reference now */ + bdrv_unref(job_node); + + job->did_complete = &job_has_completed; + + job_start(&job->common.job); + job->should_complete = true; + + g_assert(!job_has_completed); + ret = bdrv_drop_intermediate(chain[1], chain[0], NULL); + g_assert(ret == 0); + g_assert(job_has_completed); + + bdrv_unref(chain[2]); +} + + +typedef struct BDRVReplaceTestState { + bool was_drained; + bool was_undrained; + bool has_read; + + int drain_count; + + bool yield_before_read; + Coroutine *io_co; + Coroutine *drain_co; +} BDRVReplaceTestState; + +static void bdrv_replace_test_close(BlockDriverState *bs) +{ +} + +/** + * If @bs has a backing file: + * Yield if .yield_before_read is true (and wait for drain_begin to + * wake us up). + * Forward the read to bs->backing. Set .has_read to true. + * If drain_begin has woken us, wake it in turn. + * + * Otherwise: + * Set .has_read to true and return success. + */ +static int coroutine_fn bdrv_replace_test_co_preadv(BlockDriverState *bs, + uint64_t offset, + uint64_t bytes, + QEMUIOVector *qiov, + int flags) +{ + BDRVReplaceTestState *s = bs->opaque; + + if (bs->backing) { + int ret; + + g_assert(!s->drain_count); + + s->io_co = qemu_coroutine_self(); + if (s->yield_before_read) { + s->yield_before_read = false; + qemu_coroutine_yield(); + } + s->io_co = NULL; + + ret = bdrv_co_preadv(bs->backing, offset, bytes, qiov, 0); + s->has_read = true; + + /* Wake up drain_co if it runs */ + if (s->drain_co) { + aio_co_wake(s->drain_co); + } + + return ret; + } + + s->has_read = true; + return 0; +} + +/** + * If .drain_count is 0, wake up .io_co if there is one; and set + * .was_drained. + * Increment .drain_count. + */ +static void coroutine_fn bdrv_replace_test_co_drain_begin(BlockDriverState *bs) +{ + BDRVReplaceTestState *s = bs->opaque; + + if (!s->drain_count) { + /* Keep waking io_co up until it is done */ + s->drain_co = qemu_coroutine_self(); + while (s->io_co) { + aio_co_wake(s->io_co); + s->io_co = NULL; + qemu_coroutine_yield(); + } + s->drain_co = NULL; + + s->was_drained = true; + } + s->drain_count++; +} + +/** + * Reduce .drain_count, set .was_undrained once it reaches 0. + * If .drain_count reaches 0 and the node has a backing file, issue a + * read request. + */ +static void coroutine_fn bdrv_replace_test_co_drain_end(BlockDriverState *bs) +{ + BDRVReplaceTestState *s = bs->opaque; + + g_assert(s->drain_count > 0); + if (!--s->drain_count) { + int ret; + + s->was_undrained = true; + + if (bs->backing) { + char data; + QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, &data, 1); + + /* Queue a read request post-drain */ + ret = bdrv_replace_test_co_preadv(bs, 0, 1, &qiov, 0); + g_assert(ret >= 0); + } + } +} + +static BlockDriver bdrv_replace_test = { + .format_name = "replace_test", + .instance_size = sizeof(BDRVReplaceTestState), + + .bdrv_close = bdrv_replace_test_close, + .bdrv_co_preadv = bdrv_replace_test_co_preadv, + + .bdrv_co_drain_begin = bdrv_replace_test_co_drain_begin, + .bdrv_co_drain_end = bdrv_replace_test_co_drain_end, + + .bdrv_child_perm = bdrv_default_perms, +}; + +static void coroutine_fn test_replace_child_mid_drain_read_co(void *opaque) +{ + int ret; + char data; + + ret = blk_co_pread(opaque, 0, 1, &data, 0); + g_assert(ret >= 0); +} + +/** + * We test two things: + * (1) bdrv_replace_child_noperm() must not undrain the parent if both + * children are drained. + * (2) bdrv_replace_child_noperm() must never flush I/O requests to a + * drained child. If the old child is drained, it must flush I/O + * requests after the new one has been attached. If the new child + * is drained, it must flush I/O requests before the old one is + * detached. + * + * To do so, we create one parent node and two child nodes; then + * attach one of the children (old_child_bs) to the parent, then + * drain both old_child_bs and new_child_bs according to + * old_drain_count and new_drain_count, respectively, and finally + * we invoke bdrv_replace_node() to replace old_child_bs by + * new_child_bs. + * + * The test block driver we use here (bdrv_replace_test) has a read + * function that: + * - For the parent node, can optionally yield, and then forwards the + * read to bdrv_preadv(), + * - For the child node, just returns immediately. + * + * If the read yields, the drain_begin function will wake it up. + * + * The drain_end function issues a read on the parent once it is fully + * undrained (which simulates requests starting to come in again). + */ +static void do_test_replace_child_mid_drain(int old_drain_count, + int new_drain_count) +{ + BlockBackend *parent_blk; + BlockDriverState *parent_bs; + BlockDriverState *old_child_bs, *new_child_bs; + BDRVReplaceTestState *parent_s; + BDRVReplaceTestState *old_child_s, *new_child_s; + Coroutine *io_co; + int i; + + parent_bs = bdrv_new_open_driver(&bdrv_replace_test, "parent", 0, + &error_abort); + parent_s = parent_bs->opaque; + + parent_blk = blk_new(qemu_get_aio_context(), + BLK_PERM_CONSISTENT_READ, BLK_PERM_ALL); + blk_insert_bs(parent_blk, parent_bs, &error_abort); + + old_child_bs = bdrv_new_open_driver(&bdrv_replace_test, "old-child", 0, + &error_abort); + new_child_bs = bdrv_new_open_driver(&bdrv_replace_test, "new-child", 0, + &error_abort); + old_child_s = old_child_bs->opaque; + new_child_s = new_child_bs->opaque; + + /* So that we can read something */ + parent_bs->total_sectors = 1; + old_child_bs->total_sectors = 1; + new_child_bs->total_sectors = 1; + + bdrv_ref(old_child_bs); + parent_bs->backing = bdrv_attach_child(parent_bs, old_child_bs, "child", + &child_of_bds, BDRV_CHILD_COW, + &error_abort); + + for (i = 0; i < old_drain_count; i++) { + bdrv_drained_begin(old_child_bs); + } + for (i = 0; i < new_drain_count; i++) { + bdrv_drained_begin(new_child_bs); + } + + if (!old_drain_count) { + /* + * Start a read operation that will yield, so it will not + * complete before the node is drained. + */ + parent_s->yield_before_read = true; + io_co = qemu_coroutine_create(test_replace_child_mid_drain_read_co, + parent_blk); + qemu_coroutine_enter(io_co); + } + + /* If we have started a read operation, it should have yielded */ + g_assert(!parent_s->has_read); + + /* Reset drained status so we can see what bdrv_replace_node() does */ + parent_s->was_drained = false; + parent_s->was_undrained = false; + + g_assert(parent_bs->quiesce_counter == old_drain_count); + bdrv_replace_node(old_child_bs, new_child_bs, &error_abort); + g_assert(parent_bs->quiesce_counter == new_drain_count); + + if (!old_drain_count && !new_drain_count) { + /* + * From undrained to undrained drains and undrains the parent, + * because bdrv_replace_node() contains a drained section for + * @old_child_bs. + */ + g_assert(parent_s->was_drained && parent_s->was_undrained); + } else if (!old_drain_count && new_drain_count) { + /* + * From undrained to drained should drain the parent and keep + * it that way. + */ + g_assert(parent_s->was_drained && !parent_s->was_undrained); + } else if (old_drain_count && !new_drain_count) { + /* + * From drained to undrained should undrain the parent and + * keep it that way. + */ + g_assert(!parent_s->was_drained && parent_s->was_undrained); + } else /* if (old_drain_count && new_drain_count) */ { + /* + * From drained to drained must not undrain the parent at any + * point + */ + g_assert(!parent_s->was_drained && !parent_s->was_undrained); + } + + if (!old_drain_count || !new_drain_count) { + /* + * If !old_drain_count, we have started a read request before + * bdrv_replace_node(). If !new_drain_count, the parent must + * have been undrained at some point, and + * bdrv_replace_test_co_drain_end() starts a read request + * then. + */ + g_assert(parent_s->has_read); + } else { + /* + * If the parent was never undrained, there is no way to start + * a read request. + */ + g_assert(!parent_s->has_read); + } + + /* A drained child must have not received any request */ + g_assert(!(old_drain_count && old_child_s->has_read)); + g_assert(!(new_drain_count && new_child_s->has_read)); + + for (i = 0; i < new_drain_count; i++) { + bdrv_drained_end(new_child_bs); + } + for (i = 0; i < old_drain_count; i++) { + bdrv_drained_end(old_child_bs); + } + + /* + * By now, bdrv_replace_test_co_drain_end() must have been called + * at some point while the new child was attached to the parent. + */ + g_assert(parent_s->has_read); + g_assert(new_child_s->has_read); + + blk_unref(parent_blk); + bdrv_unref(parent_bs); + bdrv_unref(old_child_bs); + bdrv_unref(new_child_bs); +} + +static void test_replace_child_mid_drain(void) +{ + int old_drain_count, new_drain_count; + + for (old_drain_count = 0; old_drain_count < 2; old_drain_count++) { + for (new_drain_count = 0; new_drain_count < 2; new_drain_count++) { + do_test_replace_child_mid_drain(old_drain_count, new_drain_count); + } + } +} + +int main(int argc, char **argv) +{ + int ret; + + bdrv_init(); + qemu_init_main_loop(&error_abort); + + g_test_init(&argc, &argv, NULL); + qemu_event_init(&done_event, false); + + g_test_add_func("/bdrv-drain/driver-cb/drain_all", test_drv_cb_drain_all); + g_test_add_func("/bdrv-drain/driver-cb/drain", test_drv_cb_drain); + g_test_add_func("/bdrv-drain/driver-cb/drain_subtree", + test_drv_cb_drain_subtree); + + g_test_add_func("/bdrv-drain/driver-cb/co/drain_all", + test_drv_cb_co_drain_all); + g_test_add_func("/bdrv-drain/driver-cb/co/drain", test_drv_cb_co_drain); + g_test_add_func("/bdrv-drain/driver-cb/co/drain_subtree", + test_drv_cb_co_drain_subtree); + + + g_test_add_func("/bdrv-drain/quiesce/drain_all", test_quiesce_drain_all); + g_test_add_func("/bdrv-drain/quiesce/drain", test_quiesce_drain); + g_test_add_func("/bdrv-drain/quiesce/drain_subtree", + test_quiesce_drain_subtree); + + g_test_add_func("/bdrv-drain/quiesce/co/drain_all", + test_quiesce_co_drain_all); + g_test_add_func("/bdrv-drain/quiesce/co/drain", test_quiesce_co_drain); + g_test_add_func("/bdrv-drain/quiesce/co/drain_subtree", + test_quiesce_co_drain_subtree); + + g_test_add_func("/bdrv-drain/nested", test_nested); + g_test_add_func("/bdrv-drain/multiparent", test_multiparent); + + g_test_add_func("/bdrv-drain/graph-change/drain_subtree", + test_graph_change_drain_subtree); + g_test_add_func("/bdrv-drain/graph-change/drain_all", + test_graph_change_drain_all); + + g_test_add_func("/bdrv-drain/iothread/drain_all", test_iothread_drain_all); + g_test_add_func("/bdrv-drain/iothread/drain", test_iothread_drain); + g_test_add_func("/bdrv-drain/iothread/drain_subtree", + test_iothread_drain_subtree); + + g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all); + g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain); + g_test_add_func("/bdrv-drain/blockjob/drain_subtree", + test_blockjob_drain_subtree); + + g_test_add_func("/bdrv-drain/blockjob/error/drain_all", + test_blockjob_error_drain_all); + g_test_add_func("/bdrv-drain/blockjob/error/drain", + test_blockjob_error_drain); + g_test_add_func("/bdrv-drain/blockjob/error/drain_subtree", + test_blockjob_error_drain_subtree); + + g_test_add_func("/bdrv-drain/blockjob/iothread/drain_all", + test_blockjob_iothread_drain_all); + g_test_add_func("/bdrv-drain/blockjob/iothread/drain", + test_blockjob_iothread_drain); + g_test_add_func("/bdrv-drain/blockjob/iothread/drain_subtree", + test_blockjob_iothread_drain_subtree); + + g_test_add_func("/bdrv-drain/blockjob/iothread/error/drain_all", + test_blockjob_iothread_error_drain_all); + g_test_add_func("/bdrv-drain/blockjob/iothread/error/drain", + test_blockjob_iothread_error_drain); + g_test_add_func("/bdrv-drain/blockjob/iothread/error/drain_subtree", + test_blockjob_iothread_error_drain_subtree); + + g_test_add_func("/bdrv-drain/deletion/drain", test_delete_by_drain); + g_test_add_func("/bdrv-drain/detach/drain_all", test_detach_by_drain_all); + g_test_add_func("/bdrv-drain/detach/drain", test_detach_by_drain); + g_test_add_func("/bdrv-drain/detach/drain_subtree", test_detach_by_drain_subtree); + g_test_add_func("/bdrv-drain/detach/parent_cb", test_detach_by_parent_cb); + g_test_add_func("/bdrv-drain/detach/driver_cb", test_detach_by_driver_cb); + + g_test_add_func("/bdrv-drain/attach/drain", test_append_to_drained); + + g_test_add_func("/bdrv-drain/set_aio_context", test_set_aio_context); + + g_test_add_func("/bdrv-drain/blockjob/commit_by_drained_end", + test_blockjob_commit_by_drained_end); + + g_test_add_func("/bdrv-drain/bdrv_drop_intermediate/poll", + test_drop_intermediate_poll); + + g_test_add_func("/bdrv-drain/replace_child/mid-drain", + test_replace_child_mid_drain); + + ret = g_test_run(); + qemu_event_destroy(&done_event); + return ret; +} diff --git a/tests/unit/test-bdrv-graph-mod.c b/tests/unit/test-bdrv-graph-mod.c new file mode 100644 index 0000000000..c4f7d16039 --- /dev/null +++ b/tests/unit/test-bdrv-graph-mod.c @@ -0,0 +1,200 @@ +/* + * Block node graph modifications tests + * + * Copyright (c) 2019 Virtuozzo International GmbH. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/main-loop.h" +#include "block/block_int.h" +#include "sysemu/block-backend.h" + +static BlockDriver bdrv_pass_through = { + .format_name = "pass-through", + .bdrv_child_perm = bdrv_default_perms, +}; + +static void no_perm_default_perms(BlockDriverState *bs, BdrvChild *c, + BdrvChildRole role, + BlockReopenQueue *reopen_queue, + uint64_t perm, uint64_t shared, + uint64_t *nperm, uint64_t *nshared) +{ + *nperm = 0; + *nshared = BLK_PERM_ALL; +} + +static BlockDriver bdrv_no_perm = { + .format_name = "no-perm", + .bdrv_child_perm = no_perm_default_perms, +}; + +static BlockDriverState *no_perm_node(const char *name) +{ + return bdrv_new_open_driver(&bdrv_no_perm, name, BDRV_O_RDWR, &error_abort); +} + +static BlockDriverState *pass_through_node(const char *name) +{ + return bdrv_new_open_driver(&bdrv_pass_through, name, + BDRV_O_RDWR, &error_abort); +} + +/* + * test_update_perm_tree + * + * When checking node for a possibility to update permissions, it's subtree + * should be correctly checked too. New permissions for each node should be + * calculated and checked in context of permissions of other nodes. If we + * check new permissions of the node only in context of old permissions of + * its neighbors, we can finish up with wrong permission graph. + * + * This test firstly create the following graph: + * +--------+ + * | root | + * +--------+ + * | + * | perm: write, read + * | shared: except write + * v + * +-------------------+ +----------------+ + * | passtrough filter |---------->| null-co node | + * +-------------------+ +----------------+ + * + * + * and then, tries to append filter under node. Expected behavior: fail. + * Otherwise we'll get the following picture, with two BdrvChild'ren, having + * write permission to one node, without actually sharing it. + * + * +--------+ + * | root | + * +--------+ + * | + * | perm: write, read + * | shared: except write + * v + * +-------------------+ + * | passtrough filter | + * +-------------------+ + * | | + * perm: write, read | | perm: write, read + * shared: except write | | shared: except write + * v v + * +----------------+ + * | null co node | + * +----------------+ + */ +static void test_update_perm_tree(void) +{ + int ret; + + BlockBackend *root = blk_new(qemu_get_aio_context(), + BLK_PERM_WRITE | BLK_PERM_CONSISTENT_READ, + BLK_PERM_ALL & ~BLK_PERM_WRITE); + BlockDriverState *bs = no_perm_node("node"); + BlockDriverState *filter = pass_through_node("filter"); + + blk_insert_bs(root, bs, &error_abort); + + bdrv_attach_child(filter, bs, "child", &child_of_bds, + BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, &error_abort); + + ret = bdrv_append(filter, bs, NULL); + g_assert_cmpint(ret, <, 0); + + blk_unref(root); +} + +/* + * test_should_update_child + * + * Test that bdrv_replace_node, and concretely should_update_child + * do the right thing, i.e. not creating loops on the graph. + * + * The test does the following: + * 1. initial graph: + * + * +------+ +--------+ + * | root | | filter | + * +------+ +--------+ + * | | + * root| target| + * v v + * +------+ +--------+ + * | node |<---------| target | + * +------+ backing +--------+ + * + * 2. Append @filter above @node. If should_update_child works correctly, + * it understands, that backing child of @target should not be updated, + * as it will create a loop on node graph. Resulting picture should + * be the left one, not the right: + * + * +------+ +------+ + * | root | | root | + * +------+ +------+ + * | | + * root| root| + * v v + * +--------+ target +--------+ target + * | filter |--------------+ | filter |--------------+ + * +--------+ | +--------+ | + * | | | ^ v + * backing| | backing| | +--------+ + * v v | +-----------| target | + * +------+ +--------+ v backing +--------+ + * | node |<---------| target | +------+ + * +------+ backing +--------+ | node | + * +------+ + * + * (good picture) (bad picture) + * + */ +static void test_should_update_child(void) +{ + BlockBackend *root = blk_new(qemu_get_aio_context(), 0, BLK_PERM_ALL); + BlockDriverState *bs = no_perm_node("node"); + BlockDriverState *filter = no_perm_node("filter"); + BlockDriverState *target = no_perm_node("target"); + + blk_insert_bs(root, bs, &error_abort); + + bdrv_set_backing_hd(target, bs, &error_abort); + + g_assert(target->backing->bs == bs); + bdrv_attach_child(filter, target, "target", &child_of_bds, + BDRV_CHILD_DATA, &error_abort); + bdrv_append(filter, bs, &error_abort); + g_assert(target->backing->bs == bs); + + bdrv_unref(bs); + blk_unref(root); +} + +int main(int argc, char *argv[]) +{ + bdrv_init(); + qemu_init_main_loop(&error_abort); + + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/bdrv-graph-mod/update-perm-tree", test_update_perm_tree); + g_test_add_func("/bdrv-graph-mod/should-update-child", + test_should_update_child); + + return g_test_run(); +} diff --git a/tests/unit/test-bitcnt.c b/tests/unit/test-bitcnt.c new file mode 100644 index 0000000000..e153dcb8a2 --- /dev/null +++ b/tests/unit/test-bitcnt.c @@ -0,0 +1,140 @@ +/* + * Test bit count routines + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "qemu/host-utils.h" + +struct bitcnt_test_data { + /* value to count */ + union { + uint8_t w8; + uint16_t w16; + uint32_t w32; + uint64_t w64; + } value; + /* expected result */ + int popct; +}; + +struct bitcnt_test_data eight_bit_data[] = { + { { .w8 = 0x00 }, .popct=0 }, + { { .w8 = 0x01 }, .popct=1 }, + { { .w8 = 0x03 }, .popct=2 }, + { { .w8 = 0x04 }, .popct=1 }, + { { .w8 = 0x0f }, .popct=4 }, + { { .w8 = 0x3f }, .popct=6 }, + { { .w8 = 0x40 }, .popct=1 }, + { { .w8 = 0xf0 }, .popct=4 }, + { { .w8 = 0x7f }, .popct=7 }, + { { .w8 = 0x80 }, .popct=1 }, + { { .w8 = 0xf1 }, .popct=5 }, + { { .w8 = 0xfe }, .popct=7 }, + { { .w8 = 0xff }, .popct=8 }, +}; + +static void test_ctpop8(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(eight_bit_data); i++) { + struct bitcnt_test_data *d = &eight_bit_data[i]; + g_assert(ctpop8(d->value.w8)==d->popct); + } +} + +struct bitcnt_test_data sixteen_bit_data[] = { + { { .w16 = 0x0000 }, .popct=0 }, + { { .w16 = 0x0001 }, .popct=1 }, + { { .w16 = 0x0003 }, .popct=2 }, + { { .w16 = 0x000f }, .popct=4 }, + { { .w16 = 0x003f }, .popct=6 }, + { { .w16 = 0x00f0 }, .popct=4 }, + { { .w16 = 0x0f0f }, .popct=8 }, + { { .w16 = 0x1f1f }, .popct=10 }, + { { .w16 = 0x4000 }, .popct=1 }, + { { .w16 = 0x4001 }, .popct=2 }, + { { .w16 = 0x7000 }, .popct=3 }, + { { .w16 = 0x7fff }, .popct=15 }, +}; + +static void test_ctpop16(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(sixteen_bit_data); i++) { + struct bitcnt_test_data *d = &sixteen_bit_data[i]; + g_assert(ctpop16(d->value.w16)==d->popct); + } +} + +struct bitcnt_test_data thirtytwo_bit_data[] = { + { { .w32 = 0x00000000 }, .popct=0 }, + { { .w32 = 0x00000001 }, .popct=1 }, + { { .w32 = 0x0000000f }, .popct=4 }, + { { .w32 = 0x00000f0f }, .popct=8 }, + { { .w32 = 0x00001f1f }, .popct=10 }, + { { .w32 = 0x00004001 }, .popct=2 }, + { { .w32 = 0x00007000 }, .popct=3 }, + { { .w32 = 0x00007fff }, .popct=15 }, + { { .w32 = 0x55555555 }, .popct=16 }, + { { .w32 = 0xaaaaaaaa }, .popct=16 }, + { { .w32 = 0xff000000 }, .popct=8 }, + { { .w32 = 0xc0c0c0c0 }, .popct=8 }, + { { .w32 = 0x0ffffff0 }, .popct=24 }, + { { .w32 = 0x80000000 }, .popct=1 }, + { { .w32 = 0xffffffff }, .popct=32 }, +}; + +static void test_ctpop32(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(thirtytwo_bit_data); i++) { + struct bitcnt_test_data *d = &thirtytwo_bit_data[i]; + g_assert(ctpop32(d->value.w32)==d->popct); + } +} + +struct bitcnt_test_data sixtyfour_bit_data[] = { + { { .w64 = 0x0000000000000000ULL }, .popct=0 }, + { { .w64 = 0x0000000000000001ULL }, .popct=1 }, + { { .w64 = 0x000000000000000fULL }, .popct=4 }, + { { .w64 = 0x0000000000000f0fULL }, .popct=8 }, + { { .w64 = 0x0000000000001f1fULL }, .popct=10 }, + { { .w64 = 0x0000000000004001ULL }, .popct=2 }, + { { .w64 = 0x0000000000007000ULL }, .popct=3 }, + { { .w64 = 0x0000000000007fffULL }, .popct=15 }, + { { .w64 = 0x0000005500555555ULL }, .popct=16 }, + { { .w64 = 0x00aa0000aaaa00aaULL }, .popct=16 }, + { { .w64 = 0x000f000000f00000ULL }, .popct=8 }, + { { .w64 = 0x0c0c0000c0c0c0c0ULL }, .popct=12 }, + { { .w64 = 0xf00f00f0f0f0f000ULL }, .popct=24 }, + { { .w64 = 0x8000000000000000ULL }, .popct=1 }, + { { .w64 = 0xf0f0f0f0f0f0f0f0ULL }, .popct=32 }, + { { .w64 = 0xffffffffffffffffULL }, .popct=64 }, +}; + +static void test_ctpop64(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(sixtyfour_bit_data); i++) { + struct bitcnt_test_data *d = &sixtyfour_bit_data[i]; + g_assert(ctpop64(d->value.w64)==d->popct); + } +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + g_test_add_func("/bitcnt/ctpop8", test_ctpop8); + g_test_add_func("/bitcnt/ctpop16", test_ctpop16); + g_test_add_func("/bitcnt/ctpop32", test_ctpop32); + g_test_add_func("/bitcnt/ctpop64", test_ctpop64); + return g_test_run(); +} diff --git a/tests/unit/test-bitmap.c b/tests/unit/test-bitmap.c new file mode 100644 index 0000000000..8db4f67883 --- /dev/null +++ b/tests/unit/test-bitmap.c @@ -0,0 +1,138 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * Bitmap.c unit-tests. + * + * Copyright (C) 2019, Red Hat, Inc. + * + * Author: Peter Xu + */ + +#include "qemu/osdep.h" +#include "qemu/bitmap.h" + +#define BMAP_SIZE 1024 + +static void check_bitmap_copy_with_offset(void) +{ + unsigned long *bmap1, *bmap2, *bmap3, total; + + bmap1 = bitmap_new(BMAP_SIZE); + bmap2 = bitmap_new(BMAP_SIZE); + bmap3 = bitmap_new(BMAP_SIZE); + + bmap1[0] = g_test_rand_int(); + bmap1[1] = g_test_rand_int(); + bmap1[2] = g_test_rand_int(); + bmap1[3] = g_test_rand_int(); + total = BITS_PER_LONG * 4; + + /* Shift 115 bits into bmap2 */ + bitmap_copy_with_dst_offset(bmap2, bmap1, 115, total); + /* Shift another 85 bits into bmap3 */ + bitmap_copy_with_dst_offset(bmap3, bmap2, 85, total + 115); + /* Shift back 200 bits back */ + bitmap_copy_with_src_offset(bmap2, bmap3, 200, total); + + g_assert_cmpmem(bmap1, total / BITS_PER_LONG, + bmap2, total / BITS_PER_LONG); + + bitmap_clear(bmap1, 0, BMAP_SIZE); + /* Set bits in bmap1 are 100-245 */ + bitmap_set(bmap1, 100, 145); + + /* Set bits in bmap2 are 60-205 */ + bitmap_copy_with_src_offset(bmap2, bmap1, 40, 250); + g_assert_cmpint(find_first_bit(bmap2, 60), ==, 60); + g_assert_cmpint(find_next_zero_bit(bmap2, 205, 60), ==, 205); + g_assert(test_bit(205, bmap2) == 0); + + /* Set bits in bmap3 are 135-280 */ + bitmap_copy_with_dst_offset(bmap3, bmap1, 35, 250); + g_assert_cmpint(find_first_bit(bmap3, 135), ==, 135); + g_assert_cmpint(find_next_zero_bit(bmap3, 280, 135), ==, 280); + g_assert(test_bit(280, bmap3) == 0); + + g_free(bmap1); + g_free(bmap2); + g_free(bmap3); +} + +typedef void (*bmap_set_func)(unsigned long *map, long i, long len); +static void bitmap_set_case(bmap_set_func set_func) +{ + unsigned long *bmap; + int offset; + + bmap = bitmap_new(BMAP_SIZE); + + /* Set one bit at offset in second word */ + for (offset = 0; offset <= BITS_PER_LONG; offset++) { + bitmap_clear(bmap, 0, BMAP_SIZE); + set_func(bmap, BITS_PER_LONG + offset, 1); + g_assert_cmpint(find_first_bit(bmap, 2 * BITS_PER_LONG), + ==, BITS_PER_LONG + offset); + g_assert_cmpint(find_next_zero_bit(bmap, + 3 * BITS_PER_LONG, + BITS_PER_LONG + offset), + ==, BITS_PER_LONG + offset + 1); + } + + /* Both Aligned, set bits [BITS_PER_LONG, 3*BITS_PER_LONG] */ + set_func(bmap, BITS_PER_LONG, 2 * BITS_PER_LONG); + g_assert_cmpuint(bmap[1], ==, -1ul); + g_assert_cmpuint(bmap[2], ==, -1ul); + g_assert_cmpint(find_first_bit(bmap, BITS_PER_LONG), ==, BITS_PER_LONG); + g_assert_cmpint(find_next_zero_bit(bmap, 3 * BITS_PER_LONG, BITS_PER_LONG), + ==, 3 * BITS_PER_LONG); + + for (offset = 0; offset <= BITS_PER_LONG; offset++) { + bitmap_clear(bmap, 0, BMAP_SIZE); + /* End Aligned, set bits [BITS_PER_LONG - offset, 3*BITS_PER_LONG] */ + set_func(bmap, BITS_PER_LONG - offset, 2 * BITS_PER_LONG + offset); + g_assert_cmpuint(bmap[1], ==, -1ul); + g_assert_cmpuint(bmap[2], ==, -1ul); + g_assert_cmpint(find_first_bit(bmap, BITS_PER_LONG), + ==, BITS_PER_LONG - offset); + g_assert_cmpint(find_next_zero_bit(bmap, + 3 * BITS_PER_LONG, + BITS_PER_LONG - offset), + ==, 3 * BITS_PER_LONG); + } + + for (offset = 0; offset <= BITS_PER_LONG; offset++) { + bitmap_clear(bmap, 0, BMAP_SIZE); + /* Start Aligned, set bits [BITS_PER_LONG, 3*BITS_PER_LONG + offset] */ + set_func(bmap, BITS_PER_LONG, 2 * BITS_PER_LONG + offset); + g_assert_cmpuint(bmap[1], ==, -1ul); + g_assert_cmpuint(bmap[2], ==, -1ul); + g_assert_cmpint(find_first_bit(bmap, BITS_PER_LONG), + ==, BITS_PER_LONG); + g_assert_cmpint(find_next_zero_bit(bmap, + 3 * BITS_PER_LONG + offset, + BITS_PER_LONG), + ==, 3 * BITS_PER_LONG + offset); + } + + g_free(bmap); +} + +static void check_bitmap_set(void) +{ + bitmap_set_case(bitmap_set); + bitmap_set_case(bitmap_set_atomic); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/bitmap/bitmap_copy_with_offset", + check_bitmap_copy_with_offset); + g_test_add_func("/bitmap/bitmap_set", + check_bitmap_set); + + g_test_run(); + + return 0; +} diff --git a/tests/unit/test-bitops.c b/tests/unit/test-bitops.c new file mode 100644 index 0000000000..5a791d2e17 --- /dev/null +++ b/tests/unit/test-bitops.c @@ -0,0 +1,146 @@ +/* + * Test bitops routines + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "qemu/bitops.h" + +typedef struct { + uint32_t value; + int start; + int length; + int32_t result; +} S32Test; + +typedef struct { + uint64_t value; + int start; + int length; + int64_t result; +} S64Test; + +static const S32Test test_s32_data[] = { + { 0x38463983, 4, 4, -8 }, + { 0x38463983, 12, 8, 0x63 }, + { 0x38463983, 0, 32, 0x38463983 }, +}; + +static const S64Test test_s64_data[] = { + { 0x8459826734967223ULL, 60, 4, -8 }, + { 0x8459826734967223ULL, 0, 64, 0x8459826734967223LL }, +}; + +static void test_sextract32(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(test_s32_data); i++) { + const S32Test *test = &test_s32_data[i]; + int32_t r = sextract32(test->value, test->start, test->length); + + g_assert_cmpint(r, ==, test->result); + } +} + +static void test_sextract64(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(test_s32_data); i++) { + const S32Test *test = &test_s32_data[i]; + int64_t r = sextract64(test->value, test->start, test->length); + + g_assert_cmpint(r, ==, test->result); + } + + for (i = 0; i < ARRAY_SIZE(test_s64_data); i++) { + const S64Test *test = &test_s64_data[i]; + int64_t r = sextract64(test->value, test->start, test->length); + + g_assert_cmpint(r, ==, test->result); + } +} + +typedef struct { + uint32_t unshuffled; + uint32_t shuffled; +} Shuffle32Test; + +typedef struct { + uint64_t unshuffled; + uint64_t shuffled; +} Shuffle64Test; + +static const Shuffle32Test test_shuffle32_data[] = { + { 0x0000FFFF, 0x55555555 }, + { 0x000081C5, 0x40015011 }, +}; + +static const Shuffle64Test test_shuffle64_data[] = { + { 0x00000000FFFFFFFFULL, 0x5555555555555555ULL }, + { 0x00000000493AB02CULL, 0x1041054445000450ULL }, +}; + +static void test_half_shuffle32(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(test_shuffle32_data); i++) { + const Shuffle32Test *test = &test_shuffle32_data[i]; + uint32_t r = half_shuffle32(test->unshuffled); + + g_assert_cmpint(r, ==, test->shuffled); + } +} + +static void test_half_shuffle64(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(test_shuffle64_data); i++) { + const Shuffle64Test *test = &test_shuffle64_data[i]; + uint64_t r = half_shuffle64(test->unshuffled); + + g_assert_cmpint(r, ==, test->shuffled); + } +} + +static void test_half_unshuffle32(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(test_shuffle32_data); i++) { + const Shuffle32Test *test = &test_shuffle32_data[i]; + uint32_t r = half_unshuffle32(test->shuffled); + + g_assert_cmpint(r, ==, test->unshuffled); + } +} + +static void test_half_unshuffle64(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(test_shuffle64_data); i++) { + const Shuffle64Test *test = &test_shuffle64_data[i]; + uint64_t r = half_unshuffle64(test->shuffled); + + g_assert_cmpint(r, ==, test->unshuffled); + } +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + g_test_add_func("/bitops/sextract32", test_sextract32); + g_test_add_func("/bitops/sextract64", test_sextract64); + g_test_add_func("/bitops/half_shuffle32", test_half_shuffle32); + g_test_add_func("/bitops/half_shuffle64", test_half_shuffle64); + g_test_add_func("/bitops/half_unshuffle32", test_half_unshuffle32); + g_test_add_func("/bitops/half_unshuffle64", test_half_unshuffle64); + return g_test_run(); +} diff --git a/tests/unit/test-block-backend.c b/tests/unit/test-block-backend.c new file mode 100644 index 0000000000..2fb1a444bd --- /dev/null +++ b/tests/unit/test-block-backend.c @@ -0,0 +1,85 @@ +/* + * BlockBackend tests + * + * Copyright (c) 2017 Kevin Wolf + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "block/block.h" +#include "sysemu/block-backend.h" +#include "qapi/error.h" +#include "qemu/main-loop.h" + +static void test_drain_aio_error_flush_cb(void *opaque, int ret) +{ + bool *completed = opaque; + + g_assert(ret == -ENOMEDIUM); + *completed = true; +} + +static void test_drain_aio_error(void) +{ + BlockBackend *blk = blk_new(qemu_get_aio_context(), + BLK_PERM_ALL, BLK_PERM_ALL); + BlockAIOCB *acb; + bool completed = false; + + acb = blk_aio_flush(blk, test_drain_aio_error_flush_cb, &completed); + g_assert(acb != NULL); + g_assert(completed == false); + + blk_drain(blk); + g_assert(completed == true); + + blk_unref(blk); +} + +static void test_drain_all_aio_error(void) +{ + BlockBackend *blk = blk_new(qemu_get_aio_context(), + BLK_PERM_ALL, BLK_PERM_ALL); + BlockAIOCB *acb; + bool completed = false; + + acb = blk_aio_flush(blk, test_drain_aio_error_flush_cb, &completed); + g_assert(acb != NULL); + g_assert(completed == false); + + blk_drain_all(); + g_assert(completed == true); + + blk_unref(blk); +} + +int main(int argc, char **argv) +{ + bdrv_init(); + qemu_init_main_loop(&error_abort); + + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/block-backend/drain_aio_error", test_drain_aio_error); + g_test_add_func("/block-backend/drain_all_aio_error", + test_drain_all_aio_error); + + return g_test_run(); +} diff --git a/tests/unit/test-block-iothread.c b/tests/unit/test-block-iothread.c new file mode 100644 index 0000000000..3f866a35c6 --- /dev/null +++ b/tests/unit/test-block-iothread.c @@ -0,0 +1,774 @@ +/* + * Block tests for iothreads + * + * Copyright (c) 2018 Kevin Wolf + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "block/block.h" +#include "block/blockjob_int.h" +#include "sysemu/block-backend.h" +#include "qapi/error.h" +#include "qapi/qmp/qdict.h" +#include "qemu/main-loop.h" +#include "iothread.h" + +static int coroutine_fn bdrv_test_co_prwv(BlockDriverState *bs, + uint64_t offset, uint64_t bytes, + QEMUIOVector *qiov, int flags) +{ + return 0; +} + +static int coroutine_fn bdrv_test_co_pdiscard(BlockDriverState *bs, + int64_t offset, int bytes) +{ + return 0; +} + +static int coroutine_fn +bdrv_test_co_truncate(BlockDriverState *bs, int64_t offset, bool exact, + PreallocMode prealloc, BdrvRequestFlags flags, + Error **errp) +{ + return 0; +} + +static int coroutine_fn bdrv_test_co_block_status(BlockDriverState *bs, + bool want_zero, + int64_t offset, int64_t count, + int64_t *pnum, int64_t *map, + BlockDriverState **file) +{ + *pnum = count; + return 0; +} + +static BlockDriver bdrv_test = { + .format_name = "test", + .instance_size = 1, + + .bdrv_co_preadv = bdrv_test_co_prwv, + .bdrv_co_pwritev = bdrv_test_co_prwv, + .bdrv_co_pdiscard = bdrv_test_co_pdiscard, + .bdrv_co_truncate = bdrv_test_co_truncate, + .bdrv_co_block_status = bdrv_test_co_block_status, +}; + +static void test_sync_op_pread(BdrvChild *c) +{ + uint8_t buf[512]; + int ret; + + /* Success */ + ret = bdrv_pread(c, 0, buf, sizeof(buf)); + g_assert_cmpint(ret, ==, 512); + + /* Early error: Negative offset */ + ret = bdrv_pread(c, -2, buf, sizeof(buf)); + g_assert_cmpint(ret, ==, -EIO); +} + +static void test_sync_op_pwrite(BdrvChild *c) +{ + uint8_t buf[512]; + int ret; + + /* Success */ + ret = bdrv_pwrite(c, 0, buf, sizeof(buf)); + g_assert_cmpint(ret, ==, 512); + + /* Early error: Negative offset */ + ret = bdrv_pwrite(c, -2, buf, sizeof(buf)); + g_assert_cmpint(ret, ==, -EIO); +} + +static void test_sync_op_blk_pread(BlockBackend *blk) +{ + uint8_t buf[512]; + int ret; + + /* Success */ + ret = blk_pread(blk, 0, buf, sizeof(buf)); + g_assert_cmpint(ret, ==, 512); + + /* Early error: Negative offset */ + ret = blk_pread(blk, -2, buf, sizeof(buf)); + g_assert_cmpint(ret, ==, -EIO); +} + +static void test_sync_op_blk_pwrite(BlockBackend *blk) +{ + uint8_t buf[512]; + int ret; + + /* Success */ + ret = blk_pwrite(blk, 0, buf, sizeof(buf), 0); + g_assert_cmpint(ret, ==, 512); + + /* Early error: Negative offset */ + ret = blk_pwrite(blk, -2, buf, sizeof(buf), 0); + g_assert_cmpint(ret, ==, -EIO); +} + +static void test_sync_op_load_vmstate(BdrvChild *c) +{ + uint8_t buf[512]; + int ret; + + /* Error: Driver does not support snapshots */ + ret = bdrv_load_vmstate(c->bs, buf, 0, sizeof(buf)); + g_assert_cmpint(ret, ==, -ENOTSUP); +} + +static void test_sync_op_save_vmstate(BdrvChild *c) +{ + uint8_t buf[512]; + int ret; + + /* Error: Driver does not support snapshots */ + ret = bdrv_save_vmstate(c->bs, buf, 0, sizeof(buf)); + g_assert_cmpint(ret, ==, -ENOTSUP); +} + +static void test_sync_op_pdiscard(BdrvChild *c) +{ + int ret; + + /* Normal success path */ + c->bs->open_flags |= BDRV_O_UNMAP; + ret = bdrv_pdiscard(c, 0, 512); + g_assert_cmpint(ret, ==, 0); + + /* Early success: UNMAP not supported */ + c->bs->open_flags &= ~BDRV_O_UNMAP; + ret = bdrv_pdiscard(c, 0, 512); + g_assert_cmpint(ret, ==, 0); + + /* Early error: Negative offset */ + ret = bdrv_pdiscard(c, -2, 512); + g_assert_cmpint(ret, ==, -EIO); +} + +static void test_sync_op_blk_pdiscard(BlockBackend *blk) +{ + int ret; + + /* Early success: UNMAP not supported */ + ret = blk_pdiscard(blk, 0, 512); + g_assert_cmpint(ret, ==, 0); + + /* Early error: Negative offset */ + ret = blk_pdiscard(blk, -2, 512); + g_assert_cmpint(ret, ==, -EIO); +} + +static void test_sync_op_truncate(BdrvChild *c) +{ + int ret; + + /* Normal success path */ + ret = bdrv_truncate(c, 65536, false, PREALLOC_MODE_OFF, 0, NULL); + g_assert_cmpint(ret, ==, 0); + + /* Early error: Negative offset */ + ret = bdrv_truncate(c, -2, false, PREALLOC_MODE_OFF, 0, NULL); + g_assert_cmpint(ret, ==, -EINVAL); + + /* Error: Read-only image */ + c->bs->read_only = true; + c->bs->open_flags &= ~BDRV_O_RDWR; + + ret = bdrv_truncate(c, 65536, false, PREALLOC_MODE_OFF, 0, NULL); + g_assert_cmpint(ret, ==, -EACCES); + + c->bs->read_only = false; + c->bs->open_flags |= BDRV_O_RDWR; +} + +static void test_sync_op_block_status(BdrvChild *c) +{ + int ret; + int64_t n; + + /* Normal success path */ + ret = bdrv_is_allocated(c->bs, 0, 65536, &n); + g_assert_cmpint(ret, ==, 0); + + /* Early success: No driver support */ + bdrv_test.bdrv_co_block_status = NULL; + ret = bdrv_is_allocated(c->bs, 0, 65536, &n); + g_assert_cmpint(ret, ==, 1); + + /* Early success: bytes = 0 */ + ret = bdrv_is_allocated(c->bs, 0, 0, &n); + g_assert_cmpint(ret, ==, 0); + + /* Early success: Offset > image size*/ + ret = bdrv_is_allocated(c->bs, 0x1000000, 0x1000000, &n); + g_assert_cmpint(ret, ==, 0); +} + +static void test_sync_op_flush(BdrvChild *c) +{ + int ret; + + /* Normal success path */ + ret = bdrv_flush(c->bs); + g_assert_cmpint(ret, ==, 0); + + /* Early success: Read-only image */ + c->bs->read_only = true; + c->bs->open_flags &= ~BDRV_O_RDWR; + + ret = bdrv_flush(c->bs); + g_assert_cmpint(ret, ==, 0); + + c->bs->read_only = false; + c->bs->open_flags |= BDRV_O_RDWR; +} + +static void test_sync_op_blk_flush(BlockBackend *blk) +{ + BlockDriverState *bs = blk_bs(blk); + int ret; + + /* Normal success path */ + ret = blk_flush(blk); + g_assert_cmpint(ret, ==, 0); + + /* Early success: Read-only image */ + bs->read_only = true; + bs->open_flags &= ~BDRV_O_RDWR; + + ret = blk_flush(blk); + g_assert_cmpint(ret, ==, 0); + + bs->read_only = false; + bs->open_flags |= BDRV_O_RDWR; +} + +static void test_sync_op_check(BdrvChild *c) +{ + BdrvCheckResult result; + int ret; + + /* Error: Driver does not implement check */ + ret = bdrv_check(c->bs, &result, 0); + g_assert_cmpint(ret, ==, -ENOTSUP); +} + +static void test_sync_op_invalidate_cache(BdrvChild *c) +{ + /* Early success: Image is not inactive */ + bdrv_invalidate_cache(c->bs, NULL); +} + + +typedef struct SyncOpTest { + const char *name; + void (*fn)(BdrvChild *c); + void (*blkfn)(BlockBackend *blk); +} SyncOpTest; + +const SyncOpTest sync_op_tests[] = { + { + .name = "/sync-op/pread", + .fn = test_sync_op_pread, + .blkfn = test_sync_op_blk_pread, + }, { + .name = "/sync-op/pwrite", + .fn = test_sync_op_pwrite, + .blkfn = test_sync_op_blk_pwrite, + }, { + .name = "/sync-op/load_vmstate", + .fn = test_sync_op_load_vmstate, + }, { + .name = "/sync-op/save_vmstate", + .fn = test_sync_op_save_vmstate, + }, { + .name = "/sync-op/pdiscard", + .fn = test_sync_op_pdiscard, + .blkfn = test_sync_op_blk_pdiscard, + }, { + .name = "/sync-op/truncate", + .fn = test_sync_op_truncate, + }, { + .name = "/sync-op/block_status", + .fn = test_sync_op_block_status, + }, { + .name = "/sync-op/flush", + .fn = test_sync_op_flush, + .blkfn = test_sync_op_blk_flush, + }, { + .name = "/sync-op/check", + .fn = test_sync_op_check, + }, { + .name = "/sync-op/invalidate_cache", + .fn = test_sync_op_invalidate_cache, + }, +}; + +/* Test synchronous operations that run in a different iothread, so we have to + * poll for the coroutine there to return. */ +static void test_sync_op(const void *opaque) +{ + const SyncOpTest *t = opaque; + IOThread *iothread = iothread_new(); + AioContext *ctx = iothread_get_aio_context(iothread); + BlockBackend *blk; + BlockDriverState *bs; + BdrvChild *c; + + blk = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL); + bs = bdrv_new_open_driver(&bdrv_test, "base", BDRV_O_RDWR, &error_abort); + bs->total_sectors = 65536 / BDRV_SECTOR_SIZE; + blk_insert_bs(blk, bs, &error_abort); + c = QLIST_FIRST(&bs->parents); + + blk_set_aio_context(blk, ctx, &error_abort); + aio_context_acquire(ctx); + t->fn(c); + if (t->blkfn) { + t->blkfn(blk); + } + blk_set_aio_context(blk, qemu_get_aio_context(), &error_abort); + aio_context_release(ctx); + + bdrv_unref(bs); + blk_unref(blk); +} + +typedef struct TestBlockJob { + BlockJob common; + bool should_complete; + int n; +} TestBlockJob; + +static int test_job_prepare(Job *job) +{ + g_assert(qemu_get_current_aio_context() == qemu_get_aio_context()); + return 0; +} + +static int coroutine_fn test_job_run(Job *job, Error **errp) +{ + TestBlockJob *s = container_of(job, TestBlockJob, common.job); + + job_transition_to_ready(&s->common.job); + while (!s->should_complete) { + s->n++; + g_assert(qemu_get_current_aio_context() == job->aio_context); + + /* Avoid job_sleep_ns() because it marks the job as !busy. We want to + * emulate some actual activity (probably some I/O) here so that the + * drain involved in AioContext switches has to wait for this activity + * to stop. */ + qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 1000000); + + job_pause_point(&s->common.job); + } + + g_assert(qemu_get_current_aio_context() == job->aio_context); + return 0; +} + +static void test_job_complete(Job *job, Error **errp) +{ + TestBlockJob *s = container_of(job, TestBlockJob, common.job); + s->should_complete = true; +} + +BlockJobDriver test_job_driver = { + .job_driver = { + .instance_size = sizeof(TestBlockJob), + .free = block_job_free, + .user_resume = block_job_user_resume, + .run = test_job_run, + .complete = test_job_complete, + .prepare = test_job_prepare, + }, +}; + +static void test_attach_blockjob(void) +{ + IOThread *iothread = iothread_new(); + AioContext *ctx = iothread_get_aio_context(iothread); + BlockBackend *blk; + BlockDriverState *bs; + TestBlockJob *tjob; + + blk = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL); + bs = bdrv_new_open_driver(&bdrv_test, "base", BDRV_O_RDWR, &error_abort); + blk_insert_bs(blk, bs, &error_abort); + + tjob = block_job_create("job0", &test_job_driver, NULL, bs, + 0, BLK_PERM_ALL, + 0, 0, NULL, NULL, &error_abort); + job_start(&tjob->common.job); + + while (tjob->n == 0) { + aio_poll(qemu_get_aio_context(), false); + } + + blk_set_aio_context(blk, ctx, &error_abort); + + tjob->n = 0; + while (tjob->n == 0) { + aio_poll(qemu_get_aio_context(), false); + } + + aio_context_acquire(ctx); + blk_set_aio_context(blk, qemu_get_aio_context(), &error_abort); + aio_context_release(ctx); + + tjob->n = 0; + while (tjob->n == 0) { + aio_poll(qemu_get_aio_context(), false); + } + + blk_set_aio_context(blk, ctx, &error_abort); + + tjob->n = 0; + while (tjob->n == 0) { + aio_poll(qemu_get_aio_context(), false); + } + + aio_context_acquire(ctx); + job_complete_sync(&tjob->common.job, &error_abort); + blk_set_aio_context(blk, qemu_get_aio_context(), &error_abort); + aio_context_release(ctx); + + bdrv_unref(bs); + blk_unref(blk); +} + +/* + * Test that changing the AioContext for one node in a tree (here through blk) + * changes all other nodes as well: + * + * blk + * | + * | bs_verify [blkverify] + * | / \ + * | / \ + * bs_a [bdrv_test] bs_b [bdrv_test] + * + */ +static void test_propagate_basic(void) +{ + IOThread *iothread = iothread_new(); + AioContext *ctx = iothread_get_aio_context(iothread); + AioContext *main_ctx; + BlockBackend *blk; + BlockDriverState *bs_a, *bs_b, *bs_verify; + QDict *options; + + /* + * Create bs_a and its BlockBackend. We cannot take the RESIZE + * permission because blkverify will not share it on the test + * image. + */ + blk = blk_new(qemu_get_aio_context(), BLK_PERM_ALL & ~BLK_PERM_RESIZE, + BLK_PERM_ALL); + bs_a = bdrv_new_open_driver(&bdrv_test, "bs_a", BDRV_O_RDWR, &error_abort); + blk_insert_bs(blk, bs_a, &error_abort); + + /* Create bs_b */ + bs_b = bdrv_new_open_driver(&bdrv_test, "bs_b", BDRV_O_RDWR, &error_abort); + + /* Create blkverify filter that references both bs_a and bs_b */ + options = qdict_new(); + qdict_put_str(options, "driver", "blkverify"); + qdict_put_str(options, "test", "bs_a"); + qdict_put_str(options, "raw", "bs_b"); + + bs_verify = bdrv_open(NULL, NULL, options, BDRV_O_RDWR, &error_abort); + + /* Switch the AioContext */ + blk_set_aio_context(blk, ctx, &error_abort); + g_assert(blk_get_aio_context(blk) == ctx); + g_assert(bdrv_get_aio_context(bs_a) == ctx); + g_assert(bdrv_get_aio_context(bs_verify) == ctx); + g_assert(bdrv_get_aio_context(bs_b) == ctx); + + /* Switch the AioContext back */ + main_ctx = qemu_get_aio_context(); + aio_context_acquire(ctx); + blk_set_aio_context(blk, main_ctx, &error_abort); + aio_context_release(ctx); + g_assert(blk_get_aio_context(blk) == main_ctx); + g_assert(bdrv_get_aio_context(bs_a) == main_ctx); + g_assert(bdrv_get_aio_context(bs_verify) == main_ctx); + g_assert(bdrv_get_aio_context(bs_b) == main_ctx); + + bdrv_unref(bs_verify); + bdrv_unref(bs_b); + bdrv_unref(bs_a); + blk_unref(blk); +} + +/* + * Test that diamonds in the graph don't lead to endless recursion: + * + * blk + * | + * bs_verify [blkverify] + * / \ + * / \ + * bs_b [raw] bs_c[raw] + * \ / + * \ / + * bs_a [bdrv_test] + */ +static void test_propagate_diamond(void) +{ + IOThread *iothread = iothread_new(); + AioContext *ctx = iothread_get_aio_context(iothread); + AioContext *main_ctx; + BlockBackend *blk; + BlockDriverState *bs_a, *bs_b, *bs_c, *bs_verify; + QDict *options; + + /* Create bs_a */ + bs_a = bdrv_new_open_driver(&bdrv_test, "bs_a", BDRV_O_RDWR, &error_abort); + + /* Create bs_b and bc_c */ + options = qdict_new(); + qdict_put_str(options, "driver", "raw"); + qdict_put_str(options, "file", "bs_a"); + qdict_put_str(options, "node-name", "bs_b"); + bs_b = bdrv_open(NULL, NULL, options, BDRV_O_RDWR, &error_abort); + + options = qdict_new(); + qdict_put_str(options, "driver", "raw"); + qdict_put_str(options, "file", "bs_a"); + qdict_put_str(options, "node-name", "bs_c"); + bs_c = bdrv_open(NULL, NULL, options, BDRV_O_RDWR, &error_abort); + + /* Create blkverify filter that references both bs_b and bs_c */ + options = qdict_new(); + qdict_put_str(options, "driver", "blkverify"); + qdict_put_str(options, "test", "bs_b"); + qdict_put_str(options, "raw", "bs_c"); + + bs_verify = bdrv_open(NULL, NULL, options, BDRV_O_RDWR, &error_abort); + /* + * Do not take the RESIZE permission: This would require the same + * from bs_c and thus from bs_a; however, blkverify will not share + * it on bs_b, and thus it will not be available for bs_a. + */ + blk = blk_new(qemu_get_aio_context(), BLK_PERM_ALL & ~BLK_PERM_RESIZE, + BLK_PERM_ALL); + blk_insert_bs(blk, bs_verify, &error_abort); + + /* Switch the AioContext */ + blk_set_aio_context(blk, ctx, &error_abort); + g_assert(blk_get_aio_context(blk) == ctx); + g_assert(bdrv_get_aio_context(bs_verify) == ctx); + g_assert(bdrv_get_aio_context(bs_a) == ctx); + g_assert(bdrv_get_aio_context(bs_b) == ctx); + g_assert(bdrv_get_aio_context(bs_c) == ctx); + + /* Switch the AioContext back */ + main_ctx = qemu_get_aio_context(); + aio_context_acquire(ctx); + blk_set_aio_context(blk, main_ctx, &error_abort); + aio_context_release(ctx); + g_assert(blk_get_aio_context(blk) == main_ctx); + g_assert(bdrv_get_aio_context(bs_verify) == main_ctx); + g_assert(bdrv_get_aio_context(bs_a) == main_ctx); + g_assert(bdrv_get_aio_context(bs_b) == main_ctx); + g_assert(bdrv_get_aio_context(bs_c) == main_ctx); + + blk_unref(blk); + bdrv_unref(bs_verify); + bdrv_unref(bs_c); + bdrv_unref(bs_b); + bdrv_unref(bs_a); +} + +static void test_propagate_mirror(void) +{ + IOThread *iothread = iothread_new(); + AioContext *ctx = iothread_get_aio_context(iothread); + AioContext *main_ctx = qemu_get_aio_context(); + BlockDriverState *src, *target, *filter; + BlockBackend *blk; + Job *job; + Error *local_err = NULL; + + /* Create src and target*/ + src = bdrv_new_open_driver(&bdrv_test, "src", BDRV_O_RDWR, &error_abort); + target = bdrv_new_open_driver(&bdrv_test, "target", BDRV_O_RDWR, + &error_abort); + + /* Start a mirror job */ + mirror_start("job0", src, target, NULL, JOB_DEFAULT, 0, 0, 0, + MIRROR_SYNC_MODE_NONE, MIRROR_OPEN_BACKING_CHAIN, false, + BLOCKDEV_ON_ERROR_REPORT, BLOCKDEV_ON_ERROR_REPORT, + false, "filter_node", MIRROR_COPY_MODE_BACKGROUND, + &error_abort); + job = job_get("job0"); + filter = bdrv_find_node("filter_node"); + + /* Change the AioContext of src */ + bdrv_try_set_aio_context(src, ctx, &error_abort); + g_assert(bdrv_get_aio_context(src) == ctx); + g_assert(bdrv_get_aio_context(target) == ctx); + g_assert(bdrv_get_aio_context(filter) == ctx); + g_assert(job->aio_context == ctx); + + /* Change the AioContext of target */ + aio_context_acquire(ctx); + bdrv_try_set_aio_context(target, main_ctx, &error_abort); + aio_context_release(ctx); + g_assert(bdrv_get_aio_context(src) == main_ctx); + g_assert(bdrv_get_aio_context(target) == main_ctx); + g_assert(bdrv_get_aio_context(filter) == main_ctx); + + /* With a BlockBackend on src, changing target must fail */ + blk = blk_new(qemu_get_aio_context(), 0, BLK_PERM_ALL); + blk_insert_bs(blk, src, &error_abort); + + bdrv_try_set_aio_context(target, ctx, &local_err); + error_free_or_abort(&local_err); + + g_assert(blk_get_aio_context(blk) == main_ctx); + g_assert(bdrv_get_aio_context(src) == main_ctx); + g_assert(bdrv_get_aio_context(target) == main_ctx); + g_assert(bdrv_get_aio_context(filter) == main_ctx); + + /* ...unless we explicitly allow it */ + aio_context_acquire(ctx); + blk_set_allow_aio_context_change(blk, true); + bdrv_try_set_aio_context(target, ctx, &error_abort); + aio_context_release(ctx); + + g_assert(blk_get_aio_context(blk) == ctx); + g_assert(bdrv_get_aio_context(src) == ctx); + g_assert(bdrv_get_aio_context(target) == ctx); + g_assert(bdrv_get_aio_context(filter) == ctx); + + job_cancel_sync_all(); + + aio_context_acquire(ctx); + blk_set_aio_context(blk, main_ctx, &error_abort); + bdrv_try_set_aio_context(target, main_ctx, &error_abort); + aio_context_release(ctx); + + blk_unref(blk); + bdrv_unref(src); + bdrv_unref(target); +} + +static void test_attach_second_node(void) +{ + IOThread *iothread = iothread_new(); + AioContext *ctx = iothread_get_aio_context(iothread); + AioContext *main_ctx = qemu_get_aio_context(); + BlockBackend *blk; + BlockDriverState *bs, *filter; + QDict *options; + + blk = blk_new(ctx, BLK_PERM_ALL, BLK_PERM_ALL); + bs = bdrv_new_open_driver(&bdrv_test, "base", BDRV_O_RDWR, &error_abort); + blk_insert_bs(blk, bs, &error_abort); + + options = qdict_new(); + qdict_put_str(options, "driver", "raw"); + qdict_put_str(options, "file", "base"); + + filter = bdrv_open(NULL, NULL, options, BDRV_O_RDWR, &error_abort); + g_assert(blk_get_aio_context(blk) == ctx); + g_assert(bdrv_get_aio_context(bs) == ctx); + g_assert(bdrv_get_aio_context(filter) == ctx); + + aio_context_acquire(ctx); + blk_set_aio_context(blk, main_ctx, &error_abort); + aio_context_release(ctx); + g_assert(blk_get_aio_context(blk) == main_ctx); + g_assert(bdrv_get_aio_context(bs) == main_ctx); + g_assert(bdrv_get_aio_context(filter) == main_ctx); + + bdrv_unref(filter); + bdrv_unref(bs); + blk_unref(blk); +} + +static void test_attach_preserve_blk_ctx(void) +{ + IOThread *iothread = iothread_new(); + AioContext *ctx = iothread_get_aio_context(iothread); + BlockBackend *blk; + BlockDriverState *bs; + + blk = blk_new(ctx, BLK_PERM_ALL, BLK_PERM_ALL); + bs = bdrv_new_open_driver(&bdrv_test, "base", BDRV_O_RDWR, &error_abort); + bs->total_sectors = 65536 / BDRV_SECTOR_SIZE; + + /* Add node to BlockBackend that has an iothread context assigned */ + blk_insert_bs(blk, bs, &error_abort); + g_assert(blk_get_aio_context(blk) == ctx); + g_assert(bdrv_get_aio_context(bs) == ctx); + + /* Remove the node again */ + aio_context_acquire(ctx); + blk_remove_bs(blk); + aio_context_release(ctx); + g_assert(blk_get_aio_context(blk) == ctx); + g_assert(bdrv_get_aio_context(bs) == qemu_get_aio_context()); + + /* Re-attach the node */ + blk_insert_bs(blk, bs, &error_abort); + g_assert(blk_get_aio_context(blk) == ctx); + g_assert(bdrv_get_aio_context(bs) == ctx); + + aio_context_acquire(ctx); + blk_set_aio_context(blk, qemu_get_aio_context(), &error_abort); + aio_context_release(ctx); + bdrv_unref(bs); + blk_unref(blk); +} + +int main(int argc, char **argv) +{ + int i; + + bdrv_init(); + qemu_init_main_loop(&error_abort); + + g_test_init(&argc, &argv, NULL); + + for (i = 0; i < ARRAY_SIZE(sync_op_tests); i++) { + const SyncOpTest *t = &sync_op_tests[i]; + g_test_add_data_func(t->name, t, test_sync_op); + } + + g_test_add_func("/attach/blockjob", test_attach_blockjob); + g_test_add_func("/attach/second_node", test_attach_second_node); + g_test_add_func("/attach/preserve_blk_ctx", test_attach_preserve_blk_ctx); + g_test_add_func("/propagate/basic", test_propagate_basic); + g_test_add_func("/propagate/diamond", test_propagate_diamond); + g_test_add_func("/propagate/mirror", test_propagate_mirror); + + return g_test_run(); +} diff --git a/tests/unit/test-blockjob-txn.c b/tests/unit/test-blockjob-txn.c new file mode 100644 index 0000000000..8bd13b9949 --- /dev/null +++ b/tests/unit/test-blockjob-txn.c @@ -0,0 +1,262 @@ +/* + * Blockjob transactions tests + * + * Copyright Red Hat, Inc. 2015 + * + * Authors: + * Stefan Hajnoczi + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/main-loop.h" +#include "block/blockjob_int.h" +#include "sysemu/block-backend.h" +#include "qapi/qmp/qdict.h" + +typedef struct { + BlockJob common; + unsigned int iterations; + bool use_timer; + int rc; + int *result; +} TestBlockJob; + +static void test_block_job_clean(Job *job) +{ + BlockJob *bjob = container_of(job, BlockJob, job); + BlockDriverState *bs = blk_bs(bjob->blk); + + bdrv_unref(bs); +} + +static int coroutine_fn test_block_job_run(Job *job, Error **errp) +{ + TestBlockJob *s = container_of(job, TestBlockJob, common.job); + + while (s->iterations--) { + if (s->use_timer) { + job_sleep_ns(job, 0); + } else { + job_yield(job); + } + + if (job_is_cancelled(job)) { + break; + } + } + + return s->rc; +} + +typedef struct { + TestBlockJob *job; + int *result; +} TestBlockJobCBData; + +static void test_block_job_cb(void *opaque, int ret) +{ + TestBlockJobCBData *data = opaque; + if (!ret && job_is_cancelled(&data->job->common.job)) { + ret = -ECANCELED; + } + *data->result = ret; + g_free(data); +} + +static const BlockJobDriver test_block_job_driver = { + .job_driver = { + .instance_size = sizeof(TestBlockJob), + .free = block_job_free, + .user_resume = block_job_user_resume, + .run = test_block_job_run, + .clean = test_block_job_clean, + }, +}; + +/* Create a block job that completes with a given return code after a given + * number of event loop iterations. The return code is stored in the given + * result pointer. + * + * The event loop iterations can either be handled automatically with a 0 delay + * timer, or they can be stepped manually by entering the coroutine. + */ +static BlockJob *test_block_job_start(unsigned int iterations, + bool use_timer, + int rc, int *result, JobTxn *txn) +{ + BlockDriverState *bs; + TestBlockJob *s; + TestBlockJobCBData *data; + static unsigned counter; + char job_id[24]; + + data = g_new0(TestBlockJobCBData, 1); + + QDict *opt = qdict_new(); + qdict_put_str(opt, "file.read-zeroes", "on"); + bs = bdrv_open("null-co://", NULL, opt, 0, &error_abort); + g_assert_nonnull(bs); + + snprintf(job_id, sizeof(job_id), "job%u", counter++); + s = block_job_create(job_id, &test_block_job_driver, txn, bs, + 0, BLK_PERM_ALL, 0, JOB_DEFAULT, + test_block_job_cb, data, &error_abort); + s->iterations = iterations; + s->use_timer = use_timer; + s->rc = rc; + s->result = result; + data->job = s; + data->result = result; + return &s->common; +} + +static void test_single_job(int expected) +{ + BlockJob *job; + JobTxn *txn; + int result = -EINPROGRESS; + + txn = job_txn_new(); + job = test_block_job_start(1, true, expected, &result, txn); + job_start(&job->job); + + if (expected == -ECANCELED) { + job_cancel(&job->job, false); + } + + while (result == -EINPROGRESS) { + aio_poll(qemu_get_aio_context(), true); + } + g_assert_cmpint(result, ==, expected); + + job_txn_unref(txn); +} + +static void test_single_job_success(void) +{ + test_single_job(0); +} + +static void test_single_job_failure(void) +{ + test_single_job(-EIO); +} + +static void test_single_job_cancel(void) +{ + test_single_job(-ECANCELED); +} + +static void test_pair_jobs(int expected1, int expected2) +{ + BlockJob *job1; + BlockJob *job2; + JobTxn *txn; + int result1 = -EINPROGRESS; + int result2 = -EINPROGRESS; + + txn = job_txn_new(); + job1 = test_block_job_start(1, true, expected1, &result1, txn); + job2 = test_block_job_start(2, true, expected2, &result2, txn); + job_start(&job1->job); + job_start(&job2->job); + + /* Release our reference now to trigger as many nice + * use-after-free bugs as possible. + */ + job_txn_unref(txn); + + if (expected1 == -ECANCELED) { + job_cancel(&job1->job, false); + } + if (expected2 == -ECANCELED) { + job_cancel(&job2->job, false); + } + + while (result1 == -EINPROGRESS || result2 == -EINPROGRESS) { + aio_poll(qemu_get_aio_context(), true); + } + + /* Failure or cancellation of one job cancels the other job */ + if (expected1 != 0) { + expected2 = -ECANCELED; + } else if (expected2 != 0) { + expected1 = -ECANCELED; + } + + g_assert_cmpint(result1, ==, expected1); + g_assert_cmpint(result2, ==, expected2); +} + +static void test_pair_jobs_success(void) +{ + test_pair_jobs(0, 0); +} + +static void test_pair_jobs_failure(void) +{ + /* Test both orderings. The two jobs run for a different number of + * iterations so the code path is different depending on which job fails + * first. + */ + test_pair_jobs(-EIO, 0); + test_pair_jobs(0, -EIO); +} + +static void test_pair_jobs_cancel(void) +{ + test_pair_jobs(-ECANCELED, 0); + test_pair_jobs(0, -ECANCELED); +} + +static void test_pair_jobs_fail_cancel_race(void) +{ + BlockJob *job1; + BlockJob *job2; + JobTxn *txn; + int result1 = -EINPROGRESS; + int result2 = -EINPROGRESS; + + txn = job_txn_new(); + job1 = test_block_job_start(1, true, -ECANCELED, &result1, txn); + job2 = test_block_job_start(2, false, 0, &result2, txn); + job_start(&job1->job); + job_start(&job2->job); + + job_cancel(&job1->job, false); + + /* Now make job2 finish before the main loop kicks jobs. This simulates + * the race between a pending kick and another job completing. + */ + job_enter(&job2->job); + job_enter(&job2->job); + + while (result1 == -EINPROGRESS || result2 == -EINPROGRESS) { + aio_poll(qemu_get_aio_context(), true); + } + + g_assert_cmpint(result1, ==, -ECANCELED); + g_assert_cmpint(result2, ==, -ECANCELED); + + job_txn_unref(txn); +} + +int main(int argc, char **argv) +{ + qemu_init_main_loop(&error_abort); + bdrv_init(); + + g_test_init(&argc, &argv, NULL); + g_test_add_func("/single/success", test_single_job_success); + g_test_add_func("/single/failure", test_single_job_failure); + g_test_add_func("/single/cancel", test_single_job_cancel); + g_test_add_func("/pair/success", test_pair_jobs_success); + g_test_add_func("/pair/failure", test_pair_jobs_failure); + g_test_add_func("/pair/cancel", test_pair_jobs_cancel); + g_test_add_func("/pair/fail-cancel-race", test_pair_jobs_fail_cancel_race); + return g_test_run(); +} diff --git a/tests/unit/test-blockjob.c b/tests/unit/test-blockjob.c new file mode 100644 index 0000000000..7519847912 --- /dev/null +++ b/tests/unit/test-blockjob.c @@ -0,0 +1,393 @@ +/* + * Blockjob tests + * + * Copyright Igalia, S.L. 2016 + * + * Authors: + * Alberto Garcia + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/main-loop.h" +#include "block/blockjob_int.h" +#include "sysemu/block-backend.h" +#include "qapi/qmp/qdict.h" + +static const BlockJobDriver test_block_job_driver = { + .job_driver = { + .instance_size = sizeof(BlockJob), + .free = block_job_free, + .user_resume = block_job_user_resume, + }, +}; + +static void block_job_cb(void *opaque, int ret) +{ +} + +static BlockJob *mk_job(BlockBackend *blk, const char *id, + const BlockJobDriver *drv, bool should_succeed, + int flags) +{ + BlockJob *job; + Error *err = NULL; + + job = block_job_create(id, drv, NULL, blk_bs(blk), + 0, BLK_PERM_ALL, 0, flags, block_job_cb, + NULL, &err); + if (should_succeed) { + g_assert_null(err); + g_assert_nonnull(job); + if (id) { + g_assert_cmpstr(job->job.id, ==, id); + } else { + g_assert_cmpstr(job->job.id, ==, blk_name(blk)); + } + } else { + error_free_or_abort(&err); + g_assert_null(job); + } + + return job; +} + +static BlockJob *do_test_id(BlockBackend *blk, const char *id, + bool should_succeed) +{ + return mk_job(blk, id, &test_block_job_driver, + should_succeed, JOB_DEFAULT); +} + +/* This creates a BlockBackend (optionally with a name) with a + * BlockDriverState inserted. */ +static BlockBackend *create_blk(const char *name) +{ + /* No I/O is performed on this device */ + BlockBackend *blk = blk_new(qemu_get_aio_context(), 0, BLK_PERM_ALL); + BlockDriverState *bs; + + QDict *opt = qdict_new(); + qdict_put_str(opt, "file.read-zeroes", "on"); + bs = bdrv_open("null-co://", NULL, opt, 0, &error_abort); + g_assert_nonnull(bs); + + blk_insert_bs(blk, bs, &error_abort); + bdrv_unref(bs); + + if (name) { + Error *err = NULL; + monitor_add_blk(blk, name, &err); + g_assert_null(err); + } + + return blk; +} + +/* This destroys the backend */ +static void destroy_blk(BlockBackend *blk) +{ + if (blk_name(blk)[0] != '\0') { + monitor_remove_blk(blk); + } + + blk_remove_bs(blk); + blk_unref(blk); +} + +static void test_job_ids(void) +{ + BlockBackend *blk[3]; + BlockJob *job[3]; + + blk[0] = create_blk(NULL); + blk[1] = create_blk("drive1"); + blk[2] = create_blk("drive2"); + + /* No job ID provided and the block backend has no name */ + job[0] = do_test_id(blk[0], NULL, false); + + /* These are all invalid job IDs */ + job[0] = do_test_id(blk[0], "0id", false); + job[0] = do_test_id(blk[0], "", false); + job[0] = do_test_id(blk[0], " ", false); + job[0] = do_test_id(blk[0], "123", false); + job[0] = do_test_id(blk[0], "_id", false); + job[0] = do_test_id(blk[0], "-id", false); + job[0] = do_test_id(blk[0], ".id", false); + job[0] = do_test_id(blk[0], "#id", false); + + /* This one is valid */ + job[0] = do_test_id(blk[0], "id0", true); + + /* We can have two jobs in the same BDS */ + job[1] = do_test_id(blk[0], "id1", true); + job_early_fail(&job[1]->job); + + /* Duplicate job IDs are not allowed */ + job[1] = do_test_id(blk[1], "id0", false); + + /* But once job[0] finishes we can reuse its ID */ + job_early_fail(&job[0]->job); + job[1] = do_test_id(blk[1], "id0", true); + + /* No job ID specified, defaults to the backend name ('drive1') */ + job_early_fail(&job[1]->job); + job[1] = do_test_id(blk[1], NULL, true); + + /* Duplicate job ID */ + job[2] = do_test_id(blk[2], "drive1", false); + + /* The ID of job[2] would default to 'drive2' but it is already in use */ + job[0] = do_test_id(blk[0], "drive2", true); + job[2] = do_test_id(blk[2], NULL, false); + + /* This one is valid */ + job[2] = do_test_id(blk[2], "id_2", true); + + job_early_fail(&job[0]->job); + job_early_fail(&job[1]->job); + job_early_fail(&job[2]->job); + + destroy_blk(blk[0]); + destroy_blk(blk[1]); + destroy_blk(blk[2]); +} + +typedef struct CancelJob { + BlockJob common; + BlockBackend *blk; + bool should_converge; + bool should_complete; +} CancelJob; + +static void cancel_job_complete(Job *job, Error **errp) +{ + CancelJob *s = container_of(job, CancelJob, common.job); + s->should_complete = true; +} + +static int coroutine_fn cancel_job_run(Job *job, Error **errp) +{ + CancelJob *s = container_of(job, CancelJob, common.job); + + while (!s->should_complete) { + if (job_is_cancelled(&s->common.job)) { + return 0; + } + + if (!job_is_ready(&s->common.job) && s->should_converge) { + job_transition_to_ready(&s->common.job); + } + + job_sleep_ns(&s->common.job, 100000); + } + + return 0; +} + +static const BlockJobDriver test_cancel_driver = { + .job_driver = { + .instance_size = sizeof(CancelJob), + .free = block_job_free, + .user_resume = block_job_user_resume, + .run = cancel_job_run, + .complete = cancel_job_complete, + }, +}; + +static CancelJob *create_common(Job **pjob) +{ + BlockBackend *blk; + Job *job; + BlockJob *bjob; + CancelJob *s; + + blk = create_blk(NULL); + bjob = mk_job(blk, "Steve", &test_cancel_driver, true, + JOB_MANUAL_FINALIZE | JOB_MANUAL_DISMISS); + job = &bjob->job; + job_ref(job); + assert(job->status == JOB_STATUS_CREATED); + s = container_of(bjob, CancelJob, common); + s->blk = blk; + + *pjob = job; + return s; +} + +static void cancel_common(CancelJob *s) +{ + BlockJob *job = &s->common; + BlockBackend *blk = s->blk; + JobStatus sts = job->job.status; + AioContext *ctx; + + ctx = job->job.aio_context; + aio_context_acquire(ctx); + + job_cancel_sync(&job->job); + if (sts != JOB_STATUS_CREATED && sts != JOB_STATUS_CONCLUDED) { + Job *dummy = &job->job; + job_dismiss(&dummy, &error_abort); + } + assert(job->job.status == JOB_STATUS_NULL); + job_unref(&job->job); + destroy_blk(blk); + + aio_context_release(ctx); +} + +static void test_cancel_created(void) +{ + Job *job; + CancelJob *s; + + s = create_common(&job); + cancel_common(s); +} + +static void test_cancel_running(void) +{ + Job *job; + CancelJob *s; + + s = create_common(&job); + + job_start(job); + assert(job->status == JOB_STATUS_RUNNING); + + cancel_common(s); +} + +static void test_cancel_paused(void) +{ + Job *job; + CancelJob *s; + + s = create_common(&job); + + job_start(job); + assert(job->status == JOB_STATUS_RUNNING); + + job_user_pause(job, &error_abort); + job_enter(job); + assert(job->status == JOB_STATUS_PAUSED); + + cancel_common(s); +} + +static void test_cancel_ready(void) +{ + Job *job; + CancelJob *s; + + s = create_common(&job); + + job_start(job); + assert(job->status == JOB_STATUS_RUNNING); + + s->should_converge = true; + job_enter(job); + assert(job->status == JOB_STATUS_READY); + + cancel_common(s); +} + +static void test_cancel_standby(void) +{ + Job *job; + CancelJob *s; + + s = create_common(&job); + + job_start(job); + assert(job->status == JOB_STATUS_RUNNING); + + s->should_converge = true; + job_enter(job); + assert(job->status == JOB_STATUS_READY); + + job_user_pause(job, &error_abort); + job_enter(job); + assert(job->status == JOB_STATUS_STANDBY); + + cancel_common(s); +} + +static void test_cancel_pending(void) +{ + Job *job; + CancelJob *s; + + s = create_common(&job); + + job_start(job); + assert(job->status == JOB_STATUS_RUNNING); + + s->should_converge = true; + job_enter(job); + assert(job->status == JOB_STATUS_READY); + + job_complete(job, &error_abort); + job_enter(job); + while (!job->deferred_to_main_loop) { + aio_poll(qemu_get_aio_context(), true); + } + assert(job->status == JOB_STATUS_READY); + aio_poll(qemu_get_aio_context(), true); + assert(job->status == JOB_STATUS_PENDING); + + cancel_common(s); +} + +static void test_cancel_concluded(void) +{ + Job *job; + CancelJob *s; + + s = create_common(&job); + + job_start(job); + assert(job->status == JOB_STATUS_RUNNING); + + s->should_converge = true; + job_enter(job); + assert(job->status == JOB_STATUS_READY); + + job_complete(job, &error_abort); + job_enter(job); + while (!job->deferred_to_main_loop) { + aio_poll(qemu_get_aio_context(), true); + } + assert(job->status == JOB_STATUS_READY); + aio_poll(qemu_get_aio_context(), true); + assert(job->status == JOB_STATUS_PENDING); + + aio_context_acquire(job->aio_context); + job_finalize(job, &error_abort); + aio_context_release(job->aio_context); + assert(job->status == JOB_STATUS_CONCLUDED); + + cancel_common(s); +} + +int main(int argc, char **argv) +{ + qemu_init_main_loop(&error_abort); + bdrv_init(); + + g_test_init(&argc, &argv, NULL); + g_test_add_func("/blockjob/ids", test_job_ids); + g_test_add_func("/blockjob/cancel/created", test_cancel_created); + g_test_add_func("/blockjob/cancel/running", test_cancel_running); + g_test_add_func("/blockjob/cancel/paused", test_cancel_paused); + g_test_add_func("/blockjob/cancel/ready", test_cancel_ready); + g_test_add_func("/blockjob/cancel/standby", test_cancel_standby); + g_test_add_func("/blockjob/cancel/pending", test_cancel_pending); + g_test_add_func("/blockjob/cancel/concluded", test_cancel_concluded); + return g_test_run(); +} diff --git a/tests/unit/test-bufferiszero.c b/tests/unit/test-bufferiszero.c new file mode 100644 index 0000000000..e45fd31804 --- /dev/null +++ b/tests/unit/test-bufferiszero.c @@ -0,0 +1,78 @@ +/* + * QEMU buffer_is_zero test + * + * Copyright (c) 2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + * + */ + +#include "qemu/osdep.h" +#include "qemu/cutils.h" + +static char buffer[8 * 1024 * 1024]; + +static void test_1(void) +{ + size_t s, a, o; + + /* Basic positive test. */ + g_assert(buffer_is_zero(buffer, sizeof(buffer))); + + /* Basic negative test. */ + buffer[sizeof(buffer) - 1] = 1; + g_assert(!buffer_is_zero(buffer, sizeof(buffer))); + buffer[sizeof(buffer) - 1] = 0; + + /* Positive tests for size and alignment. */ + for (a = 1; a <= 64; a++) { + for (s = 1; s < 1024; s++) { + buffer[a - 1] = 1; + buffer[a + s] = 1; + g_assert(buffer_is_zero(buffer + a, s)); + buffer[a - 1] = 0; + buffer[a + s] = 0; + } + } + + /* Negative tests for size, alignment, and the offset of the marker. */ + for (a = 1; a <= 64; a++) { + for (s = 1; s < 1024; s++) { + for (o = 0; o < s; ++o) { + buffer[a + o] = 1; + g_assert(!buffer_is_zero(buffer + a, s)); + buffer[a + o] = 0; + } + } + } +} + +static void test_2(void) +{ + if (g_test_perf()) { + test_1(); + } else { + do { + test_1(); + } while (test_buffer_is_zero_next_accel()); + } +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + g_test_add_func("/cutils/bufferiszero", test_2); + + return g_test_run(); +} diff --git a/tests/unit/test-char.c b/tests/unit/test-char.c new file mode 100644 index 0000000000..755d54c15e --- /dev/null +++ b/tests/unit/test-char.c @@ -0,0 +1,1560 @@ +#include "qemu/osdep.h" +#include + +#include "qemu/config-file.h" +#include "qemu/module.h" +#include "qemu/option.h" +#include "qemu/sockets.h" +#include "chardev/char-fe.h" +#include "sysemu/sysemu.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-char.h" +#include "qapi/qmp/qdict.h" +#include "qom/qom-qobject.h" +#include "io/channel-socket.h" +#include "qapi/qobject-input-visitor.h" +#include "qapi/qapi-visit-sockets.h" +#include "socket-helpers.h" + +static bool quit; + +typedef struct FeHandler { + int read_count; + bool is_open; + int openclose_count; + bool openclose_mismatch; + int last_event; + char read_buf[128]; +} FeHandler; + +static void main_loop(void) +{ + quit = false; + do { + main_loop_wait(false); + } while (!quit); +} + +static int fe_can_read(void *opaque) +{ + FeHandler *h = opaque; + + return sizeof(h->read_buf) - h->read_count; +} + +static void fe_read(void *opaque, const uint8_t *buf, int size) +{ + FeHandler *h = opaque; + + g_assert_cmpint(size, <=, fe_can_read(opaque)); + + memcpy(h->read_buf + h->read_count, buf, size); + h->read_count += size; + quit = true; +} + +static void fe_event(void *opaque, QEMUChrEvent event) +{ + FeHandler *h = opaque; + bool new_open_state; + + h->last_event = event; + switch (event) { + case CHR_EVENT_BREAK: + break; + case CHR_EVENT_OPENED: + case CHR_EVENT_CLOSED: + h->openclose_count++; + new_open_state = (event == CHR_EVENT_OPENED); + if (h->is_open == new_open_state) { + h->openclose_mismatch = true; + } + h->is_open = new_open_state; + /* fallthrough */ + default: + quit = true; + break; + } +} + +#ifdef _WIN32 +static void char_console_test_subprocess(void) +{ + QemuOpts *opts; + Chardev *chr; + + opts = qemu_opts_create(qemu_find_opts("chardev"), "console-label", + 1, &error_abort); + qemu_opt_set(opts, "backend", "console", &error_abort); + + chr = qemu_chr_new_from_opts(opts, NULL, NULL); + g_assert_nonnull(chr); + + qemu_chr_write_all(chr, (const uint8_t *)"CONSOLE", 7); + + qemu_opts_del(opts); + object_unparent(OBJECT(chr)); +} + +static void char_console_test(void) +{ + g_test_trap_subprocess("/char/console/subprocess", 0, 0); + g_test_trap_assert_passed(); + g_test_trap_assert_stdout("CONSOLE"); +} +#endif +static void char_stdio_test_subprocess(void) +{ + Chardev *chr; + CharBackend be; + int ret; + + chr = qemu_chr_new("label", "stdio", NULL); + g_assert_nonnull(chr); + + qemu_chr_fe_init(&be, chr, &error_abort); + qemu_chr_fe_set_open(&be, true); + ret = qemu_chr_fe_write(&be, (void *)"buf", 4); + g_assert_cmpint(ret, ==, 4); + + qemu_chr_fe_deinit(&be, true); +} + +static void char_stdio_test(void) +{ + g_test_trap_subprocess("/char/stdio/subprocess", 0, 0); + g_test_trap_assert_passed(); + g_test_trap_assert_stdout("buf"); +} + +static void char_ringbuf_test(void) +{ + QemuOpts *opts; + Chardev *chr; + CharBackend be; + char *data; + int ret; + + opts = qemu_opts_create(qemu_find_opts("chardev"), "ringbuf-label", + 1, &error_abort); + qemu_opt_set(opts, "backend", "ringbuf", &error_abort); + + qemu_opt_set(opts, "size", "5", &error_abort); + chr = qemu_chr_new_from_opts(opts, NULL, NULL); + g_assert_null(chr); + qemu_opts_del(opts); + + opts = qemu_opts_create(qemu_find_opts("chardev"), "ringbuf-label", + 1, &error_abort); + qemu_opt_set(opts, "backend", "ringbuf", &error_abort); + qemu_opt_set(opts, "size", "2", &error_abort); + chr = qemu_chr_new_from_opts(opts, NULL, &error_abort); + g_assert_nonnull(chr); + qemu_opts_del(opts); + + qemu_chr_fe_init(&be, chr, &error_abort); + ret = qemu_chr_fe_write(&be, (void *)"buff", 4); + g_assert_cmpint(ret, ==, 4); + + data = qmp_ringbuf_read("ringbuf-label", 4, false, 0, &error_abort); + g_assert_cmpstr(data, ==, "ff"); + g_free(data); + + data = qmp_ringbuf_read("ringbuf-label", 4, false, 0, &error_abort); + g_assert_cmpstr(data, ==, ""); + g_free(data); + + qemu_chr_fe_deinit(&be, true); + + /* check alias */ + opts = qemu_opts_create(qemu_find_opts("chardev"), "memory-label", + 1, &error_abort); + qemu_opt_set(opts, "backend", "memory", &error_abort); + qemu_opt_set(opts, "size", "2", &error_abort); + chr = qemu_chr_new_from_opts(opts, NULL, NULL); + g_assert_nonnull(chr); + object_unparent(OBJECT(chr)); + qemu_opts_del(opts); +} + +static void char_mux_test(void) +{ + QemuOpts *opts; + Chardev *chr, *base; + char *data; + FeHandler h1 = { 0, false, 0, false, }, h2 = { 0, false, 0, false, }; + CharBackend chr_be1, chr_be2; + + opts = qemu_opts_create(qemu_find_opts("chardev"), "mux-label", + 1, &error_abort); + qemu_opt_set(opts, "backend", "ringbuf", &error_abort); + qemu_opt_set(opts, "size", "128", &error_abort); + qemu_opt_set(opts, "mux", "on", &error_abort); + chr = qemu_chr_new_from_opts(opts, NULL, &error_abort); + g_assert_nonnull(chr); + qemu_opts_del(opts); + + qemu_chr_fe_init(&chr_be1, chr, &error_abort); + qemu_chr_fe_set_handlers(&chr_be1, + fe_can_read, + fe_read, + fe_event, + NULL, + &h1, + NULL, true); + + qemu_chr_fe_init(&chr_be2, chr, &error_abort); + qemu_chr_fe_set_handlers(&chr_be2, + fe_can_read, + fe_read, + fe_event, + NULL, + &h2, + NULL, true); + qemu_chr_fe_take_focus(&chr_be2); + + base = qemu_chr_find("mux-label-base"); + g_assert_cmpint(qemu_chr_be_can_write(base), !=, 0); + + qemu_chr_be_write(base, (void *)"hello", 6); + g_assert_cmpint(h1.read_count, ==, 0); + g_assert_cmpint(h2.read_count, ==, 6); + g_assert_cmpstr(h2.read_buf, ==, "hello"); + h2.read_count = 0; + + g_assert_cmpint(h1.last_event, !=, 42); /* should be MUX_OUT or OPENED */ + g_assert_cmpint(h2.last_event, !=, 42); /* should be MUX_IN or OPENED */ + /* sending event on the base broadcast to all fe, historical reasons? */ + qemu_chr_be_event(base, 42); + g_assert_cmpint(h1.last_event, ==, 42); + g_assert_cmpint(h2.last_event, ==, 42); + qemu_chr_be_event(chr, -1); + g_assert_cmpint(h1.last_event, ==, 42); + g_assert_cmpint(h2.last_event, ==, -1); + + /* switch focus */ + qemu_chr_be_write(base, (void *)"\1b", 2); + g_assert_cmpint(h1.last_event, ==, 42); + g_assert_cmpint(h2.last_event, ==, CHR_EVENT_BREAK); + + qemu_chr_be_write(base, (void *)"\1c", 2); + g_assert_cmpint(h1.last_event, ==, CHR_EVENT_MUX_IN); + g_assert_cmpint(h2.last_event, ==, CHR_EVENT_MUX_OUT); + qemu_chr_be_event(chr, -1); + g_assert_cmpint(h1.last_event, ==, -1); + g_assert_cmpint(h2.last_event, ==, CHR_EVENT_MUX_OUT); + + qemu_chr_be_write(base, (void *)"hello", 6); + g_assert_cmpint(h2.read_count, ==, 0); + g_assert_cmpint(h1.read_count, ==, 6); + g_assert_cmpstr(h1.read_buf, ==, "hello"); + h1.read_count = 0; + + qemu_chr_be_write(base, (void *)"\1b", 2); + g_assert_cmpint(h1.last_event, ==, CHR_EVENT_BREAK); + g_assert_cmpint(h2.last_event, ==, CHR_EVENT_MUX_OUT); + + /* open/close state and corresponding events */ + g_assert_true(qemu_chr_fe_backend_open(&chr_be1)); + g_assert_true(qemu_chr_fe_backend_open(&chr_be2)); + g_assert_true(h1.is_open); + g_assert_false(h1.openclose_mismatch); + g_assert_true(h2.is_open); + g_assert_false(h2.openclose_mismatch); + + h1.openclose_count = h2.openclose_count = 0; + + qemu_chr_fe_set_handlers(&chr_be1, NULL, NULL, NULL, NULL, + NULL, NULL, false); + qemu_chr_fe_set_handlers(&chr_be2, NULL, NULL, NULL, NULL, + NULL, NULL, false); + g_assert_cmpint(h1.openclose_count, ==, 0); + g_assert_cmpint(h2.openclose_count, ==, 0); + + h1.is_open = h2.is_open = false; + qemu_chr_fe_set_handlers(&chr_be1, + NULL, + NULL, + fe_event, + NULL, + &h1, + NULL, false); + qemu_chr_fe_set_handlers(&chr_be2, + NULL, + NULL, + fe_event, + NULL, + &h2, + NULL, false); + g_assert_cmpint(h1.openclose_count, ==, 1); + g_assert_false(h1.openclose_mismatch); + g_assert_cmpint(h2.openclose_count, ==, 1); + g_assert_false(h2.openclose_mismatch); + + qemu_chr_be_event(base, CHR_EVENT_CLOSED); + qemu_chr_be_event(base, CHR_EVENT_OPENED); + g_assert_cmpint(h1.openclose_count, ==, 3); + g_assert_false(h1.openclose_mismatch); + g_assert_cmpint(h2.openclose_count, ==, 3); + g_assert_false(h2.openclose_mismatch); + + qemu_chr_fe_set_handlers(&chr_be2, + fe_can_read, + fe_read, + fe_event, + NULL, + &h2, + NULL, false); + qemu_chr_fe_set_handlers(&chr_be1, + fe_can_read, + fe_read, + fe_event, + NULL, + &h1, + NULL, false); + + /* remove first handler */ + qemu_chr_fe_set_handlers(&chr_be1, NULL, NULL, NULL, NULL, + NULL, NULL, true); + qemu_chr_be_write(base, (void *)"hello", 6); + g_assert_cmpint(h1.read_count, ==, 0); + g_assert_cmpint(h2.read_count, ==, 0); + + qemu_chr_be_write(base, (void *)"\1c", 2); + qemu_chr_be_write(base, (void *)"hello", 6); + g_assert_cmpint(h1.read_count, ==, 0); + g_assert_cmpint(h2.read_count, ==, 6); + g_assert_cmpstr(h2.read_buf, ==, "hello"); + h2.read_count = 0; + + /* print help */ + qemu_chr_be_write(base, (void *)"\1?", 2); + data = qmp_ringbuf_read("mux-label-base", 128, false, 0, &error_abort); + g_assert_cmpint(strlen(data), !=, 0); + g_free(data); + + qemu_chr_fe_deinit(&chr_be1, false); + qemu_chr_fe_deinit(&chr_be2, true); +} + + +static void websock_server_read(void *opaque, const uint8_t *buf, int size) +{ + g_assert_cmpint(size, ==, 5); + g_assert(memcmp(buf, "world", size) == 0); + quit = true; +} + + +static int websock_server_can_read(void *opaque) +{ + return 10; +} + + +static bool websock_check_http_headers(char *buf, int size) +{ + int i; + const char *ans[] = { "HTTP/1.1 101 Switching Protocols\r\n", + "Server: QEMU VNC\r\n", + "Upgrade: websocket\r\n", + "Connection: Upgrade\r\n", + "Sec-WebSocket-Accept:", + "Sec-WebSocket-Protocol: binary\r\n" }; + + for (i = 0; i < 6; i++) { + if (g_strstr_len(buf, size, ans[i]) == NULL) { + return false; + } + } + + return true; +} + + +static void websock_client_read(void *opaque, const uint8_t *buf, int size) +{ + const uint8_t ping[] = { 0x89, 0x85, /* Ping header */ + 0x07, 0x77, 0x9e, 0xf9, /* Masking key */ + 0x6f, 0x12, 0xf2, 0x95, 0x68 /* "hello" */ }; + + const uint8_t binary[] = { 0x82, 0x85, /* Binary header */ + 0x74, 0x90, 0xb9, 0xdf, /* Masking key */ + 0x03, 0xff, 0xcb, 0xb3, 0x10 /* "world" */ }; + Chardev *chr_client = opaque; + + if (websock_check_http_headers((char *) buf, size)) { + qemu_chr_fe_write(chr_client->be, ping, sizeof(ping)); + } else if (buf[0] == 0x8a && buf[1] == 0x05) { + g_assert(strncmp((char *) buf + 2, "hello", 5) == 0); + qemu_chr_fe_write(chr_client->be, binary, sizeof(binary)); + } else { + g_assert(buf[0] == 0x88 && buf[1] == 0x16); + g_assert(strncmp((char *) buf + 4, "peer requested close", 10) == 0); + quit = true; + } +} + + +static int websock_client_can_read(void *opaque) +{ + return 4096; +} + + +static void char_websock_test(void) +{ + QObject *addr; + QDict *qdict; + const char *port; + char *tmp; + char *handshake_port; + CharBackend be; + CharBackend client_be; + Chardev *chr_client; + Chardev *chr = qemu_chr_new("server", + "websocket:127.0.0.1:0,server=on,wait=off", NULL); + const char handshake[] = "GET / HTTP/1.1\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n" + "Host: localhost:%s\r\n" + "Origin: http://localhost:%s\r\n" + "Sec-WebSocket-Key: o9JHNiS3/0/0zYE1wa3yIw==\r\n" + "Sec-WebSocket-Version: 13\r\n" + "Sec-WebSocket-Protocol: binary\r\n\r\n"; + const uint8_t close[] = { 0x88, 0x82, /* Close header */ + 0xef, 0xaa, 0xc5, 0x97, /* Masking key */ + 0xec, 0x42 /* Status code */ }; + + addr = object_property_get_qobject(OBJECT(chr), "addr", &error_abort); + qdict = qobject_to(QDict, addr); + port = qdict_get_str(qdict, "port"); + tmp = g_strdup_printf("tcp:127.0.0.1:%s", port); + handshake_port = g_strdup_printf(handshake, port, port); + qobject_unref(qdict); + + qemu_chr_fe_init(&be, chr, &error_abort); + qemu_chr_fe_set_handlers(&be, websock_server_can_read, websock_server_read, + NULL, NULL, chr, NULL, true); + + chr_client = qemu_chr_new("client", tmp, NULL); + qemu_chr_fe_init(&client_be, chr_client, &error_abort); + qemu_chr_fe_set_handlers(&client_be, websock_client_can_read, + websock_client_read, + NULL, NULL, chr_client, NULL, true); + g_free(tmp); + + qemu_chr_write_all(chr_client, + (uint8_t *) handshake_port, + strlen(handshake_port)); + g_free(handshake_port); + main_loop(); + + g_assert(object_property_get_bool(OBJECT(chr), "connected", &error_abort)); + g_assert(object_property_get_bool(OBJECT(chr_client), + "connected", &error_abort)); + + qemu_chr_write_all(chr_client, close, sizeof(close)); + main_loop(); + + object_unparent(OBJECT(chr_client)); + object_unparent(OBJECT(chr)); +} + + +#ifndef _WIN32 +static void char_pipe_test(void) +{ + gchar *tmp_path = g_dir_make_tmp("qemu-test-char.XXXXXX", NULL); + gchar *tmp, *in, *out, *pipe = g_build_filename(tmp_path, "pipe", NULL); + Chardev *chr; + CharBackend be; + int ret, fd; + char buf[10]; + FeHandler fe = { 0, }; + + in = g_strdup_printf("%s.in", pipe); + if (mkfifo(in, 0600) < 0) { + abort(); + } + out = g_strdup_printf("%s.out", pipe); + if (mkfifo(out, 0600) < 0) { + abort(); + } + + tmp = g_strdup_printf("pipe:%s", pipe); + chr = qemu_chr_new("pipe", tmp, NULL); + g_assert_nonnull(chr); + g_free(tmp); + + qemu_chr_fe_init(&be, chr, &error_abort); + + ret = qemu_chr_fe_write(&be, (void *)"pipe-out", 9); + g_assert_cmpint(ret, ==, 9); + + fd = open(out, O_RDWR); + ret = read(fd, buf, sizeof(buf)); + g_assert_cmpint(ret, ==, 9); + g_assert_cmpstr(buf, ==, "pipe-out"); + close(fd); + + fd = open(in, O_WRONLY); + ret = write(fd, "pipe-in", 8); + g_assert_cmpint(ret, ==, 8); + close(fd); + + qemu_chr_fe_set_handlers(&be, + fe_can_read, + fe_read, + fe_event, + NULL, + &fe, + NULL, true); + + main_loop(); + + g_assert_cmpint(fe.read_count, ==, 8); + g_assert_cmpstr(fe.read_buf, ==, "pipe-in"); + + qemu_chr_fe_deinit(&be, true); + + g_assert(g_unlink(in) == 0); + g_assert(g_unlink(out) == 0); + g_assert(g_rmdir(tmp_path) == 0); + g_free(in); + g_free(out); + g_free(tmp_path); + g_free(pipe); +} +#endif + +typedef struct SocketIdleData { + GMainLoop *loop; + Chardev *chr; + bool conn_expected; + CharBackend *be; + CharBackend *client_be; +} SocketIdleData; + + +static void socket_read_hello(void *opaque, const uint8_t *buf, int size) +{ + g_assert_cmpint(size, ==, 5); + g_assert(strncmp((char *)buf, "hello", 5) == 0); + + quit = true; +} + +static int socket_can_read_hello(void *opaque) +{ + return 10; +} + +static int make_udp_socket(int *port) +{ + struct sockaddr_in addr = { 0, }; + socklen_t alen = sizeof(addr); + int ret, sock = qemu_socket(PF_INET, SOCK_DGRAM, 0); + + g_assert_cmpint(sock, >, 0); + addr.sin_family = AF_INET ; + addr.sin_addr.s_addr = htonl(INADDR_ANY); + addr.sin_port = 0; + ret = bind(sock, (struct sockaddr *)&addr, sizeof(addr)); + g_assert_cmpint(ret, ==, 0); + ret = getsockname(sock, (struct sockaddr *)&addr, &alen); + g_assert_cmpint(ret, ==, 0); + + *port = ntohs(addr.sin_port); + return sock; +} + +static void char_udp_test_internal(Chardev *reuse_chr, int sock) +{ + struct sockaddr_in other; + SocketIdleData d = { 0, }; + Chardev *chr; + CharBackend *be; + socklen_t alen = sizeof(other); + int ret; + char buf[10]; + char *tmp = NULL; + + if (reuse_chr) { + chr = reuse_chr; + be = chr->be; + } else { + int port; + sock = make_udp_socket(&port); + tmp = g_strdup_printf("udp:127.0.0.1:%d", port); + chr = qemu_chr_new("client", tmp, NULL); + g_assert_nonnull(chr); + + be = g_alloca(sizeof(CharBackend)); + qemu_chr_fe_init(be, chr, &error_abort); + } + + d.chr = chr; + qemu_chr_fe_set_handlers(be, socket_can_read_hello, socket_read_hello, + NULL, NULL, &d, NULL, true); + ret = qemu_chr_write_all(chr, (uint8_t *)"hello", 5); + g_assert_cmpint(ret, ==, 5); + + ret = recvfrom(sock, buf, sizeof(buf), 0, + (struct sockaddr *)&other, &alen); + g_assert_cmpint(ret, ==, 5); + ret = sendto(sock, buf, 5, 0, (struct sockaddr *)&other, alen); + g_assert_cmpint(ret, ==, 5); + + main_loop(); + + if (!reuse_chr) { + close(sock); + qemu_chr_fe_deinit(be, true); + } + g_free(tmp); +} + +static void char_udp_test(void) +{ + char_udp_test_internal(NULL, 0); +} + + +typedef struct { + int event; + bool got_pong; + CharBackend *be; +} CharSocketTestData; + + +#define SOCKET_PING "Hello" +#define SOCKET_PONG "World" + +typedef void (*char_socket_cb)(void *opaque, QEMUChrEvent event); + +static void +char_socket_event(void *opaque, QEMUChrEvent event) +{ + CharSocketTestData *data = opaque; + data->event = event; +} + +static void +char_socket_event_with_error(void *opaque, QEMUChrEvent event) +{ + static bool first_error; + CharSocketTestData *data = opaque; + CharBackend *be = data->be; + data->event = event; + switch (event) { + case CHR_EVENT_OPENED: + if (!first_error) { + first_error = true; + qemu_chr_fe_disconnect(be); + } + return; + case CHR_EVENT_CLOSED: + return; + default: + return; + } +} + + +static void +char_socket_read(void *opaque, const uint8_t *buf, int size) +{ + CharSocketTestData *data = opaque; + g_assert_cmpint(size, ==, sizeof(SOCKET_PONG)); + g_assert(memcmp(buf, SOCKET_PONG, size) == 0); + data->got_pong = true; +} + + +static int +char_socket_can_read(void *opaque) +{ + return sizeof(SOCKET_PONG); +} + + +static char * +char_socket_addr_to_opt_str(SocketAddress *addr, bool fd_pass, + const char *reconnect, bool is_listen) +{ + if (fd_pass) { + QIOChannelSocket *ioc = qio_channel_socket_new(); + int fd; + char *optstr; + g_assert(!reconnect); + if (is_listen) { + qio_channel_socket_listen_sync(ioc, addr, 1, &error_abort); + } else { + qio_channel_socket_connect_sync(ioc, addr, &error_abort); + } + fd = ioc->fd; + ioc->fd = -1; + optstr = g_strdup_printf("socket,id=cdev0,fd=%d%s", + fd, is_listen ? ",server=on,wait=off" : ""); + object_unref(OBJECT(ioc)); + return optstr; + } else { + switch (addr->type) { + case SOCKET_ADDRESS_TYPE_INET: + return g_strdup_printf("socket,id=cdev0,host=%s,port=%s%s%s", + addr->u.inet.host, + addr->u.inet.port, + reconnect ? reconnect : "", + is_listen ? ",server=on,wait=off" : ""); + + case SOCKET_ADDRESS_TYPE_UNIX: + return g_strdup_printf("socket,id=cdev0,path=%s%s%s", + addr->u.q_unix.path, + reconnect ? reconnect : "", + is_listen ? ",server=on,wait=off" : ""); + + default: + g_assert_not_reached(); + } + } +} + + +static int +char_socket_ping_pong(QIOChannel *ioc, Error **errp) +{ + char greeting[sizeof(SOCKET_PING)]; + const char *response = SOCKET_PONG; + + int ret; + ret = qio_channel_read_all(ioc, greeting, sizeof(greeting), errp); + if (ret != 0) { + object_unref(OBJECT(ioc)); + return -1; + } + + g_assert(memcmp(greeting, SOCKET_PING, sizeof(greeting)) == 0); + + qio_channel_write_all(ioc, response, sizeof(SOCKET_PONG), errp); + object_unref(OBJECT(ioc)); + return 0; +} + + +static gpointer +char_socket_server_client_thread(gpointer data) +{ + SocketAddress *addr = data; + QIOChannelSocket *ioc = qio_channel_socket_new(); + + qio_channel_socket_connect_sync(ioc, addr, &error_abort); + + char_socket_ping_pong(QIO_CHANNEL(ioc), &error_abort); + + return NULL; +} + + +typedef struct { + SocketAddress *addr; + bool wait_connected; + bool fd_pass; +} CharSocketServerTestConfig; + + +static void char_socket_server_test(gconstpointer opaque) +{ + const CharSocketServerTestConfig *config = opaque; + Chardev *chr; + CharBackend be = {0}; + CharSocketTestData data = {0}; + QObject *qaddr; + SocketAddress *addr; + Visitor *v; + QemuThread thread; + int ret; + bool reconnected = false; + char *optstr; + QemuOpts *opts; + + g_setenv("QTEST_SILENT_ERRORS", "1", 1); + /* + * We rely on config->addr containing "wait=off", otherwise + * qemu_chr_new() will block until a client connects. We + * can't spawn our client thread though, because until + * qemu_chr_new() returns we don't know what TCP port was + * allocated by the OS + */ + optstr = char_socket_addr_to_opt_str(config->addr, + config->fd_pass, + NULL, + true); + opts = qemu_opts_parse_noisily(qemu_find_opts("chardev"), + optstr, true); + g_assert_nonnull(opts); + chr = qemu_chr_new_from_opts(opts, NULL, &error_abort); + qemu_opts_del(opts); + g_assert_nonnull(chr); + g_assert(!object_property_get_bool(OBJECT(chr), "connected", &error_abort)); + + qaddr = object_property_get_qobject(OBJECT(chr), "addr", &error_abort); + g_assert_nonnull(qaddr); + + v = qobject_input_visitor_new(qaddr); + visit_type_SocketAddress(v, "addr", &addr, &error_abort); + visit_free(v); + qobject_unref(qaddr); + + qemu_chr_fe_init(&be, chr, &error_abort); + + reconnect: + data.event = -1; + data.be = &be; + qemu_chr_fe_set_handlers(&be, NULL, NULL, + char_socket_event, NULL, + &data, NULL, true); + g_assert(data.event == -1); + + /* + * Kick off a thread to act as the "remote" client + * which just plays ping-pong with us + */ + qemu_thread_create(&thread, "client", + char_socket_server_client_thread, + addr, QEMU_THREAD_JOINABLE); + g_assert(data.event == -1); + + if (config->wait_connected) { + /* Synchronously accept a connection */ + qemu_chr_wait_connected(chr, &error_abort); + } else { + /* + * Asynchronously accept a connection when the evnt + * loop reports the listener socket as readable + */ + while (data.event == -1) { + main_loop_wait(false); + } + } + g_assert(object_property_get_bool(OBJECT(chr), "connected", &error_abort)); + g_assert(data.event == CHR_EVENT_OPENED); + data.event = -1; + + /* Send a greeting to the client */ + ret = qemu_chr_fe_write_all(&be, (const uint8_t *)SOCKET_PING, + sizeof(SOCKET_PING)); + g_assert_cmpint(ret, ==, sizeof(SOCKET_PING)); + g_assert(data.event == -1); + + /* Setup a callback to receive the reply to our greeting */ + qemu_chr_fe_set_handlers(&be, char_socket_can_read, + char_socket_read, + char_socket_event, NULL, + &data, NULL, true); + g_assert(data.event == CHR_EVENT_OPENED); + data.event = -1; + + /* Wait for the client to go away */ + while (data.event == -1) { + main_loop_wait(false); + } + g_assert(!object_property_get_bool(OBJECT(chr), "connected", &error_abort)); + g_assert(data.event == CHR_EVENT_CLOSED); + g_assert(data.got_pong); + + qemu_thread_join(&thread); + + if (!reconnected) { + reconnected = true; + goto reconnect; + } + + qapi_free_SocketAddress(addr); + object_unparent(OBJECT(chr)); + g_free(optstr); + g_unsetenv("QTEST_SILENT_ERRORS"); +} + + +static gpointer +char_socket_client_server_thread(gpointer data) +{ + QIOChannelSocket *ioc = data; + QIOChannelSocket *cioc; + +retry: + cioc = qio_channel_socket_accept(ioc, &error_abort); + g_assert_nonnull(cioc); + + if (char_socket_ping_pong(QIO_CHANNEL(cioc), NULL) != 0) { + goto retry; + } + + return NULL; +} + + +typedef struct { + SocketAddress *addr; + const char *reconnect; + bool wait_connected; + bool fd_pass; + char_socket_cb event_cb; +} CharSocketClientTestConfig; + +static void char_socket_client_dupid_test(gconstpointer opaque) +{ + const CharSocketClientTestConfig *config = opaque; + QIOChannelSocket *ioc; + char *optstr; + Chardev *chr1, *chr2; + SocketAddress *addr; + QemuOpts *opts; + Error *local_err = NULL; + + /* + * Setup a listener socket and determine get its address + * so we know the TCP port for the client later + */ + ioc = qio_channel_socket_new(); + g_assert_nonnull(ioc); + qio_channel_socket_listen_sync(ioc, config->addr, 1, &error_abort); + addr = qio_channel_socket_get_local_address(ioc, &error_abort); + g_assert_nonnull(addr); + + /* + * Populate the chardev address based on what the server + * is actually listening on + */ + optstr = char_socket_addr_to_opt_str(addr, + config->fd_pass, + config->reconnect, + false); + + opts = qemu_opts_parse_noisily(qemu_find_opts("chardev"), + optstr, true); + g_assert_nonnull(opts); + chr1 = qemu_chr_new_from_opts(opts, NULL, &error_abort); + g_assert_nonnull(chr1); + qemu_chr_wait_connected(chr1, &error_abort); + + chr2 = qemu_chr_new_from_opts(opts, NULL, &local_err); + g_assert_null(chr2); + error_free_or_abort(&local_err); + + object_unref(OBJECT(ioc)); + qemu_opts_del(opts); + object_unparent(OBJECT(chr1)); + qapi_free_SocketAddress(addr); + g_free(optstr); +} + +static void char_socket_client_test(gconstpointer opaque) +{ + const CharSocketClientTestConfig *config = opaque; + const char_socket_cb event_cb = config->event_cb; + QIOChannelSocket *ioc; + char *optstr; + Chardev *chr; + CharBackend be = {0}; + CharSocketTestData data = {0}; + SocketAddress *addr; + QemuThread thread; + int ret; + bool reconnected = false; + QemuOpts *opts; + + /* + * Setup a listener socket and determine get its address + * so we know the TCP port for the client later + */ + ioc = qio_channel_socket_new(); + g_assert_nonnull(ioc); + qio_channel_socket_listen_sync(ioc, config->addr, 1, &error_abort); + addr = qio_channel_socket_get_local_address(ioc, &error_abort); + g_assert_nonnull(addr); + + /* + * Kick off a thread to act as the "remote" client + * which just plays ping-pong with us + */ + qemu_thread_create(&thread, "client", + char_socket_client_server_thread, + ioc, QEMU_THREAD_JOINABLE); + + /* + * Populate the chardev address based on what the server + * is actually listening on + */ + optstr = char_socket_addr_to_opt_str(addr, + config->fd_pass, + config->reconnect, + false); + + opts = qemu_opts_parse_noisily(qemu_find_opts("chardev"), + optstr, true); + g_assert_nonnull(opts); + chr = qemu_chr_new_from_opts(opts, NULL, &error_abort); + qemu_opts_del(opts); + g_assert_nonnull(chr); + + if (config->reconnect) { + /* + * If reconnect is set, the connection will be + * established in a background thread and we won't + * see the "connected" status updated until we + * run the main event loop, or call qemu_chr_wait_connected + */ + g_assert(!object_property_get_bool(OBJECT(chr), "connected", + &error_abort)); + } else { + g_assert(object_property_get_bool(OBJECT(chr), "connected", + &error_abort)); + } + + qemu_chr_fe_init(&be, chr, &error_abort); + + reconnect: + data.event = -1; + data.be = &be; + qemu_chr_fe_set_handlers(&be, NULL, NULL, + event_cb, NULL, + &data, NULL, true); + if (config->reconnect) { + g_assert(data.event == -1); + } else { + g_assert(data.event == CHR_EVENT_OPENED); + } + + if (config->wait_connected) { + /* + * Synchronously wait for the connection to complete + * This should be a no-op if reconnect is not set. + */ + qemu_chr_wait_connected(chr, &error_abort); + } else { + /* + * Asynchronously wait for the connection to be reported + * as complete when the background thread reports its + * status. + * The loop will short-circuit if reconnect was set + */ + while (data.event == -1) { + main_loop_wait(false); + } + } + g_assert(data.event == CHR_EVENT_OPENED); + data.event = -1; + g_assert(object_property_get_bool(OBJECT(chr), "connected", &error_abort)); + + /* Send a greeting to the server */ + ret = qemu_chr_fe_write_all(&be, (const uint8_t *)SOCKET_PING, + sizeof(SOCKET_PING)); + g_assert_cmpint(ret, ==, sizeof(SOCKET_PING)); + g_assert(data.event == -1); + + /* Setup a callback to receive the reply to our greeting */ + qemu_chr_fe_set_handlers(&be, char_socket_can_read, + char_socket_read, + event_cb, NULL, + &data, NULL, true); + g_assert(data.event == CHR_EVENT_OPENED); + data.event = -1; + + /* Wait for the server to go away */ + while (data.event == -1) { + main_loop_wait(false); + } + g_assert(data.event == CHR_EVENT_CLOSED); + g_assert(!object_property_get_bool(OBJECT(chr), "connected", &error_abort)); + g_assert(data.got_pong); + qemu_thread_join(&thread); + + if (config->reconnect && !reconnected) { + reconnected = true; + qemu_thread_create(&thread, "client", + char_socket_client_server_thread, + ioc, QEMU_THREAD_JOINABLE); + goto reconnect; + } + + object_unref(OBJECT(ioc)); + object_unparent(OBJECT(chr)); + qapi_free_SocketAddress(addr); + g_free(optstr); +} + +static void +count_closed_event(void *opaque, QEMUChrEvent event) +{ + int *count = opaque; + if (event == CHR_EVENT_CLOSED) { + (*count)++; + } +} + +static void +char_socket_discard_read(void *opaque, const uint8_t *buf, int size) +{ +} + +static void char_socket_server_two_clients_test(gconstpointer opaque) +{ + SocketAddress *incoming_addr = (gpointer) opaque; + Chardev *chr; + CharBackend be = {0}; + QObject *qaddr; + SocketAddress *addr; + Visitor *v; + char *optstr; + QemuOpts *opts; + QIOChannelSocket *ioc1, *ioc2; + int closed = 0; + + g_setenv("QTEST_SILENT_ERRORS", "1", 1); + /* + * We rely on addr containing "wait=off", otherwise + * qemu_chr_new() will block until a client connects. We + * can't spawn our client thread though, because until + * qemu_chr_new() returns we don't know what TCP port was + * allocated by the OS + */ + optstr = char_socket_addr_to_opt_str(incoming_addr, + false, + NULL, + true); + opts = qemu_opts_parse_noisily(qemu_find_opts("chardev"), + optstr, true); + g_assert_nonnull(opts); + chr = qemu_chr_new_from_opts(opts, NULL, &error_abort); + qemu_opts_del(opts); + g_assert_nonnull(chr); + g_assert(!object_property_get_bool(OBJECT(chr), "connected", &error_abort)); + + qaddr = object_property_get_qobject(OBJECT(chr), "addr", &error_abort); + g_assert_nonnull(qaddr); + + v = qobject_input_visitor_new(qaddr); + visit_type_SocketAddress(v, "addr", &addr, &error_abort); + visit_free(v); + qobject_unref(qaddr); + + qemu_chr_fe_init(&be, chr, &error_abort); + + qemu_chr_fe_set_handlers(&be, char_socket_can_read, char_socket_discard_read, + count_closed_event, NULL, + &closed, NULL, true); + + ioc1 = qio_channel_socket_new(); + qio_channel_socket_connect_sync(ioc1, addr, &error_abort); + qemu_chr_wait_connected(chr, &error_abort); + + /* switch the chardev to another context */ + GMainContext *ctx = g_main_context_new(); + qemu_chr_fe_set_handlers(&be, char_socket_can_read, char_socket_discard_read, + count_closed_event, NULL, + &closed, ctx, true); + + /* Start a second connection while the first is still connected. + * It will be placed in the listen() backlog, and connect() will + * succeed immediately. + */ + ioc2 = qio_channel_socket_new(); + qio_channel_socket_connect_sync(ioc2, addr, &error_abort); + + object_unref(OBJECT(ioc1)); + /* The two connections should now be processed serially. */ + while (g_main_context_iteration(ctx, TRUE)) { + if (closed == 1 && ioc2) { + object_unref(OBJECT(ioc2)); + ioc2 = NULL; + } + if (closed == 2) { + break; + } + } + + qapi_free_SocketAddress(addr); + object_unparent(OBJECT(chr)); + g_main_context_unref(ctx); + g_free(optstr); + g_unsetenv("QTEST_SILENT_ERRORS"); +} + + +#if defined(HAVE_CHARDEV_SERIAL) && !defined(WIN32) +static void char_serial_test(void) +{ + QemuOpts *opts; + Chardev *chr; + + opts = qemu_opts_create(qemu_find_opts("chardev"), "serial-id", + 1, &error_abort); + qemu_opt_set(opts, "backend", "serial", &error_abort); + qemu_opt_set(opts, "path", "/dev/null", &error_abort); + + chr = qemu_chr_new_from_opts(opts, NULL, NULL); + g_assert_nonnull(chr); + /* TODO: add more tests with a pty */ + object_unparent(OBJECT(chr)); + + /* test tty alias */ + qemu_opt_set(opts, "backend", "tty", &error_abort); + chr = qemu_chr_new_from_opts(opts, NULL, &error_abort); + g_assert_nonnull(chr); + object_unparent(OBJECT(chr)); + + qemu_opts_del(opts); +} +#endif + +#ifndef _WIN32 +static void char_file_fifo_test(void) +{ + Chardev *chr; + CharBackend be; + char *tmp_path = g_dir_make_tmp("qemu-test-char.XXXXXX", NULL); + char *fifo = g_build_filename(tmp_path, "fifo", NULL); + char *out = g_build_filename(tmp_path, "out", NULL); + ChardevFile file = { .in = fifo, + .has_in = true, + .out = out }; + ChardevBackend backend = { .type = CHARDEV_BACKEND_KIND_FILE, + .u.file.data = &file }; + FeHandler fe = { 0, }; + int fd, ret; + + if (mkfifo(fifo, 0600) < 0) { + abort(); + } + + fd = open(fifo, O_RDWR); + ret = write(fd, "fifo-in", 8); + g_assert_cmpint(ret, ==, 8); + + chr = qemu_chardev_new("label-file", TYPE_CHARDEV_FILE, &backend, + NULL, &error_abort); + + qemu_chr_fe_init(&be, chr, &error_abort); + qemu_chr_fe_set_handlers(&be, + fe_can_read, + fe_read, + fe_event, + NULL, + &fe, NULL, true); + + g_assert_cmpint(fe.last_event, !=, CHR_EVENT_BREAK); + qmp_chardev_send_break("label-foo", NULL); + g_assert_cmpint(fe.last_event, !=, CHR_EVENT_BREAK); + qmp_chardev_send_break("label-file", NULL); + g_assert_cmpint(fe.last_event, ==, CHR_EVENT_BREAK); + + main_loop(); + + close(fd); + + g_assert_cmpint(fe.read_count, ==, 8); + g_assert_cmpstr(fe.read_buf, ==, "fifo-in"); + + qemu_chr_fe_deinit(&be, true); + + g_unlink(fifo); + g_free(fifo); + g_unlink(out); + g_free(out); + g_rmdir(tmp_path); + g_free(tmp_path); +} +#endif + +static void char_file_test_internal(Chardev *ext_chr, const char *filepath) +{ + char *tmp_path = g_dir_make_tmp("qemu-test-char.XXXXXX", NULL); + char *out; + Chardev *chr; + char *contents = NULL; + ChardevFile file = {}; + ChardevBackend backend = { .type = CHARDEV_BACKEND_KIND_FILE, + .u.file.data = &file }; + gsize length; + int ret; + + if (ext_chr) { + chr = ext_chr; + out = g_strdup(filepath); + file.out = out; + } else { + out = g_build_filename(tmp_path, "out", NULL); + file.out = out; + chr = qemu_chardev_new(NULL, TYPE_CHARDEV_FILE, &backend, + NULL, &error_abort); + } + ret = qemu_chr_write_all(chr, (uint8_t *)"hello!", 6); + g_assert_cmpint(ret, ==, 6); + + ret = g_file_get_contents(out, &contents, &length, NULL); + g_assert(ret == TRUE); + g_assert_cmpint(length, ==, 6); + g_assert(strncmp(contents, "hello!", 6) == 0); + + if (!ext_chr) { + object_unparent(OBJECT(chr)); + g_unlink(out); + } + g_free(contents); + g_rmdir(tmp_path); + g_free(tmp_path); + g_free(out); +} + +static void char_file_test(void) +{ + char_file_test_internal(NULL, NULL); +} + +static void char_null_test(void) +{ + Error *err = NULL; + Chardev *chr; + CharBackend be; + int ret; + + chr = qemu_chr_find("label-null"); + g_assert_null(chr); + + chr = qemu_chr_new("label-null", "null", NULL); + chr = qemu_chr_find("label-null"); + g_assert_nonnull(chr); + + g_assert(qemu_chr_has_feature(chr, + QEMU_CHAR_FEATURE_FD_PASS) == false); + g_assert(qemu_chr_has_feature(chr, + QEMU_CHAR_FEATURE_RECONNECTABLE) == false); + + /* check max avail */ + qemu_chr_fe_init(&be, chr, &error_abort); + qemu_chr_fe_init(&be, chr, &err); + error_free_or_abort(&err); + + /* deinit & reinit */ + qemu_chr_fe_deinit(&be, false); + qemu_chr_fe_init(&be, chr, &error_abort); + + qemu_chr_fe_set_open(&be, true); + + qemu_chr_fe_set_handlers(&be, + fe_can_read, + fe_read, + fe_event, + NULL, + NULL, NULL, true); + + ret = qemu_chr_fe_write(&be, (void *)"buf", 4); + g_assert_cmpint(ret, ==, 4); + + qemu_chr_fe_deinit(&be, true); +} + +static void char_invalid_test(void) +{ + Chardev *chr; + g_setenv("QTEST_SILENT_ERRORS", "1", 1); + chr = qemu_chr_new("label-invalid", "invalid", NULL); + g_assert_null(chr); + g_unsetenv("QTEST_SILENT_ERRORS"); +} + +static int chardev_change(void *opaque) +{ + return 0; +} + +static int chardev_change_denied(void *opaque) +{ + return -1; +} + +static void char_hotswap_test(void) +{ + char *chr_args; + Chardev *chr; + CharBackend be; + + gchar *tmp_path = g_dir_make_tmp("qemu-test-char.XXXXXX", NULL); + char *filename = g_build_filename(tmp_path, "file", NULL); + ChardevFile file = { .out = filename }; + ChardevBackend backend = { .type = CHARDEV_BACKEND_KIND_FILE, + .u.file.data = &file }; + ChardevReturn *ret; + + int port; + int sock = make_udp_socket(&port); + g_assert_cmpint(sock, >, 0); + + chr_args = g_strdup_printf("udp:127.0.0.1:%d", port); + + chr = qemu_chr_new("chardev", chr_args, NULL); + qemu_chr_fe_init(&be, chr, &error_abort); + + /* check that chardev operates correctly */ + char_udp_test_internal(chr, sock); + + /* set the handler that denies the hotswap */ + qemu_chr_fe_set_handlers(&be, NULL, NULL, + NULL, chardev_change_denied, NULL, NULL, true); + + /* now, change is denied and has to keep the old backend operating */ + ret = qmp_chardev_change("chardev", &backend, NULL); + g_assert(!ret); + g_assert(be.chr == chr); + + char_udp_test_internal(chr, sock); + + /* now allow the change */ + qemu_chr_fe_set_handlers(&be, NULL, NULL, + NULL, chardev_change, NULL, NULL, true); + + /* has to succeed now */ + ret = qmp_chardev_change("chardev", &backend, &error_abort); + g_assert(be.chr != chr); + + close(sock); + chr = be.chr; + + /* run the file chardev test */ + char_file_test_internal(chr, filename); + + object_unparent(OBJECT(chr)); + + qapi_free_ChardevReturn(ret); + g_unlink(filename); + g_free(filename); + g_rmdir(tmp_path); + g_free(tmp_path); + g_free(chr_args); +} + +static SocketAddress tcpaddr = { + .type = SOCKET_ADDRESS_TYPE_INET, + .u.inet.host = (char *)"127.0.0.1", + .u.inet.port = (char *)"0", +}; +#ifndef WIN32 +static SocketAddress unixaddr = { + .type = SOCKET_ADDRESS_TYPE_UNIX, + .u.q_unix.path = (char *)"test-char.sock", +}; +#endif + +int main(int argc, char **argv) +{ + bool has_ipv4, has_ipv6; + + qemu_init_main_loop(&error_abort); + socket_init(); + + g_test_init(&argc, &argv, NULL); + + if (socket_check_protocol_support(&has_ipv4, &has_ipv6) < 0) { + g_printerr("socket_check_protocol_support() failed\n"); + goto end; + } + + module_call_init(MODULE_INIT_QOM); + qemu_add_opts(&qemu_chardev_opts); + + g_test_add_func("/char/null", char_null_test); + g_test_add_func("/char/invalid", char_invalid_test); + g_test_add_func("/char/ringbuf", char_ringbuf_test); + g_test_add_func("/char/mux", char_mux_test); +#ifdef _WIN32 + g_test_add_func("/char/console/subprocess", char_console_test_subprocess); + g_test_add_func("/char/console", char_console_test); +#endif + g_test_add_func("/char/stdio/subprocess", char_stdio_test_subprocess); + g_test_add_func("/char/stdio", char_stdio_test); +#ifndef _WIN32 + g_test_add_func("/char/pipe", char_pipe_test); +#endif + g_test_add_func("/char/file", char_file_test); +#ifndef _WIN32 + g_test_add_func("/char/file-fifo", char_file_fifo_test); +#endif + +#define SOCKET_SERVER_TEST(name, addr) \ + static CharSocketServerTestConfig server1 ## name = \ + { addr, false, false }; \ + static CharSocketServerTestConfig server2 ## name = \ + { addr, true, false }; \ + static CharSocketServerTestConfig server3 ## name = \ + { addr, false, true }; \ + static CharSocketServerTestConfig server4 ## name = \ + { addr, true, true }; \ + g_test_add_data_func("/char/socket/server/mainloop/" # name, \ + &server1 ##name, char_socket_server_test); \ + g_test_add_data_func("/char/socket/server/wait-conn/" # name, \ + &server2 ##name, char_socket_server_test); \ + g_test_add_data_func("/char/socket/server/mainloop-fdpass/" # name, \ + &server3 ##name, char_socket_server_test); \ + g_test_add_data_func("/char/socket/server/wait-conn-fdpass/" # name, \ + &server4 ##name, char_socket_server_test) + +#define SOCKET_CLIENT_TEST(name, addr) \ + static CharSocketClientTestConfig client1 ## name = \ + { addr, NULL, false, false, char_socket_event }; \ + static CharSocketClientTestConfig client2 ## name = \ + { addr, NULL, true, false, char_socket_event }; \ + static CharSocketClientTestConfig client3 ## name = \ + { addr, ",reconnect=1", false, false, char_socket_event }; \ + static CharSocketClientTestConfig client4 ## name = \ + { addr, ",reconnect=1", true, false, char_socket_event }; \ + static CharSocketClientTestConfig client5 ## name = \ + { addr, NULL, false, true, char_socket_event }; \ + static CharSocketClientTestConfig client6 ## name = \ + { addr, NULL, true, true, char_socket_event }; \ + static CharSocketClientTestConfig client7 ## name = \ + { addr, ",reconnect=1", true, false, \ + char_socket_event_with_error }; \ + static CharSocketClientTestConfig client8 ## name = \ + { addr, ",reconnect=1", false, false, char_socket_event }; \ + g_test_add_data_func("/char/socket/client/mainloop/" # name, \ + &client1 ##name, char_socket_client_test); \ + g_test_add_data_func("/char/socket/client/wait-conn/" # name, \ + &client2 ##name, char_socket_client_test); \ + g_test_add_data_func("/char/socket/client/mainloop-reconnect/" # name, \ + &client3 ##name, char_socket_client_test); \ + g_test_add_data_func("/char/socket/client/wait-conn-reconnect/" # name, \ + &client4 ##name, char_socket_client_test); \ + g_test_add_data_func("/char/socket/client/mainloop-fdpass/" # name, \ + &client5 ##name, char_socket_client_test); \ + g_test_add_data_func("/char/socket/client/wait-conn-fdpass/" # name, \ + &client6 ##name, char_socket_client_test); \ + g_test_add_data_func("/char/socket/client/reconnect-error/" # name, \ + &client7 ##name, char_socket_client_test); \ + g_test_add_data_func("/char/socket/client/dupid-reconnect/" # name, \ + &client8 ##name, char_socket_client_dupid_test) + + if (has_ipv4) { + SOCKET_SERVER_TEST(tcp, &tcpaddr); + SOCKET_CLIENT_TEST(tcp, &tcpaddr); + g_test_add_data_func("/char/socket/server/two-clients/tcp", &tcpaddr, + char_socket_server_two_clients_test); + } +#ifndef WIN32 + SOCKET_SERVER_TEST(unix, &unixaddr); + SOCKET_CLIENT_TEST(unix, &unixaddr); + g_test_add_data_func("/char/socket/server/two-clients/unix", &unixaddr, + char_socket_server_two_clients_test); +#endif + + g_test_add_func("/char/udp", char_udp_test); +#if defined(HAVE_CHARDEV_SERIAL) && !defined(WIN32) + g_test_add_func("/char/serial", char_serial_test); +#endif + g_test_add_func("/char/hotswap", char_hotswap_test); + g_test_add_func("/char/websocket", char_websock_test); + +end: + return g_test_run(); +} diff --git a/tests/unit/test-clone-visitor.c b/tests/unit/test-clone-visitor.c new file mode 100644 index 0000000000..4944b3d857 --- /dev/null +++ b/tests/unit/test-clone-visitor.c @@ -0,0 +1,199 @@ +/* + * QAPI Clone Visitor unit-tests. + * + * Copyright (C) 2016 Red Hat Inc. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" + +#include "qemu-common.h" +#include "qapi/clone-visitor.h" +#include "test-qapi-visit.h" + +static void test_clone_struct(void) +{ + UserDefOne *src, *dst; + + src = g_new0(UserDefOne, 1); + src->integer = 42; + src->string = g_strdup("Hello"); + src->has_enum1 = false; + src->enum1 = ENUM_ONE_VALUE2; + + dst = QAPI_CLONE(UserDefOne, src); + g_assert(dst); + g_assert_cmpint(dst->integer, ==, 42); + g_assert(dst->string != src->string); + g_assert_cmpstr(dst->string, ==, "Hello"); + g_assert_cmpint(dst->has_enum1, ==, false); + /* Our implementation does this, but it is not required: + g_assert_cmpint(dst->enum1, ==, ENUM_ONE_VALUE2); + */ + + qapi_free_UserDefOne(src); + qapi_free_UserDefOne(dst); +} + +static void test_clone_alternate(void) +{ + AltEnumBool *b_src, *s_src, *b_dst, *s_dst; + + b_src = g_new0(AltEnumBool, 1); + b_src->type = QTYPE_QBOOL; + b_src->u.b = true; + s_src = g_new0(AltEnumBool, 1); + s_src->type = QTYPE_QSTRING; + s_src->u.e = ENUM_ONE_VALUE1; + + b_dst = QAPI_CLONE(AltEnumBool, b_src); + g_assert(b_dst); + g_assert_cmpint(b_dst->type, ==, b_src->type); + g_assert_cmpint(b_dst->u.b, ==, b_src->u.b); + s_dst = QAPI_CLONE(AltEnumBool, s_src); + g_assert(s_dst); + g_assert_cmpint(s_dst->type, ==, s_src->type); + g_assert_cmpint(s_dst->u.e, ==, s_src->u.e); + + qapi_free_AltEnumBool(b_src); + qapi_free_AltEnumBool(s_src); + qapi_free_AltEnumBool(b_dst); + qapi_free_AltEnumBool(s_dst); +} + +static void test_clone_list_union(void) +{ + uint8List *src = NULL, *dst; + uint8List *tmp = NULL; + int i; + + /* Build list in reverse */ + for (i = 10; i; i--) { + QAPI_LIST_PREPEND(src, i); + } + + dst = QAPI_CLONE(uint8List, src); + for (tmp = dst, i = 1; i <= 10; i++) { + g_assert(tmp); + g_assert_cmpint(tmp->value, ==, i); + tmp = tmp->next; + } + g_assert(!tmp); + + qapi_free_uint8List(src); + qapi_free_uint8List(dst); +} + +static void test_clone_empty(void) +{ + Empty2 *src, *dst; + + src = g_new0(Empty2, 1); + dst = QAPI_CLONE(Empty2, src); + g_assert(dst); + qapi_free_Empty2(src); + qapi_free_Empty2(dst); +} + +static void test_clone_complex1(void) +{ + UserDefListUnion *src, *dst; + + src = g_new0(UserDefListUnion, 1); + src->type = USER_DEF_LIST_UNION_KIND_STRING; + + dst = QAPI_CLONE(UserDefListUnion, src); + g_assert(dst); + g_assert_cmpint(dst->type, ==, src->type); + g_assert(!dst->u.string.data); + + qapi_free_UserDefListUnion(src); + qapi_free_UserDefListUnion(dst); +} + +static void test_clone_complex2(void) +{ + WrapAlternate *src, *dst; + + src = g_new0(WrapAlternate, 1); + src->alt = g_new(UserDefAlternate, 1); + src->alt->type = QTYPE_QDICT; + src->alt->u.udfu.integer = 42; + /* Clone intentionally converts NULL into "" for strings */ + src->alt->u.udfu.string = NULL; + src->alt->u.udfu.enum1 = ENUM_ONE_VALUE3; + src->alt->u.udfu.u.value3.intb = 99; + src->alt->u.udfu.u.value3.has_a_b = true; + src->alt->u.udfu.u.value3.a_b = true; + + dst = QAPI_CLONE(WrapAlternate, src); + g_assert(dst); + g_assert(dst->alt); + g_assert_cmpint(dst->alt->type, ==, QTYPE_QDICT); + g_assert_cmpint(dst->alt->u.udfu.integer, ==, 42); + g_assert_cmpstr(dst->alt->u.udfu.string, ==, ""); + g_assert_cmpint(dst->alt->u.udfu.enum1, ==, ENUM_ONE_VALUE3); + g_assert_cmpint(dst->alt->u.udfu.u.value3.intb, ==, 99); + g_assert_cmpint(dst->alt->u.udfu.u.value3.has_a_b, ==, true); + g_assert_cmpint(dst->alt->u.udfu.u.value3.a_b, ==, true); + + qapi_free_WrapAlternate(src); + qapi_free_WrapAlternate(dst); +} + +static void test_clone_complex3(void) +{ + __org_qemu_x_Struct2 *src, *dst; + __org_qemu_x_Union1List *tmp; + + src = g_new0(__org_qemu_x_Struct2, 1); + tmp = src->array = g_new0(__org_qemu_x_Union1List, 1); + tmp->value = g_new0(__org_qemu_x_Union1, 1); + tmp->value->type = ORG_QEMU_X_UNION1_KIND___ORG_QEMU_X_BRANCH; + tmp->value->u.__org_qemu_x_branch.data = g_strdup("one"); + tmp = tmp->next = g_new0(__org_qemu_x_Union1List, 1); + tmp->value = g_new0(__org_qemu_x_Union1, 1); + tmp->value->type = ORG_QEMU_X_UNION1_KIND___ORG_QEMU_X_BRANCH; + tmp->value->u.__org_qemu_x_branch.data = g_strdup("two"); + tmp = tmp->next = g_new0(__org_qemu_x_Union1List, 1); + tmp->value = g_new0(__org_qemu_x_Union1, 1); + tmp->value->type = ORG_QEMU_X_UNION1_KIND___ORG_QEMU_X_BRANCH; + tmp->value->u.__org_qemu_x_branch.data = g_strdup("three"); + + dst = QAPI_CLONE(__org_qemu_x_Struct2, src); + g_assert(dst); + tmp = dst->array; + g_assert(tmp); + g_assert(tmp->value); + g_assert_cmpstr(tmp->value->u.__org_qemu_x_branch.data, ==, "one"); + tmp = tmp->next; + g_assert(tmp); + g_assert(tmp->value); + g_assert_cmpstr(tmp->value->u.__org_qemu_x_branch.data, ==, "two"); + tmp = tmp->next; + g_assert(tmp); + g_assert(tmp->value); + g_assert_cmpstr(tmp->value->u.__org_qemu_x_branch.data, ==, "three"); + tmp = tmp->next; + g_assert(!tmp); + + qapi_free___org_qemu_x_Struct2(src); + qapi_free___org_qemu_x_Struct2(dst); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/visitor/clone/struct", test_clone_struct); + g_test_add_func("/visitor/clone/alternate", test_clone_alternate); + g_test_add_func("/visitor/clone/list_union", test_clone_list_union); + g_test_add_func("/visitor/clone/empty", test_clone_empty); + g_test_add_func("/visitor/clone/complex1", test_clone_complex1); + g_test_add_func("/visitor/clone/complex2", test_clone_complex2); + g_test_add_func("/visitor/clone/complex3", test_clone_complex3); + + return g_test_run(); +} diff --git a/tests/unit/test-coroutine.c b/tests/unit/test-coroutine.c new file mode 100644 index 0000000000..e946d93a65 --- /dev/null +++ b/tests/unit/test-coroutine.c @@ -0,0 +1,512 @@ +/* + * Coroutine tests + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Stefan Hajnoczi + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "qemu/coroutine.h" +#include "qemu/coroutine_int.h" +#include "qemu/lockable.h" + +/* + * Check that qemu_in_coroutine() works + */ + +static void coroutine_fn verify_in_coroutine(void *opaque) +{ + g_assert(qemu_in_coroutine()); +} + +static void test_in_coroutine(void) +{ + Coroutine *coroutine; + + g_assert(!qemu_in_coroutine()); + + coroutine = qemu_coroutine_create(verify_in_coroutine, NULL); + qemu_coroutine_enter(coroutine); +} + +/* + * Check that qemu_coroutine_self() works + */ + +static void coroutine_fn verify_self(void *opaque) +{ + Coroutine **p_co = opaque; + g_assert(qemu_coroutine_self() == *p_co); +} + +static void test_self(void) +{ + Coroutine *coroutine; + + coroutine = qemu_coroutine_create(verify_self, &coroutine); + qemu_coroutine_enter(coroutine); +} + +/* + * Check that qemu_coroutine_entered() works + */ + +static void coroutine_fn verify_entered_step_2(void *opaque) +{ + Coroutine *caller = (Coroutine *)opaque; + + g_assert(qemu_coroutine_entered(caller)); + g_assert(qemu_coroutine_entered(qemu_coroutine_self())); + qemu_coroutine_yield(); + + /* Once more to check it still works after yielding */ + g_assert(qemu_coroutine_entered(caller)); + g_assert(qemu_coroutine_entered(qemu_coroutine_self())); +} + +static void coroutine_fn verify_entered_step_1(void *opaque) +{ + Coroutine *self = qemu_coroutine_self(); + Coroutine *coroutine; + + g_assert(qemu_coroutine_entered(self)); + + coroutine = qemu_coroutine_create(verify_entered_step_2, self); + g_assert(!qemu_coroutine_entered(coroutine)); + qemu_coroutine_enter(coroutine); + g_assert(!qemu_coroutine_entered(coroutine)); + qemu_coroutine_enter(coroutine); +} + +static void test_entered(void) +{ + Coroutine *coroutine; + + coroutine = qemu_coroutine_create(verify_entered_step_1, NULL); + g_assert(!qemu_coroutine_entered(coroutine)); + qemu_coroutine_enter(coroutine); +} + +/* + * Check that coroutines may nest multiple levels + */ + +typedef struct { + unsigned int n_enter; /* num coroutines entered */ + unsigned int n_return; /* num coroutines returned */ + unsigned int max; /* maximum level of nesting */ +} NestData; + +static void coroutine_fn nest(void *opaque) +{ + NestData *nd = opaque; + + nd->n_enter++; + + if (nd->n_enter < nd->max) { + Coroutine *child; + + child = qemu_coroutine_create(nest, nd); + qemu_coroutine_enter(child); + } + + nd->n_return++; +} + +static void test_nesting(void) +{ + Coroutine *root; + NestData nd = { + .n_enter = 0, + .n_return = 0, + .max = 128, + }; + + root = qemu_coroutine_create(nest, &nd); + qemu_coroutine_enter(root); + + /* Must enter and return from max nesting level */ + g_assert_cmpint(nd.n_enter, ==, nd.max); + g_assert_cmpint(nd.n_return, ==, nd.max); +} + +/* + * Check that yield/enter transfer control correctly + */ + +static void coroutine_fn yield_5_times(void *opaque) +{ + bool *done = opaque; + int i; + + for (i = 0; i < 5; i++) { + qemu_coroutine_yield(); + } + *done = true; +} + +static void test_yield(void) +{ + Coroutine *coroutine; + bool done = false; + int i = -1; /* one extra time to return from coroutine */ + + coroutine = qemu_coroutine_create(yield_5_times, &done); + while (!done) { + qemu_coroutine_enter(coroutine); + i++; + } + g_assert_cmpint(i, ==, 5); /* coroutine must yield 5 times */ +} + +static void coroutine_fn c2_fn(void *opaque) +{ + qemu_coroutine_yield(); +} + +static void coroutine_fn c1_fn(void *opaque) +{ + Coroutine *c2 = opaque; + qemu_coroutine_enter(c2); +} + +static void test_no_dangling_access(void) +{ + Coroutine *c1; + Coroutine *c2; + Coroutine tmp; + + c2 = qemu_coroutine_create(c2_fn, NULL); + c1 = qemu_coroutine_create(c1_fn, c2); + + qemu_coroutine_enter(c1); + + /* c1 shouldn't be used any more now; make sure we segfault if it is */ + tmp = *c1; + memset(c1, 0xff, sizeof(Coroutine)); + qemu_coroutine_enter(c2); + + /* Must restore the coroutine now to avoid corrupted pool */ + *c1 = tmp; +} + +static bool locked; +static int done; + +static void coroutine_fn mutex_fn(void *opaque) +{ + CoMutex *m = opaque; + qemu_co_mutex_lock(m); + assert(!locked); + locked = true; + qemu_coroutine_yield(); + locked = false; + qemu_co_mutex_unlock(m); + done++; +} + +static void coroutine_fn lockable_fn(void *opaque) +{ + QemuLockable *x = opaque; + qemu_lockable_lock(x); + assert(!locked); + locked = true; + qemu_coroutine_yield(); + locked = false; + qemu_lockable_unlock(x); + done++; +} + +static void do_test_co_mutex(CoroutineEntry *entry, void *opaque) +{ + Coroutine *c1 = qemu_coroutine_create(entry, opaque); + Coroutine *c2 = qemu_coroutine_create(entry, opaque); + + done = 0; + qemu_coroutine_enter(c1); + g_assert(locked); + qemu_coroutine_enter(c2); + + /* Unlock queues c2. It is then started automatically when c1 yields or + * terminates. + */ + qemu_coroutine_enter(c1); + g_assert_cmpint(done, ==, 1); + g_assert(locked); + + qemu_coroutine_enter(c2); + g_assert_cmpint(done, ==, 2); + g_assert(!locked); +} + +static void test_co_mutex(void) +{ + CoMutex m; + + qemu_co_mutex_init(&m); + do_test_co_mutex(mutex_fn, &m); +} + +static void test_co_mutex_lockable(void) +{ + CoMutex m; + CoMutex *null_pointer = NULL; + + qemu_co_mutex_init(&m); + do_test_co_mutex(lockable_fn, QEMU_MAKE_LOCKABLE(&m)); + + g_assert(QEMU_MAKE_LOCKABLE(null_pointer) == NULL); +} + +/* + * Check that creation, enter, and return work + */ + +static void coroutine_fn set_and_exit(void *opaque) +{ + bool *done = opaque; + + *done = true; +} + +static void test_lifecycle(void) +{ + Coroutine *coroutine; + bool done = false; + + /* Create, enter, and return from coroutine */ + coroutine = qemu_coroutine_create(set_and_exit, &done); + qemu_coroutine_enter(coroutine); + g_assert(done); /* expect done to be true (first time) */ + + /* Repeat to check that no state affects this test */ + done = false; + coroutine = qemu_coroutine_create(set_and_exit, &done); + qemu_coroutine_enter(coroutine); + g_assert(done); /* expect done to be true (second time) */ +} + + +#define RECORD_SIZE 10 /* Leave some room for expansion */ +struct coroutine_position { + int func; + int state; +}; +static struct coroutine_position records[RECORD_SIZE]; +static unsigned record_pos; + +static void record_push(int func, int state) +{ + struct coroutine_position *cp = &records[record_pos++]; + g_assert_cmpint(record_pos, <, RECORD_SIZE); + cp->func = func; + cp->state = state; +} + +static void coroutine_fn co_order_test(void *opaque) +{ + record_push(2, 1); + g_assert(qemu_in_coroutine()); + qemu_coroutine_yield(); + record_push(2, 2); + g_assert(qemu_in_coroutine()); +} + +static void do_order_test(void) +{ + Coroutine *co; + + co = qemu_coroutine_create(co_order_test, NULL); + record_push(1, 1); + qemu_coroutine_enter(co); + record_push(1, 2); + g_assert(!qemu_in_coroutine()); + qemu_coroutine_enter(co); + record_push(1, 3); + g_assert(!qemu_in_coroutine()); +} + +static void test_order(void) +{ + int i; + const struct coroutine_position expected_pos[] = { + {1, 1,}, {2, 1}, {1, 2}, {2, 2}, {1, 3} + }; + do_order_test(); + g_assert_cmpint(record_pos, ==, 5); + for (i = 0; i < record_pos; i++) { + g_assert_cmpint(records[i].func , ==, expected_pos[i].func ); + g_assert_cmpint(records[i].state, ==, expected_pos[i].state); + } +} +/* + * Lifecycle benchmark + */ + +static void coroutine_fn empty_coroutine(void *opaque) +{ + /* Do nothing */ +} + +static void perf_lifecycle(void) +{ + Coroutine *coroutine; + unsigned int i, max; + double duration; + + max = 1000000; + + g_test_timer_start(); + for (i = 0; i < max; i++) { + coroutine = qemu_coroutine_create(empty_coroutine, NULL); + qemu_coroutine_enter(coroutine); + } + duration = g_test_timer_elapsed(); + + g_test_message("Lifecycle %u iterations: %f s", max, duration); +} + +static void perf_nesting(void) +{ + unsigned int i, maxcycles, maxnesting; + double duration; + + maxcycles = 10000; + maxnesting = 1000; + Coroutine *root; + + g_test_timer_start(); + for (i = 0; i < maxcycles; i++) { + NestData nd = { + .n_enter = 0, + .n_return = 0, + .max = maxnesting, + }; + root = qemu_coroutine_create(nest, &nd); + qemu_coroutine_enter(root); + } + duration = g_test_timer_elapsed(); + + g_test_message("Nesting %u iterations of %u depth each: %f s", + maxcycles, maxnesting, duration); +} + +/* + * Yield benchmark + */ + +static void coroutine_fn yield_loop(void *opaque) +{ + unsigned int *counter = opaque; + + while ((*counter) > 0) { + (*counter)--; + qemu_coroutine_yield(); + } +} + +static void perf_yield(void) +{ + unsigned int i, maxcycles; + double duration; + + maxcycles = 100000000; + i = maxcycles; + Coroutine *coroutine = qemu_coroutine_create(yield_loop, &i); + + g_test_timer_start(); + while (i > 0) { + qemu_coroutine_enter(coroutine); + } + duration = g_test_timer_elapsed(); + + g_test_message("Yield %u iterations: %f s", maxcycles, duration); +} + +static __attribute__((noinline)) void dummy(unsigned *i) +{ + (*i)--; +} + +static void perf_baseline(void) +{ + unsigned int i, maxcycles; + double duration; + + maxcycles = 100000000; + i = maxcycles; + + g_test_timer_start(); + while (i > 0) { + dummy(&i); + } + duration = g_test_timer_elapsed(); + + g_test_message("Function call %u iterations: %f s", maxcycles, duration); +} + +static __attribute__((noinline)) void perf_cost_func(void *opaque) +{ + qemu_coroutine_yield(); +} + +static void perf_cost(void) +{ + const unsigned long maxcycles = 40000000; + unsigned long i = 0; + double duration; + unsigned long ops; + Coroutine *co; + + g_test_timer_start(); + while (i++ < maxcycles) { + co = qemu_coroutine_create(perf_cost_func, &i); + qemu_coroutine_enter(co); + qemu_coroutine_enter(co); + } + duration = g_test_timer_elapsed(); + ops = (long)(maxcycles / (duration * 1000)); + + g_test_message("Run operation %lu iterations %f s, %luK operations/s, " + "%luns per coroutine", + maxcycles, + duration, ops, + (unsigned long)(1000000000.0 * duration / maxcycles)); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + /* This test assumes there is a freelist and marks freed coroutine memory + * with a sentinel value. If there is no freelist this would legitimately + * crash, so skip it. + */ + if (CONFIG_COROUTINE_POOL) { + g_test_add_func("/basic/no-dangling-access", test_no_dangling_access); + } + + g_test_add_func("/basic/lifecycle", test_lifecycle); + g_test_add_func("/basic/yield", test_yield); + g_test_add_func("/basic/nesting", test_nesting); + g_test_add_func("/basic/self", test_self); + g_test_add_func("/basic/entered", test_entered); + g_test_add_func("/basic/in_coroutine", test_in_coroutine); + g_test_add_func("/basic/order", test_order); + g_test_add_func("/locking/co-mutex", test_co_mutex); + g_test_add_func("/locking/co-mutex/lockable", test_co_mutex_lockable); + if (g_test_perf()) { + g_test_add_func("/perf/lifecycle", perf_lifecycle); + g_test_add_func("/perf/nesting", perf_nesting); + g_test_add_func("/perf/yield", perf_yield); + g_test_add_func("/perf/function-call", perf_baseline); + g_test_add_func("/perf/cost", perf_cost); + } + return g_test_run(); +} diff --git a/tests/unit/test-crypto-afsplit.c b/tests/unit/test-crypto-afsplit.c new file mode 100644 index 0000000000..00a7c180fd --- /dev/null +++ b/tests/unit/test-crypto-afsplit.c @@ -0,0 +1,194 @@ +/* + * QEMU Crypto anti-forensic splitter + * + * Copyright (c) 2015-2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + * + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "crypto/init.h" +#include "crypto/afsplit.h" + +typedef struct QCryptoAFSplitTestData QCryptoAFSplitTestData; +struct QCryptoAFSplitTestData { + const char *path; + QCryptoHashAlgorithm hash; + uint32_t stripes; + size_t blocklen; + const uint8_t *key; + const uint8_t *splitkey; +}; + +static QCryptoAFSplitTestData test_data[] = { + { + .path = "/crypto/afsplit/sha256/5", + .hash = QCRYPTO_HASH_ALG_SHA256, + .stripes = 5, + .blocklen = 32, + .key = (const uint8_t *) + "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7" + "\xa8\xa9\xaa\xab\xac\xad\xae\xaf", + .splitkey = (const uint8_t *) + "\xfd\xd2\x73\xb1\x7d\x99\x93\x34" + "\x70\xde\xfa\x07\xc5\xac\x58\xd2" + "\x30\x67\x2f\x1a\x35\x43\x60\x7d" + "\x77\x02\xdb\x62\x3c\xcb\x2c\x33" + "\x48\x08\xb6\xf1\x7c\xa3\x20\xa0" + "\xad\x2d\x4c\xf3\xcd\x18\x6f\x53" + "\xf9\xe8\xe7\x59\x27\x3c\xa9\x54" + "\x61\x87\xb3\xaf\xf6\xf7\x7e\x64" + "\x86\xaa\x89\x7f\x1f\x9f\xdb\x86" + "\xf4\xa2\x16\xff\xa3\x4f\x8c\xa1" + "\x59\xc4\x23\x34\x28\xc4\x77\x71" + "\x83\xd4\xcd\x8e\x89\x1b\xc7\xc5" + "\xae\x4d\xa9\xcd\xc9\x72\x85\x70" + "\x13\x68\x52\x83\xfc\xb8\x11\x72" + "\xba\x3d\xc6\x4a\x28\xfa\xe2\x86" + "\x7b\x27\xab\x58\xe1\xa4\xca\xf6" + "\x9e\xbc\xfe\x0c\x92\x79\xb3\xec" + "\x1c\x5f\x79\x3b\x0d\x1e\xaa\x1a" + "\x77\x0f\x70\x19\x4b\xc8\x80\xee" + "\x27\x7c\x6e\x4a\x91\x96\x5c\xf4" + }, + { + .path = "/crypto/afsplit/sha256/5000", + .hash = QCRYPTO_HASH_ALG_SHA256, + .stripes = 5000, + .blocklen = 16, + .key = (const uint8_t *) + "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + }, + { + .path = "/crypto/afsplit/sha1/1000", + .hash = QCRYPTO_HASH_ALG_SHA1, + .stripes = 1000, + .blocklen = 32, + .key = (const uint8_t *) + "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7" + "\xa8\xa9\xaa\xab\xac\xad\xae\xaf", + }, + { + .path = "/crypto/afsplit/sha256/big", + .hash = QCRYPTO_HASH_ALG_SHA256, + .stripes = 1000, + .blocklen = 64, + .key = (const uint8_t *) + "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + }, +}; + + +static inline char hex(int i) +{ + if (i < 10) { + return '0' + i; + } + return 'a' + (i - 10); +} + +static char *hex_string(const uint8_t *bytes, + size_t len) +{ + char *hexstr = g_new0(char, len * 2 + 1); + size_t i; + + for (i = 0; i < len; i++) { + hexstr[i * 2] = hex((bytes[i] >> 4) & 0xf); + hexstr[i * 2 + 1] = hex(bytes[i] & 0xf); + } + hexstr[len * 2] = '\0'; + + return hexstr; +} + +static void test_afsplit(const void *opaque) +{ + const QCryptoAFSplitTestData *data = opaque; + size_t splitlen = data->blocklen * data->stripes; + uint8_t *splitkey = g_new0(uint8_t, splitlen); + uint8_t *key = g_new0(uint8_t, data->blocklen); + gchar *expect, *actual; + + /* First time we round-trip the key */ + qcrypto_afsplit_encode(data->hash, + data->blocklen, data->stripes, + data->key, splitkey, + &error_abort); + + qcrypto_afsplit_decode(data->hash, + data->blocklen, data->stripes, + splitkey, key, + &error_abort); + + expect = hex_string(data->key, data->blocklen); + actual = hex_string(key, data->blocklen); + + g_assert_cmpstr(actual, ==, expect); + + g_free(actual); + g_free(expect); + + /* Second time we merely try decoding a previous split */ + if (data->splitkey) { + memset(key, 0, data->blocklen); + + qcrypto_afsplit_decode(data->hash, + data->blocklen, data->stripes, + data->splitkey, key, + &error_abort); + + expect = hex_string(data->key, data->blocklen); + actual = hex_string(key, data->blocklen); + + g_assert_cmpstr(actual, ==, expect); + + g_free(actual); + g_free(expect); + } + + g_free(key); + g_free(splitkey); +} + +int main(int argc, char **argv) +{ + size_t i; + + g_test_init(&argc, &argv, NULL); + + g_assert(qcrypto_init(NULL) == 0); + + for (i = 0; i < G_N_ELEMENTS(test_data); i++) { + if (!qcrypto_hash_supports(test_data[i].hash)) { + continue; + } + g_test_add_data_func(test_data[i].path, &test_data[i], test_afsplit); + } + return g_test_run(); +} diff --git a/tests/unit/test-crypto-block.c b/tests/unit/test-crypto-block.c new file mode 100644 index 0000000000..3b1f0d509f --- /dev/null +++ b/tests/unit/test-crypto-block.c @@ -0,0 +1,368 @@ +/* + * QEMU Crypto block encryption + * + * Copyright (c) 2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + * + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "crypto/init.h" +#include "crypto/block.h" +#include "qemu/buffer.h" +#include "qemu/module.h" +#include "crypto/secret.h" +#ifndef _WIN32 +#include +#endif + +#if (defined(_WIN32) || defined RUSAGE_THREAD) && \ + (defined(CONFIG_NETTLE) || defined(CONFIG_GCRYPT)) +#define TEST_LUKS +#else +#undef TEST_LUKS +#endif + +static QCryptoBlockCreateOptions qcow_create_opts = { + .format = Q_CRYPTO_BLOCK_FORMAT_QCOW, + .u.qcow = { + .has_key_secret = true, + .key_secret = (char *)"sec0", + }, +}; + +static QCryptoBlockOpenOptions qcow_open_opts = { + .format = Q_CRYPTO_BLOCK_FORMAT_QCOW, + .u.qcow = { + .has_key_secret = true, + .key_secret = (char *)"sec0", + }, +}; + + +#ifdef TEST_LUKS +static QCryptoBlockOpenOptions luks_open_opts = { + .format = Q_CRYPTO_BLOCK_FORMAT_LUKS, + .u.luks = { + .has_key_secret = true, + .key_secret = (char *)"sec0", + }, +}; + + +/* Creation with all default values */ +static QCryptoBlockCreateOptions luks_create_opts_default = { + .format = Q_CRYPTO_BLOCK_FORMAT_LUKS, + .u.luks = { + .has_key_secret = true, + .key_secret = (char *)"sec0", + }, +}; + + +/* ...and with explicit values */ +static QCryptoBlockCreateOptions luks_create_opts_aes256_cbc_plain64 = { + .format = Q_CRYPTO_BLOCK_FORMAT_LUKS, + .u.luks = { + .has_key_secret = true, + .key_secret = (char *)"sec0", + .has_cipher_alg = true, + .cipher_alg = QCRYPTO_CIPHER_ALG_AES_256, + .has_cipher_mode = true, + .cipher_mode = QCRYPTO_CIPHER_MODE_CBC, + .has_ivgen_alg = true, + .ivgen_alg = QCRYPTO_IVGEN_ALG_PLAIN64, + }, +}; + + +static QCryptoBlockCreateOptions luks_create_opts_aes256_cbc_essiv = { + .format = Q_CRYPTO_BLOCK_FORMAT_LUKS, + .u.luks = { + .has_key_secret = true, + .key_secret = (char *)"sec0", + .has_cipher_alg = true, + .cipher_alg = QCRYPTO_CIPHER_ALG_AES_256, + .has_cipher_mode = true, + .cipher_mode = QCRYPTO_CIPHER_MODE_CBC, + .has_ivgen_alg = true, + .ivgen_alg = QCRYPTO_IVGEN_ALG_ESSIV, + .has_ivgen_hash_alg = true, + .ivgen_hash_alg = QCRYPTO_HASH_ALG_SHA256, + .has_hash_alg = true, + .hash_alg = QCRYPTO_HASH_ALG_SHA1, + }, +}; +#endif /* TEST_LUKS */ + + +static struct QCryptoBlockTestData { + const char *path; + QCryptoBlockCreateOptions *create_opts; + QCryptoBlockOpenOptions *open_opts; + + bool expect_header; + + QCryptoCipherAlgorithm cipher_alg; + QCryptoCipherMode cipher_mode; + QCryptoHashAlgorithm hash_alg; + + QCryptoIVGenAlgorithm ivgen_alg; + QCryptoHashAlgorithm ivgen_hash; + + bool slow; +} test_data[] = { + { + .path = "/crypto/block/qcow", + .create_opts = &qcow_create_opts, + .open_opts = &qcow_open_opts, + + .expect_header = false, + + .cipher_alg = QCRYPTO_CIPHER_ALG_AES_128, + .cipher_mode = QCRYPTO_CIPHER_MODE_CBC, + + .ivgen_alg = QCRYPTO_IVGEN_ALG_PLAIN64, + }, +#ifdef TEST_LUKS + { + .path = "/crypto/block/luks/default", + .create_opts = &luks_create_opts_default, + .open_opts = &luks_open_opts, + + .expect_header = true, + + .cipher_alg = QCRYPTO_CIPHER_ALG_AES_256, + .cipher_mode = QCRYPTO_CIPHER_MODE_XTS, + .hash_alg = QCRYPTO_HASH_ALG_SHA256, + + .ivgen_alg = QCRYPTO_IVGEN_ALG_PLAIN64, + + .slow = true, + }, + { + .path = "/crypto/block/luks/aes-256-cbc-plain64", + .create_opts = &luks_create_opts_aes256_cbc_plain64, + .open_opts = &luks_open_opts, + + .expect_header = true, + + .cipher_alg = QCRYPTO_CIPHER_ALG_AES_256, + .cipher_mode = QCRYPTO_CIPHER_MODE_CBC, + .hash_alg = QCRYPTO_HASH_ALG_SHA256, + + .ivgen_alg = QCRYPTO_IVGEN_ALG_PLAIN64, + + .slow = true, + }, + { + .path = "/crypto/block/luks/aes-256-cbc-essiv", + .create_opts = &luks_create_opts_aes256_cbc_essiv, + .open_opts = &luks_open_opts, + + .expect_header = true, + + .cipher_alg = QCRYPTO_CIPHER_ALG_AES_256, + .cipher_mode = QCRYPTO_CIPHER_MODE_CBC, + .hash_alg = QCRYPTO_HASH_ALG_SHA1, + + .ivgen_alg = QCRYPTO_IVGEN_ALG_ESSIV, + .ivgen_hash = QCRYPTO_HASH_ALG_SHA256, + + .slow = true, + }, +#endif +}; + + +static ssize_t test_block_read_func(QCryptoBlock *block, + size_t offset, + uint8_t *buf, + size_t buflen, + void *opaque, + Error **errp) +{ + Buffer *header = opaque; + + g_assert_cmpint(offset + buflen, <=, header->capacity); + + memcpy(buf, header->buffer + offset, buflen); + + return buflen; +} + + +static ssize_t test_block_init_func(QCryptoBlock *block, + size_t headerlen, + void *opaque, + Error **errp) +{ + Buffer *header = opaque; + + g_assert_cmpint(header->capacity, ==, 0); + + buffer_reserve(header, headerlen); + + return headerlen; +} + + +static ssize_t test_block_write_func(QCryptoBlock *block, + size_t offset, + const uint8_t *buf, + size_t buflen, + void *opaque, + Error **errp) +{ + Buffer *header = opaque; + + g_assert_cmpint(buflen + offset, <=, header->capacity); + + memcpy(header->buffer + offset, buf, buflen); + header->offset = offset + buflen; + + return buflen; +} + + +static Object *test_block_secret(void) +{ + return object_new_with_props( + TYPE_QCRYPTO_SECRET, + object_get_objects_root(), + "sec0", + &error_abort, + "data", "123456", + NULL); +} + +static void test_block_assert_setup(const struct QCryptoBlockTestData *data, + QCryptoBlock *blk) +{ + QCryptoIVGen *ivgen; + QCryptoCipher *cipher; + + ivgen = qcrypto_block_get_ivgen(blk); + cipher = qcrypto_block_get_cipher(blk); + + g_assert(ivgen); + g_assert(cipher); + + g_assert_cmpint(data->cipher_alg, ==, cipher->alg); + g_assert_cmpint(data->cipher_mode, ==, cipher->mode); + g_assert_cmpint(data->hash_alg, ==, + qcrypto_block_get_kdf_hash(blk)); + + g_assert_cmpint(data->ivgen_alg, ==, + qcrypto_ivgen_get_algorithm(ivgen)); + g_assert_cmpint(data->ivgen_hash, ==, + qcrypto_ivgen_get_hash(ivgen)); +} + + +static void test_block(gconstpointer opaque) +{ + const struct QCryptoBlockTestData *data = opaque; + QCryptoBlock *blk; + Buffer header; + Object *sec = test_block_secret(); + + memset(&header, 0, sizeof(header)); + buffer_init(&header, "header"); + + blk = qcrypto_block_create(data->create_opts, NULL, + test_block_init_func, + test_block_write_func, + &header, + &error_abort); + g_assert(blk); + + if (data->expect_header) { + g_assert_cmpint(header.capacity, >, 0); + } else { + g_assert_cmpint(header.capacity, ==, 0); + } + + test_block_assert_setup(data, blk); + + qcrypto_block_free(blk); + object_unparent(sec); + + /* Ensure we can't open without the secret */ + blk = qcrypto_block_open(data->open_opts, NULL, + test_block_read_func, + &header, + 0, + 1, + NULL); + g_assert(blk == NULL); + + /* Ensure we can't open without the secret, unless NO_IO */ + blk = qcrypto_block_open(data->open_opts, NULL, + test_block_read_func, + &header, + QCRYPTO_BLOCK_OPEN_NO_IO, + 1, + &error_abort); + + g_assert(qcrypto_block_get_cipher(blk) == NULL); + g_assert(qcrypto_block_get_ivgen(blk) == NULL); + + qcrypto_block_free(blk); + + + /* Now open for real with secret */ + sec = test_block_secret(); + blk = qcrypto_block_open(data->open_opts, NULL, + test_block_read_func, + &header, + 0, + 1, + &error_abort); + g_assert(blk); + + test_block_assert_setup(data, blk); + + qcrypto_block_free(blk); + + object_unparent(sec); + + buffer_free(&header); +} + + +int main(int argc, char **argv) +{ + gsize i; + + module_call_init(MODULE_INIT_QOM); + g_test_init(&argc, &argv, NULL); + + g_assert(qcrypto_init(NULL) == 0); + + for (i = 0; i < G_N_ELEMENTS(test_data); i++) { + if (test_data[i].open_opts->format == Q_CRYPTO_BLOCK_FORMAT_LUKS && + !qcrypto_hash_supports(test_data[i].hash_alg)) { + continue; + } + if (!test_data[i].slow || + g_test_slow()) { + g_test_add_data_func(test_data[i].path, &test_data[i], test_block); + } + } + + return g_test_run(); +} diff --git a/tests/unit/test-crypto-cipher.c b/tests/unit/test-crypto-cipher.c new file mode 100644 index 0000000000..280319a223 --- /dev/null +++ b/tests/unit/test-crypto-cipher.c @@ -0,0 +1,801 @@ +/* + * QEMU Crypto cipher algorithms + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + * + */ + +#include "qemu/osdep.h" + +#include "crypto/init.h" +#include "crypto/cipher.h" +#include "qapi/error.h" + +typedef struct QCryptoCipherTestData QCryptoCipherTestData; +struct QCryptoCipherTestData { + const char *path; + QCryptoCipherAlgorithm alg; + QCryptoCipherMode mode; + const char *key; + const char *plaintext; + const char *ciphertext; + const char *iv; +}; + +/* AES test data comes from appendix F of: + * + * http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf + */ +static QCryptoCipherTestData test_data[] = { + { + /* NIST F.1.1 ECB-AES128.Encrypt */ + .path = "/crypto/cipher/aes-ecb-128", + .alg = QCRYPTO_CIPHER_ALG_AES_128, + .mode = QCRYPTO_CIPHER_MODE_ECB, + .key = "2b7e151628aed2a6abf7158809cf4f3c", + .plaintext = + "6bc1bee22e409f96e93d7e117393172a" + "ae2d8a571e03ac9c9eb76fac45af8e51" + "30c81c46a35ce411e5fbc1191a0a52ef" + "f69f2445df4f9b17ad2b417be66c3710", + .ciphertext = + "3ad77bb40d7a3660a89ecaf32466ef97" + "f5d3d58503b9699de785895a96fdbaaf" + "43b1cd7f598ece23881b00e3ed030688" + "7b0c785e27e8ad3f8223207104725dd4" + }, + { + /* NIST F.1.3 ECB-AES192.Encrypt */ + .path = "/crypto/cipher/aes-ecb-192", + .alg = QCRYPTO_CIPHER_ALG_AES_192, + .mode = QCRYPTO_CIPHER_MODE_ECB, + .key = "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b", + .plaintext = + "6bc1bee22e409f96e93d7e117393172a" + "ae2d8a571e03ac9c9eb76fac45af8e51" + "30c81c46a35ce411e5fbc1191a0a52ef" + "f69f2445df4f9b17ad2b417be66c3710", + .ciphertext = + "bd334f1d6e45f25ff712a214571fa5cc" + "974104846d0ad3ad7734ecb3ecee4eef" + "ef7afd2270e2e60adce0ba2face6444e" + "9a4b41ba738d6c72fb16691603c18e0e" + }, + { + /* NIST F.1.5 ECB-AES256.Encrypt */ + .path = "/crypto/cipher/aes-ecb-256", + .alg = QCRYPTO_CIPHER_ALG_AES_256, + .mode = QCRYPTO_CIPHER_MODE_ECB, + .key = + "603deb1015ca71be2b73aef0857d7781" + "1f352c073b6108d72d9810a30914dff4", + .plaintext = + "6bc1bee22e409f96e93d7e117393172a" + "ae2d8a571e03ac9c9eb76fac45af8e51" + "30c81c46a35ce411e5fbc1191a0a52ef" + "f69f2445df4f9b17ad2b417be66c3710", + .ciphertext = + "f3eed1bdb5d2a03c064b5a7e3db181f8" + "591ccb10d410ed26dc5ba74a31362870" + "b6ed21b99ca6f4f9f153e7b1beafed1d" + "23304b7a39f9f3ff067d8d8f9e24ecc7", + }, + { + /* NIST F.2.1 CBC-AES128.Encrypt */ + .path = "/crypto/cipher/aes-cbc-128", + .alg = QCRYPTO_CIPHER_ALG_AES_128, + .mode = QCRYPTO_CIPHER_MODE_CBC, + .key = "2b7e151628aed2a6abf7158809cf4f3c", + .iv = "000102030405060708090a0b0c0d0e0f", + .plaintext = + "6bc1bee22e409f96e93d7e117393172a" + "ae2d8a571e03ac9c9eb76fac45af8e51" + "30c81c46a35ce411e5fbc1191a0a52ef" + "f69f2445df4f9b17ad2b417be66c3710", + .ciphertext = + "7649abac8119b246cee98e9b12e9197d" + "5086cb9b507219ee95db113a917678b2" + "73bed6b8e3c1743b7116e69e22229516" + "3ff1caa1681fac09120eca307586e1a7", + }, + { + /* NIST F.2.3 CBC-AES128.Encrypt */ + .path = "/crypto/cipher/aes-cbc-192", + .alg = QCRYPTO_CIPHER_ALG_AES_192, + .mode = QCRYPTO_CIPHER_MODE_CBC, + .key = "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b", + .iv = "000102030405060708090a0b0c0d0e0f", + .plaintext = + "6bc1bee22e409f96e93d7e117393172a" + "ae2d8a571e03ac9c9eb76fac45af8e51" + "30c81c46a35ce411e5fbc1191a0a52ef" + "f69f2445df4f9b17ad2b417be66c3710", + .ciphertext = + "4f021db243bc633d7178183a9fa071e8" + "b4d9ada9ad7dedf4e5e738763f69145a" + "571b242012fb7ae07fa9baac3df102e0" + "08b0e27988598881d920a9e64f5615cd", + }, + { + /* NIST F.2.5 CBC-AES128.Encrypt */ + .path = "/crypto/cipher/aes-cbc-256", + .alg = QCRYPTO_CIPHER_ALG_AES_256, + .mode = QCRYPTO_CIPHER_MODE_CBC, + .key = + "603deb1015ca71be2b73aef0857d7781" + "1f352c073b6108d72d9810a30914dff4", + .iv = "000102030405060708090a0b0c0d0e0f", + .plaintext = + "6bc1bee22e409f96e93d7e117393172a" + "ae2d8a571e03ac9c9eb76fac45af8e51" + "30c81c46a35ce411e5fbc1191a0a52ef" + "f69f2445df4f9b17ad2b417be66c3710", + .ciphertext = + "f58c4c04d6e5f1ba779eabfb5f7bfbd6" + "9cfc4e967edb808d679f777bc6702c7d" + "39f23369a9d9bacfa530e26304231461" + "b2eb05e2c39be9fcda6c19078c6a9d1b", + }, + { + .path = "/crypto/cipher/des-rfb-ecb-56", + .alg = QCRYPTO_CIPHER_ALG_DES_RFB, + .mode = QCRYPTO_CIPHER_MODE_ECB, + .key = "0123456789abcdef", + .plaintext = + "6bc1bee22e409f96e93d7e117393172a" + "ae2d8a571e03ac9c9eb76fac45af8e51" + "30c81c46a35ce411e5fbc1191a0a52ef" + "f69f2445df4f9b17ad2b417be66c3710", + .ciphertext = + "8f346aaf64eaf24040720d80648c52e7" + "aefc616be53ab1a3d301e69d91e01838" + "ffd29f1bb5596ad94ea2d8e6196b7f09" + "30d8ed0bf2773af36dd82a6280c20926", + }, +#if defined(CONFIG_NETTLE) || defined(CONFIG_GCRYPT) + { + /* Borrowed from linux-kernel crypto/testmgr.h */ + .path = "/crypto/cipher/3des-cbc", + .alg = QCRYPTO_CIPHER_ALG_3DES, + .mode = QCRYPTO_CIPHER_MODE_CBC, + .key = + "e9c0ff2e760b6424444d995a12d640c0" + "eac284e81495dbe8", + .iv = + "7d3388930f93b242", + .plaintext = + "6f54206f614d796e5320636565727374" + "54206f6f4d206e612079655372637465" + "20736f54206f614d796e532063656572" + "737454206f6f4d206e61207965537263" + "746520736f54206f614d796e53206365" + "6572737454206f6f4d206e6120796553" + "7263746520736f54206f614d796e5320" + "63656572737454206f6f4d206e610a79", + .ciphertext = + "0e2db6973c5633f4671721c76e8ad549" + "74b34905c51cd0ed12565c5396b6007d" + "9048fcf58d2939cc8ad5351836234ed7" + "76d1da0c9467bb048bf2036ca8cfb6ea" + "226447aa8f7513bf9fc2c3f0c956c57a" + "71632e897b1e12cae25fafd8a4f8c97a" + "d6f92131624445a6d6bc5ad32d5443cc" + "9ddea570e942458a6bfab19113b0d919", + }, + { + /* Borrowed from linux-kernel crypto/testmgr.h */ + .path = "/crypto/cipher/3des-ecb", + .alg = QCRYPTO_CIPHER_ALG_3DES, + .mode = QCRYPTO_CIPHER_MODE_ECB, + .key = + "0123456789abcdef5555555555555555" + "fedcba9876543210", + .plaintext = + "736f6d6564617461", + .ciphertext = + "18d748e563620572", + }, + { + /* Borrowed from linux-kernel crypto/testmgr.h */ + .path = "/crypto/cipher/3des-ctr", + .alg = QCRYPTO_CIPHER_ALG_3DES, + .mode = QCRYPTO_CIPHER_MODE_CTR, + .key = + "9cd6f39cb95a67005a67002dceeb2dce" + "ebb45172b451721f", + .iv = + "ffffffffffffffff", + .plaintext = + "05ec77fb42d559208b128669f05bcf56" + "39ad349f66ea7dc448d3ba0db118e34a" + "fe41285c278e11856cf75ec2553ca00b" + "9265e970db4fd6b900b41fe649fd442f" + "533a8d149863ca5dc1a833a70e9178ec" + "77de42d5bc078b12e54cf05b22563980" + "6b9f66c950c4af36ba0d947fe34add41" + "28b31a8e11f843f75e21553c876e9265" + "cc57dba235b900eb72e649d0442fb619" + "8d14ff46ca5d24a8339a6d9178c377de" + "a108bc07ee71e54cd75b22b51c806bf2" + "45c9503baf369960947fc64adda40fb3" + "1aed74f8432a5e218813876ef158cc57" + "3ea2359c67eb72c549d0bb02b619e04b" + "ff46295d248f169a6df45fc3aa3da108" + "937aee71d84cd7be01b51ce74ef2452c" + "503b82159960cb52c6a930a40f9679ed" + "74df432abd048813fa4df15823573e81" + "689c67ce51c5ac37bb02957ce04bd246" + "29b01b8f16f940f45f26aa3d846f937a" + "cd54d8a30abe01e873e74ed1452cb71e" + "8215fc47cb5225a9309b629679c074df" + "a609bd04ef76fa4dd458238a1d8168f3" + "5ace5138ac379e61957cc74bd2a50cb0" + "1be275f9402b5f268910846ff659cd54" + "3fa30a9d64e873da4ed1b803b71ee148" + "fc472e52258c179b62f55cc0ab32a609" + "907bef76d94dd4bf068a1de44ff35a2d" + "5138836a9e61c853c7ae31a50c977ee2" + "75dc402bb2058910fb42f65920543f86" + "699d64cf56daad34b803ea7de148d347", + .ciphertext = + "07c20820721f49ef19cd6f3253052215" + "a2852bdb85d2d8b9dd0d1b45cb6911d4" + "eabeb2455d0caebea0c127ac659f537e" + "afc21bb5b86d360c25c0f86d0b2901da" + "1378dc89121243faf612ef8d87627883" + "e2be41204c6d351bd10c30cfe2de2b03" + "bf4573d4e55995d1b39b276297bdde7f" + "a4d23980aa5023f074883da86a18793b" + "c4966c8d2240926ed6ad2a1fde63c0e7" + "07f72df7b5f3f0cc017c2a9bc210caaa" + "fd2b3fc5f3f6fc9b45db53e45bf3c97b" + "8e52ffc802b8ac9da10039da3d2d0e01" + "097d8d5ebe53b9b08ee7e2966ab278ea" + "de238ba5fa5ce3dabf8e316a55d16ab2" + "b5466fa5f0eeba1f9f98b0664fd03fa9" + "df5f58c4f4ff755c403a097e6e1c97d4" + "cce7e771cf0b150871fa0797cde6ca1d" + "14280ccf99137af1ebfafa9207de1da1" + "d33669fe514d9f2e83374f1f4830ed04" + "4da4ef3aca76f41c418f6337782f86a6" + "ef417ed2af88ab675271c38ef8269372" + "aad60ee70b46b13ab408a9a8a0cf200c" + "52bc8b0556b2bc319b74b92929969a50" + "dc45dc1aeb0c64d4d3057e5955c3f490" + "c2abf89b8adacea1c3f4ad77dd44c8ac" + "a3f1c9d2195cb0caa234c1f76cfdac65" + "32dc48c4f2006b77f17d76acc031632a" + "a53a62c891b10365cb43d106dfc367bc" + "dce0cd35ce4965a0527ba70d07a91bb0" + "407772c2ea0e3a7846b991b6e73d5142" + "fd51b0c62c6313785ceefccfc4700034", + }, +#endif + { + /* RFC 2144, Appendix B.1 */ + .path = "/crypto/cipher/cast5-128", + .alg = QCRYPTO_CIPHER_ALG_CAST5_128, + .mode = QCRYPTO_CIPHER_MODE_ECB, + .key = "0123456712345678234567893456789A", + .plaintext = "0123456789abcdef", + .ciphertext = "238b4fe5847e44b2", + }, + { + /* libgcrypt serpent.c */ + .path = "/crypto/cipher/serpent-128", + .alg = QCRYPTO_CIPHER_ALG_SERPENT_128, + .mode = QCRYPTO_CIPHER_MODE_ECB, + .key = "00000000000000000000000000000000", + .plaintext = "d29d576fcea3a3a7ed9099f29273d78e", + .ciphertext = "b2288b968ae8b08648d1ce9606fd992d", + }, + { + /* libgcrypt serpent.c */ + .path = "/crypto/cipher/serpent-192", + .alg = QCRYPTO_CIPHER_ALG_SERPENT_192, + .mode = QCRYPTO_CIPHER_MODE_ECB, + .key = "00000000000000000000000000000000" + "0000000000000000", + .plaintext = "d29d576fceaba3a7ed9899f2927bd78e", + .ciphertext = "130e353e1037c22405e8faefb2c3c3e9", + }, + { + /* libgcrypt serpent.c */ + .path = "/crypto/cipher/serpent-256a", + .alg = QCRYPTO_CIPHER_ALG_SERPENT_256, + .mode = QCRYPTO_CIPHER_MODE_ECB, + .key = "00000000000000000000000000000000" + "00000000000000000000000000000000", + .plaintext = "d095576fcea3e3a7ed98d9f29073d78e", + .ciphertext = "b90ee5862de69168f2bdd5125b45472b", + }, + { + /* libgcrypt serpent.c */ + .path = "/crypto/cipher/serpent-256b", + .alg = QCRYPTO_CIPHER_ALG_SERPENT_256, + .mode = QCRYPTO_CIPHER_MODE_ECB, + .key = "00000000000000000000000000000000" + "00000000000000000000000000000000", + .plaintext = "00000000010000000200000003000000", + .ciphertext = "2061a42782bd52ec691ec383b03ba77c", + }, + { + /* Twofish paper "Known Answer Test" */ + .path = "/crypto/cipher/twofish-128", + .alg = QCRYPTO_CIPHER_ALG_TWOFISH_128, + .mode = QCRYPTO_CIPHER_MODE_ECB, + .key = "d491db16e7b1c39e86cb086b789f5419", + .plaintext = "019f9809de1711858faac3a3ba20fbc3", + .ciphertext = "6363977de839486297e661c6c9d668eb", + }, + { + /* Twofish paper "Known Answer Test", I=3 */ + .path = "/crypto/cipher/twofish-192", + .alg = QCRYPTO_CIPHER_ALG_TWOFISH_192, + .mode = QCRYPTO_CIPHER_MODE_ECB, + .key = "88b2b2706b105e36b446bb6d731a1e88" + "efa71f788965bd44", + .plaintext = "39da69d6ba4997d585b6dc073ca341b2", + .ciphertext = "182b02d81497ea45f9daacdc29193a65", + }, + { + /* Twofish paper "Known Answer Test", I=4 */ + .path = "/crypto/cipher/twofish-256", + .alg = QCRYPTO_CIPHER_ALG_TWOFISH_256, + .mode = QCRYPTO_CIPHER_MODE_ECB, + .key = "d43bb7556ea32e46f2a282b7d45b4e0d" + "57ff739d4dc92c1bd7fc01700cc8216f", + .plaintext = "90afe91bb288544f2c32dc239b2635e6", + .ciphertext = "6cb4561c40bf0a9705931cb6d408e7fa", + }, + { + /* #1 32 byte key, 32 byte PTX */ + .path = "/crypto/cipher/aes-xts-128-1", + .alg = QCRYPTO_CIPHER_ALG_AES_128, + .mode = QCRYPTO_CIPHER_MODE_XTS, + .key = + "00000000000000000000000000000000" + "00000000000000000000000000000000", + .iv = + "00000000000000000000000000000000", + .plaintext = + "00000000000000000000000000000000" + "00000000000000000000000000000000", + .ciphertext = + "917cf69ebd68b2ec9b9fe9a3eadda692" + "cd43d2f59598ed858c02c2652fbf922e", + }, + { + /* #2, 32 byte key, 32 byte PTX */ + .path = "/crypto/cipher/aes-xts-128-2", + .alg = QCRYPTO_CIPHER_ALG_AES_128, + .mode = QCRYPTO_CIPHER_MODE_XTS, + .key = + "11111111111111111111111111111111" + "22222222222222222222222222222222", + .iv = + "33333333330000000000000000000000", + .plaintext = + "44444444444444444444444444444444" + "44444444444444444444444444444444", + .ciphertext = + "c454185e6a16936e39334038acef838b" + "fb186fff7480adc4289382ecd6d394f0", + }, + { + /* #5 from xts.7, 32 byte key, 32 byte PTX */ + .path = "/crypto/cipher/aes-xts-128-3", + .alg = QCRYPTO_CIPHER_ALG_AES_128, + .mode = QCRYPTO_CIPHER_MODE_XTS, + .key = + "fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0" + "bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0", + .iv = + "9a785634120000000000000000000000", + .plaintext = + "44444444444444444444444444444444" + "44444444444444444444444444444444", + .ciphertext = + "b01f86f8edc1863706fa8a4253e34f28" + "af319de38334870f4dd1f94cbe9832f1", + }, + { + /* #4, 32 byte key, 512 byte PTX */ + .path = "/crypto/cipher/aes-xts-128-4", + .alg = QCRYPTO_CIPHER_ALG_AES_128, + .mode = QCRYPTO_CIPHER_MODE_XTS, + .key = + "27182818284590452353602874713526" + "31415926535897932384626433832795", + .iv = + "00000000000000000000000000000000", + .plaintext = + "000102030405060708090a0b0c0d0e0f" + "101112131415161718191a1b1c1d1e1f" + "202122232425262728292a2b2c2d2e2f" + "303132333435363738393a3b3c3d3e3f" + "404142434445464748494a4b4c4d4e4f" + "505152535455565758595a5b5c5d5e5f" + "606162636465666768696a6b6c6d6e6f" + "707172737475767778797a7b7c7d7e7f" + "808182838485868788898a8b8c8d8e8f" + "909192939495969798999a9b9c9d9e9f" + "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf" + "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf" + "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf" + "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf" + "e0e1e2e3e4e5e6e7e8e9eaebecedeeef" + "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff" + "000102030405060708090a0b0c0d0e0f" + "101112131415161718191a1b1c1d1e1f" + "202122232425262728292a2b2c2d2e2f" + "303132333435363738393a3b3c3d3e3f" + "404142434445464748494a4b4c4d4e4f" + "505152535455565758595a5b5c5d5e5f" + "606162636465666768696a6b6c6d6e6f" + "707172737475767778797a7b7c7d7e7f" + "808182838485868788898a8b8c8d8e8f" + "909192939495969798999a9b9c9d9e9f" + "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf" + "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf" + "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf" + "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf" + "e0e1e2e3e4e5e6e7e8e9eaebecedeeef" + "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", + .ciphertext = + "27a7479befa1d476489f308cd4cfa6e2" + "a96e4bbe3208ff25287dd3819616e89c" + "c78cf7f5e543445f8333d8fa7f560000" + "05279fa5d8b5e4ad40e736ddb4d35412" + "328063fd2aab53e5ea1e0a9f332500a5" + "df9487d07a5c92cc512c8866c7e860ce" + "93fdf166a24912b422976146ae20ce84" + "6bb7dc9ba94a767aaef20c0d61ad0265" + "5ea92dc4c4e41a8952c651d33174be51" + "a10c421110e6d81588ede82103a252d8" + "a750e8768defffed9122810aaeb99f91" + "72af82b604dc4b8e51bcb08235a6f434" + "1332e4ca60482a4ba1a03b3e65008fc5" + "da76b70bf1690db4eae29c5f1badd03c" + "5ccf2a55d705ddcd86d449511ceb7ec3" + "0bf12b1fa35b913f9f747a8afd1b130e" + "94bff94effd01a91735ca1726acd0b19" + "7c4e5b03393697e126826fb6bbde8ecc" + "1e08298516e2c9ed03ff3c1b7860f6de" + "76d4cecd94c8119855ef5297ca67e9f3" + "e7ff72b1e99785ca0a7e7720c5b36dc6" + "d72cac9574c8cbbc2f801e23e56fd344" + "b07f22154beba0f08ce8891e643ed995" + "c94d9a69c9f1b5f499027a78572aeebd" + "74d20cc39881c213ee770b1010e4bea7" + "18846977ae119f7a023ab58cca0ad752" + "afe656bb3c17256a9f6e9bf19fdd5a38" + "fc82bbe872c5539edb609ef4f79c203e" + "bb140f2e583cb2ad15b4aa5b655016a8" + "449277dbd477ef2c8d6c017db738b18d" + "eb4a427d1923ce3ff262735779a418f2" + "0a282df920147beabe421ee5319d0568", + }, + { + /* Bad config - cast5-128 has 8 byte block size + * which is incompatible with XTS + */ + .path = "/crypto/cipher/cast5-xts-128", + .alg = QCRYPTO_CIPHER_ALG_CAST5_128, + .mode = QCRYPTO_CIPHER_MODE_XTS, + .key = + "27182818284590452353602874713526" + "31415926535897932384626433832795", + }, + { + /* NIST F.5.1 CTR-AES128.Encrypt */ + .path = "/crypto/cipher/aes-ctr-128", + .alg = QCRYPTO_CIPHER_ALG_AES_128, + .mode = QCRYPTO_CIPHER_MODE_CTR, + .key = "2b7e151628aed2a6abf7158809cf4f3c", + .iv = "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", + .plaintext = + "6bc1bee22e409f96e93d7e117393172a" + "ae2d8a571e03ac9c9eb76fac45af8e51" + "30c81c46a35ce411e5fbc1191a0a52ef" + "f69f2445df4f9b17ad2b417be66c3710", + .ciphertext = + "874d6191b620e3261bef6864990db6ce" + "9806f66b7970fdff8617187bb9fffdff" + "5ae4df3edbd5d35e5b4f09020db03eab" + "1e031dda2fbe03d1792170a0f3009cee", + }, + { + /* NIST F.5.3 CTR-AES192.Encrypt */ + .path = "/crypto/cipher/aes-ctr-192", + .alg = QCRYPTO_CIPHER_ALG_AES_192, + .mode = QCRYPTO_CIPHER_MODE_CTR, + .key = "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b", + .iv = "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", + .plaintext = + "6bc1bee22e409f96e93d7e117393172a" + "ae2d8a571e03ac9c9eb76fac45af8e51" + "30c81c46a35ce411e5fbc1191a0a52ef" + "f69f2445df4f9b17ad2b417be66c3710", + .ciphertext = + "1abc932417521ca24f2b0459fe7e6e0b" + "090339ec0aa6faefd5ccc2c6f4ce8e94" + "1e36b26bd1ebc670d1bd1d665620abf7" + "4f78a7f6d29809585a97daec58c6b050", + }, + { + /* NIST F.5.5 CTR-AES256.Encrypt */ + .path = "/crypto/cipher/aes-ctr-256", + .alg = QCRYPTO_CIPHER_ALG_AES_256, + .mode = QCRYPTO_CIPHER_MODE_CTR, + .key = "603deb1015ca71be2b73aef0857d7781" + "1f352c073b6108d72d9810a30914dff4", + .iv = "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", + .plaintext = + "6bc1bee22e409f96e93d7e117393172a" + "ae2d8a571e03ac9c9eb76fac45af8e51" + "30c81c46a35ce411e5fbc1191a0a52ef" + "f69f2445df4f9b17ad2b417be66c3710", + .ciphertext = + "601ec313775789a5b7a7f504bbf3d228" + "f443e3ca4d62b59aca84e990cacaf5c5" + "2b0930daa23de94ce87017ba2d84988d" + "dfc9c58db67aada613c2dd08457941a6", + } +}; + + +static inline int unhex(char c) +{ + if (c >= 'a' && c <= 'f') { + return 10 + (c - 'a'); + } + if (c >= 'A' && c <= 'F') { + return 10 + (c - 'A'); + } + return c - '0'; +} + +static inline char hex(int i) +{ + if (i < 10) { + return '0' + i; + } + return 'a' + (i - 10); +} + +static size_t unhex_string(const char *hexstr, + uint8_t **data) +{ + size_t len; + size_t i; + + if (!hexstr) { + *data = NULL; + return 0; + } + + len = strlen(hexstr); + *data = g_new0(uint8_t, len / 2); + + for (i = 0; i < len; i += 2) { + (*data)[i/2] = (unhex(hexstr[i]) << 4) | unhex(hexstr[i+1]); + } + return len / 2; +} + +static char *hex_string(const uint8_t *bytes, + size_t len) +{ + char *hexstr = g_new0(char, len * 2 + 1); + size_t i; + + for (i = 0; i < len; i++) { + hexstr[i*2] = hex((bytes[i] >> 4) & 0xf); + hexstr[i*2+1] = hex(bytes[i] & 0xf); + } + hexstr[len*2] = '\0'; + + return hexstr; +} + +static void test_cipher(const void *opaque) +{ + const QCryptoCipherTestData *data = opaque; + + QCryptoCipher *cipher; + uint8_t *key, *iv = NULL, *ciphertext = NULL, + *plaintext = NULL, *outtext = NULL; + size_t nkey, niv = 0, nciphertext = 0, nplaintext = 0; + char *outtexthex = NULL; + size_t ivsize, keysize, blocksize; + Error *err = NULL; + + nkey = unhex_string(data->key, &key); + if (data->iv) { + niv = unhex_string(data->iv, &iv); + } + if (data->ciphertext) { + nciphertext = unhex_string(data->ciphertext, &ciphertext); + } + if (data->plaintext) { + nplaintext = unhex_string(data->plaintext, &plaintext); + } + + g_assert(nciphertext == nplaintext); + + outtext = g_new0(uint8_t, nciphertext); + + cipher = qcrypto_cipher_new( + data->alg, data->mode, + key, nkey, + &err); + if (data->plaintext) { + g_assert(err == NULL); + g_assert(cipher != NULL); + } else { + error_free_or_abort(&err); + g_assert(cipher == NULL); + goto cleanup; + } + + keysize = qcrypto_cipher_get_key_len(data->alg); + blocksize = qcrypto_cipher_get_block_len(data->alg); + ivsize = qcrypto_cipher_get_iv_len(data->alg, data->mode); + + if (data->mode == QCRYPTO_CIPHER_MODE_XTS) { + g_assert_cmpint(keysize * 2, ==, nkey); + } else { + g_assert_cmpint(keysize, ==, nkey); + } + g_assert_cmpint(ivsize, ==, niv); + if (niv) { + g_assert_cmpint(blocksize, ==, niv); + } + + if (iv) { + g_assert(qcrypto_cipher_setiv(cipher, + iv, niv, + &error_abort) == 0); + } + g_assert(qcrypto_cipher_encrypt(cipher, + plaintext, + outtext, + nplaintext, + &error_abort) == 0); + + outtexthex = hex_string(outtext, nciphertext); + + g_assert_cmpstr(outtexthex, ==, data->ciphertext); + + g_free(outtexthex); + + if (iv) { + g_assert(qcrypto_cipher_setiv(cipher, + iv, niv, + &error_abort) == 0); + } + g_assert(qcrypto_cipher_decrypt(cipher, + ciphertext, + outtext, + nplaintext, + &error_abort) == 0); + + outtexthex = hex_string(outtext, nplaintext); + + g_assert_cmpstr(outtexthex, ==, data->plaintext); + + cleanup: + g_free(outtext); + g_free(outtexthex); + g_free(key); + g_free(iv); + g_free(ciphertext); + g_free(plaintext); + qcrypto_cipher_free(cipher); +} + + +static void test_cipher_null_iv(void) +{ + QCryptoCipher *cipher; + uint8_t key[32] = { 0 }; + uint8_t plaintext[32] = { 0 }; + uint8_t ciphertext[32] = { 0 }; + + cipher = qcrypto_cipher_new( + QCRYPTO_CIPHER_ALG_AES_256, + QCRYPTO_CIPHER_MODE_CBC, + key, sizeof(key), + &error_abort); + g_assert(cipher != NULL); + + /* Don't call qcrypto_cipher_setiv */ + + qcrypto_cipher_encrypt(cipher, + plaintext, + ciphertext, + sizeof(plaintext), + &error_abort); + + qcrypto_cipher_free(cipher); +} + +static void test_cipher_short_plaintext(void) +{ + Error *err = NULL; + QCryptoCipher *cipher; + uint8_t key[32] = { 0 }; + uint8_t plaintext1[20] = { 0 }; + uint8_t ciphertext1[20] = { 0 }; + uint8_t plaintext2[40] = { 0 }; + uint8_t ciphertext2[40] = { 0 }; + int ret; + + cipher = qcrypto_cipher_new( + QCRYPTO_CIPHER_ALG_AES_256, + QCRYPTO_CIPHER_MODE_CBC, + key, sizeof(key), + &error_abort); + g_assert(cipher != NULL); + + /* Should report an error as plaintext is shorter + * than block size + */ + ret = qcrypto_cipher_encrypt(cipher, + plaintext1, + ciphertext1, + sizeof(plaintext1), + &err); + g_assert(ret == -1); + error_free_or_abort(&err); + + /* Should report an error as plaintext is larger than + * block size, but not a multiple of block size + */ + ret = qcrypto_cipher_encrypt(cipher, + plaintext2, + ciphertext2, + sizeof(plaintext2), + &err); + g_assert(ret == -1); + error_free_or_abort(&err); + + qcrypto_cipher_free(cipher); +} + +int main(int argc, char **argv) +{ + size_t i; + + g_test_init(&argc, &argv, NULL); + + g_assert(qcrypto_init(NULL) == 0); + + for (i = 0; i < G_N_ELEMENTS(test_data); i++) { + if (qcrypto_cipher_supports(test_data[i].alg, test_data[i].mode)) { + g_test_add_data_func(test_data[i].path, &test_data[i], test_cipher); + } + } + + g_test_add_func("/crypto/cipher/null-iv", + test_cipher_null_iv); + + g_test_add_func("/crypto/cipher/short-plaintext", + test_cipher_short_plaintext); + + return g_test_run(); +} diff --git a/tests/unit/test-crypto-hash.c b/tests/unit/test-crypto-hash.c new file mode 100644 index 0000000000..ce7d0ab9b5 --- /dev/null +++ b/tests/unit/test-crypto-hash.c @@ -0,0 +1,255 @@ +/* + * QEMU Crypto hash algorithms + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + * + */ + +#include "qemu/osdep.h" + +#include "crypto/init.h" +#include "crypto/hash.h" + +#define INPUT_TEXT "Hiss hisss Hissss hiss Hiss hisss Hiss hiss" +#define INPUT_TEXT1 "Hiss hisss " +#define INPUT_TEXT2 "Hissss hiss " +#define INPUT_TEXT3 "Hiss hisss Hiss hiss" + +#define OUTPUT_MD5 "628d206371563035ab8ef62f492bdec9" +#define OUTPUT_SHA1 "b2e74f26758a3a421e509cee045244b78753cc02" +#define OUTPUT_SHA224 "e2f7415aad33ef79f6516b0986d7175f" \ + "9ca3389a85bf6cfed078737b" +#define OUTPUT_SHA256 "bc757abb0436586f392b437e5dd24096" \ + "f7f224de6b74d4d86e2abc6121b160d0" +#define OUTPUT_SHA384 "887ce52efb4f46700376356583b7e279" \ + "4f612bd024e4495087ddb946c448c69d" \ + "56dbf7152a94a5e63a80f3ba9f0eed78" +#define OUTPUT_SHA512 "3a90d79638235ec6c4c11bebd84d83c0" \ + "549bc1e84edc4b6ec7086487641256cb" \ + "63b54e4cb2d2032b393994aa263c0dbb" \ + "e00a9f2fe9ef6037352232a1eec55ee7" +#define OUTPUT_RIPEMD160 "f3d658fad3fdfb2b52c9369cf0d441249ddfa8a0" + +#define OUTPUT_MD5_B64 "Yo0gY3FWMDWrjvYvSSveyQ==" +#define OUTPUT_SHA1_B64 "sudPJnWKOkIeUJzuBFJEt4dTzAI=" +#define OUTPUT_SHA224_B64 "4vdBWq0z73n2UWsJhtcXX5yjOJqFv2z+0Hhzew==" +#define OUTPUT_SHA256_B64 "vHV6uwQ2WG85K0N+XdJAlvfyJN5rdNTYbiq8YSGxYNA=" +#define OUTPUT_SHA384_B64 "iHzlLvtPRnADdjVlg7fieU9hK9Ak5ElQh925RsRI" \ + "xp1W2/cVKpSl5jqA87qfDu14" +#define OUTPUT_SHA512_B64 "OpDXljgjXsbEwRvr2E2DwFSbwehO3Etuxwhkh2QS" \ + "VstjtU5MstIDKzk5lKomPA274AqfL+nvYDc1IjKh" \ + "7sVe5w==" +#define OUTPUT_RIPEMD160_B64 "89ZY+tP9+ytSyTac8NRBJJ3fqKA=" + +static const char *expected_outputs[] = { + [QCRYPTO_HASH_ALG_MD5] = OUTPUT_MD5, + [QCRYPTO_HASH_ALG_SHA1] = OUTPUT_SHA1, + [QCRYPTO_HASH_ALG_SHA224] = OUTPUT_SHA224, + [QCRYPTO_HASH_ALG_SHA256] = OUTPUT_SHA256, + [QCRYPTO_HASH_ALG_SHA384] = OUTPUT_SHA384, + [QCRYPTO_HASH_ALG_SHA512] = OUTPUT_SHA512, + [QCRYPTO_HASH_ALG_RIPEMD160] = OUTPUT_RIPEMD160, +}; +static const char *expected_outputs_b64[] = { + [QCRYPTO_HASH_ALG_MD5] = OUTPUT_MD5_B64, + [QCRYPTO_HASH_ALG_SHA1] = OUTPUT_SHA1_B64, + [QCRYPTO_HASH_ALG_SHA224] = OUTPUT_SHA224_B64, + [QCRYPTO_HASH_ALG_SHA256] = OUTPUT_SHA256_B64, + [QCRYPTO_HASH_ALG_SHA384] = OUTPUT_SHA384_B64, + [QCRYPTO_HASH_ALG_SHA512] = OUTPUT_SHA512_B64, + [QCRYPTO_HASH_ALG_RIPEMD160] = OUTPUT_RIPEMD160_B64, +}; +static const int expected_lens[] = { + [QCRYPTO_HASH_ALG_MD5] = 16, + [QCRYPTO_HASH_ALG_SHA1] = 20, + [QCRYPTO_HASH_ALG_SHA224] = 28, + [QCRYPTO_HASH_ALG_SHA256] = 32, + [QCRYPTO_HASH_ALG_SHA384] = 48, + [QCRYPTO_HASH_ALG_SHA512] = 64, + [QCRYPTO_HASH_ALG_RIPEMD160] = 20, +}; + +static const char hex[] = "0123456789abcdef"; + +/* Test with dynamic allocation */ +static void test_hash_alloc(void) +{ + size_t i; + + for (i = 0; i < G_N_ELEMENTS(expected_outputs) ; i++) { + uint8_t *result = NULL; + size_t resultlen = 0; + int ret; + size_t j; + + if (!qcrypto_hash_supports(i)) { + continue; + } + + ret = qcrypto_hash_bytes(i, + INPUT_TEXT, + strlen(INPUT_TEXT), + &result, + &resultlen, + NULL); + g_assert(ret == 0); + g_assert(resultlen == expected_lens[i]); + + for (j = 0; j < resultlen; j++) { + g_assert(expected_outputs[i][j * 2] == hex[(result[j] >> 4) & 0xf]); + g_assert(expected_outputs[i][j * 2 + 1] == hex[result[j] & 0xf]); + } + g_free(result); + } +} + +/* Test with caller preallocating */ +static void test_hash_prealloc(void) +{ + size_t i; + + for (i = 0; i < G_N_ELEMENTS(expected_outputs) ; i++) { + uint8_t *result; + size_t resultlen; + int ret; + size_t j; + + if (!qcrypto_hash_supports(i)) { + continue; + } + + resultlen = expected_lens[i]; + result = g_new0(uint8_t, resultlen); + + ret = qcrypto_hash_bytes(i, + INPUT_TEXT, + strlen(INPUT_TEXT), + &result, + &resultlen, + NULL); + g_assert(ret == 0); + + g_assert(resultlen == expected_lens[i]); + for (j = 0; j < resultlen; j++) { + g_assert(expected_outputs[i][j * 2] == hex[(result[j] >> 4) & 0xf]); + g_assert(expected_outputs[i][j * 2 + 1] == hex[result[j] & 0xf]); + } + g_free(result); + } +} + + +/* Test with dynamic allocation */ +static void test_hash_iov(void) +{ + size_t i; + + for (i = 0; i < G_N_ELEMENTS(expected_outputs) ; i++) { + struct iovec iov[3] = { + { .iov_base = (char *)INPUT_TEXT1, .iov_len = strlen(INPUT_TEXT1) }, + { .iov_base = (char *)INPUT_TEXT2, .iov_len = strlen(INPUT_TEXT2) }, + { .iov_base = (char *)INPUT_TEXT3, .iov_len = strlen(INPUT_TEXT3) }, + }; + uint8_t *result = NULL; + size_t resultlen = 0; + int ret; + size_t j; + + if (!qcrypto_hash_supports(i)) { + continue; + } + + ret = qcrypto_hash_bytesv(i, + iov, 3, + &result, + &resultlen, + NULL); + g_assert(ret == 0); + g_assert(resultlen == expected_lens[i]); + for (j = 0; j < resultlen; j++) { + g_assert(expected_outputs[i][j * 2] == hex[(result[j] >> 4) & 0xf]); + g_assert(expected_outputs[i][j * 2 + 1] == hex[result[j] & 0xf]); + } + g_free(result); + } +} + + +/* Test with printable hashing */ +static void test_hash_digest(void) +{ + size_t i; + + for (i = 0; i < G_N_ELEMENTS(expected_outputs) ; i++) { + int ret; + char *digest; + size_t digestsize; + + if (!qcrypto_hash_supports(i)) { + continue; + } + + digestsize = qcrypto_hash_digest_len(i); + + g_assert_cmpint(digestsize * 2, ==, strlen(expected_outputs[i])); + + ret = qcrypto_hash_digest(i, + INPUT_TEXT, + strlen(INPUT_TEXT), + &digest, + NULL); + g_assert(ret == 0); + g_assert_cmpstr(digest, ==, expected_outputs[i]); + g_free(digest); + } +} + +/* Test with base64 encoding */ +static void test_hash_base64(void) +{ + size_t i; + + for (i = 0; i < G_N_ELEMENTS(expected_outputs) ; i++) { + int ret; + char *digest; + + if (!qcrypto_hash_supports(i)) { + continue; + } + + ret = qcrypto_hash_base64(i, + INPUT_TEXT, + strlen(INPUT_TEXT), + &digest, + NULL); + g_assert(ret == 0); + g_assert_cmpstr(digest, ==, expected_outputs_b64[i]); + g_free(digest); + } +} + +int main(int argc, char **argv) +{ + g_assert(qcrypto_init(NULL) == 0); + + g_test_init(&argc, &argv, NULL); + g_test_add_func("/crypto/hash/iov", test_hash_iov); + g_test_add_func("/crypto/hash/alloc", test_hash_alloc); + g_test_add_func("/crypto/hash/prealloc", test_hash_prealloc); + g_test_add_func("/crypto/hash/digest", test_hash_digest); + g_test_add_func("/crypto/hash/base64", test_hash_base64); + return g_test_run(); +} diff --git a/tests/unit/test-crypto-hmac.c b/tests/unit/test-crypto-hmac.c new file mode 100644 index 0000000000..ee55382a3c --- /dev/null +++ b/tests/unit/test-crypto-hmac.c @@ -0,0 +1,266 @@ +/* + * QEMU Crypto hmac algorithms tests + * + * Copyright (c) 2016 HUAWEI TECHNOLOGIES CO., LTD. + * + * Authors: + * Longpeng(Mike) + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. See the COPYING file in the + * top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "crypto/init.h" +#include "crypto/hmac.h" + +#define INPUT_TEXT1 "ABCDEFGHIJKLMNOPQRSTUVWXY" +#define INPUT_TEXT2 "Zabcdefghijklmnopqrstuvwx" +#define INPUT_TEXT3 "yz0123456789" +#define INPUT_TEXT INPUT_TEXT1 \ + INPUT_TEXT2 \ + INPUT_TEXT3 + +#define KEY "monkey monkey monkey monkey" + +typedef struct QCryptoHmacTestData QCryptoHmacTestData; +struct QCryptoHmacTestData { + QCryptoHashAlgorithm alg; + const char *hex_digest; +}; + +static QCryptoHmacTestData test_data[] = { + { + .alg = QCRYPTO_HASH_ALG_MD5, + .hex_digest = + "ede9cb83679ba82d88fbeae865b3f8fc", + }, + { + .alg = QCRYPTO_HASH_ALG_SHA1, + .hex_digest = + "c7b5a631e3aac975c4ededfcd346e469" + "dbc5f2d1", + }, + { + .alg = QCRYPTO_HASH_ALG_SHA224, + .hex_digest = + "5f768179dbb29ca722875d0f461a2e2f" + "597d0210340a84df1a8e9c63", + }, + { + .alg = QCRYPTO_HASH_ALG_SHA256, + .hex_digest = + "3798f363c57afa6edaffe39016ca7bad" + "efd1e670afb0e3987194307dec3197db", + }, + { + .alg = QCRYPTO_HASH_ALG_SHA384, + .hex_digest = + "d218680a6032d33dccd9882d6a6a7164" + "64f26623be257a9b2919b185294f4a49" + "9e54b190bfd6bc5cedd2cd05c7e65e82", + }, + { + .alg = QCRYPTO_HASH_ALG_SHA512, + .hex_digest = + "835a4f5b3750b4c1fccfa88da2f746a4" + "900160c9f18964309bb736c13b59491b" + "8e32d37b724cc5aebb0f554c6338a3b5" + "94c4ba26862b2dadb59b7ede1d08d53e", + }, + { + .alg = QCRYPTO_HASH_ALG_RIPEMD160, + .hex_digest = + "94964ed4c1155b62b668c241d67279e5" + "8a711676", + }, +}; + +static const char hex[] = "0123456789abcdef"; + +static void test_hmac_alloc(void) +{ + size_t i; + + for (i = 0; i < G_N_ELEMENTS(test_data); i++) { + QCryptoHmacTestData *data = &test_data[i]; + QCryptoHmac *hmac = NULL; + uint8_t *result = NULL; + size_t resultlen = 0; + Error *err = NULL; + const char *exp_output = NULL; + int ret; + size_t j; + + if (!qcrypto_hmac_supports(data->alg)) { + return; + } + + exp_output = data->hex_digest; + + hmac = qcrypto_hmac_new(data->alg, (const uint8_t *)KEY, + strlen(KEY), &err); + g_assert(err == NULL); + g_assert(hmac != NULL); + + ret = qcrypto_hmac_bytes(hmac, (const char *)INPUT_TEXT, + strlen(INPUT_TEXT), &result, + &resultlen, &err); + g_assert(err == NULL); + g_assert(ret == 0); + + for (j = 0; j < resultlen; j++) { + g_assert(exp_output[j * 2] == hex[(result[j] >> 4) & 0xf]); + g_assert(exp_output[j * 2 + 1] == hex[result[j] & 0xf]); + } + + qcrypto_hmac_free(hmac); + + g_free(result); + } +} + +static void test_hmac_prealloc(void) +{ + size_t i; + + for (i = 0; i < G_N_ELEMENTS(test_data); i++) { + QCryptoHmacTestData *data = &test_data[i]; + QCryptoHmac *hmac = NULL; + uint8_t *result = NULL; + size_t resultlen = 0; + Error *err = NULL; + const char *exp_output = NULL; + int ret; + size_t j; + + if (!qcrypto_hmac_supports(data->alg)) { + return; + } + + exp_output = data->hex_digest; + + resultlen = strlen(exp_output) / 2; + result = g_new0(uint8_t, resultlen); + + hmac = qcrypto_hmac_new(data->alg, (const uint8_t *)KEY, + strlen(KEY), &err); + g_assert(err == NULL); + g_assert(hmac != NULL); + + ret = qcrypto_hmac_bytes(hmac, (const char *)INPUT_TEXT, + strlen(INPUT_TEXT), &result, + &resultlen, &err); + g_assert(err == NULL); + g_assert(ret == 0); + + exp_output = data->hex_digest; + for (j = 0; j < resultlen; j++) { + g_assert(exp_output[j * 2] == hex[(result[j] >> 4) & 0xf]); + g_assert(exp_output[j * 2 + 1] == hex[result[j] & 0xf]); + } + + qcrypto_hmac_free(hmac); + + g_free(result); + } +} + +static void test_hmac_iov(void) +{ + size_t i; + + for (i = 0; i < G_N_ELEMENTS(test_data); i++) { + QCryptoHmacTestData *data = &test_data[i]; + QCryptoHmac *hmac = NULL; + uint8_t *result = NULL; + size_t resultlen = 0; + Error *err = NULL; + const char *exp_output = NULL; + int ret; + size_t j; + struct iovec iov[3] = { + { .iov_base = (char *)INPUT_TEXT1, .iov_len = strlen(INPUT_TEXT1) }, + { .iov_base = (char *)INPUT_TEXT2, .iov_len = strlen(INPUT_TEXT2) }, + { .iov_base = (char *)INPUT_TEXT3, .iov_len = strlen(INPUT_TEXT3) }, + }; + + if (!qcrypto_hmac_supports(data->alg)) { + return; + } + + exp_output = data->hex_digest; + + hmac = qcrypto_hmac_new(data->alg, (const uint8_t *)KEY, + strlen(KEY), &err); + g_assert(err == NULL); + g_assert(hmac != NULL); + + ret = qcrypto_hmac_bytesv(hmac, iov, 3, &result, + &resultlen, &err); + g_assert(err == NULL); + g_assert(ret == 0); + + for (j = 0; j < resultlen; j++) { + g_assert(exp_output[j * 2] == hex[(result[j] >> 4) & 0xf]); + g_assert(exp_output[j * 2 + 1] == hex[result[j] & 0xf]); + } + + qcrypto_hmac_free(hmac); + + g_free(result); + } +} + +static void test_hmac_digest(void) +{ + size_t i; + + for (i = 0; i < G_N_ELEMENTS(test_data); i++) { + QCryptoHmacTestData *data = &test_data[i]; + QCryptoHmac *hmac = NULL; + uint8_t *result = NULL; + Error *err = NULL; + const char *exp_output = NULL; + int ret; + + if (!qcrypto_hmac_supports(data->alg)) { + return; + } + + exp_output = data->hex_digest; + + hmac = qcrypto_hmac_new(data->alg, (const uint8_t *)KEY, + strlen(KEY), &err); + g_assert(err == NULL); + g_assert(hmac != NULL); + + ret = qcrypto_hmac_digest(hmac, (const char *)INPUT_TEXT, + strlen(INPUT_TEXT), (char **)&result, + &err); + g_assert(err == NULL); + g_assert(ret == 0); + + g_assert_cmpstr((const char *)result, ==, exp_output); + + qcrypto_hmac_free(hmac); + + g_free(result); + } +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + g_assert(qcrypto_init(NULL) == 0); + + g_test_add_func("/crypto/hmac/iov", test_hmac_iov); + g_test_add_func("/crypto/hmac/alloc", test_hmac_alloc); + g_test_add_func("/crypto/hmac/prealloc", test_hmac_prealloc); + g_test_add_func("/crypto/hmac/digest", test_hmac_digest); + + return g_test_run(); +} diff --git a/tests/unit/test-crypto-ivgen.c b/tests/unit/test-crypto-ivgen.c new file mode 100644 index 0000000000..f581e6aba7 --- /dev/null +++ b/tests/unit/test-crypto-ivgen.c @@ -0,0 +1,174 @@ +/* + * QEMU Crypto IV generator algorithms + * + * Copyright (c) 2015-2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + * + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "crypto/ivgen.h" + + +struct QCryptoIVGenTestData { + const char *path; + uint64_t sector; + QCryptoIVGenAlgorithm ivalg; + QCryptoHashAlgorithm hashalg; + QCryptoCipherAlgorithm cipheralg; + const uint8_t *key; + size_t nkey; + const uint8_t *iv; + size_t niv; +} test_data[] = { + /* Small */ + { + "/crypto/ivgen/plain/1", + .sector = 0x1, + .ivalg = QCRYPTO_IVGEN_ALG_PLAIN, + .iv = (const uint8_t *)"\x01\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .niv = 16, + }, + /* Big ! */ + { + "/crypto/ivgen/plain/1f2e3d4c", + .sector = 0x1f2e3d4cULL, + .ivalg = QCRYPTO_IVGEN_ALG_PLAIN, + .iv = (const uint8_t *)"\x4c\x3d\x2e\x1f\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .niv = 16, + }, + /* Truncation */ + { + "/crypto/ivgen/plain/1f2e3d4c5b6a7988", + .sector = 0x1f2e3d4c5b6a7988ULL, + .ivalg = QCRYPTO_IVGEN_ALG_PLAIN, + .iv = (const uint8_t *)"\x88\x79\x6a\x5b\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .niv = 16, + }, + /* Small */ + { + "/crypto/ivgen/plain64/1", + .sector = 0x1, + .ivalg = QCRYPTO_IVGEN_ALG_PLAIN64, + .iv = (const uint8_t *)"\x01\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .niv = 16, + }, + /* Big ! */ + { + "/crypto/ivgen/plain64/1f2e3d4c", + .sector = 0x1f2e3d4cULL, + .ivalg = QCRYPTO_IVGEN_ALG_PLAIN64, + .iv = (const uint8_t *)"\x4c\x3d\x2e\x1f\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .niv = 16, + }, + /* No Truncation */ + { + "/crypto/ivgen/plain64/1f2e3d4c5b6a7988", + .sector = 0x1f2e3d4c5b6a7988ULL, + .ivalg = QCRYPTO_IVGEN_ALG_PLAIN64, + .iv = (const uint8_t *)"\x88\x79\x6a\x5b\x4c\x3d\x2e\x1f" + "\x00\x00\x00\x00\x00\x00\x00\x00", + .niv = 16, + }, + /* Small */ + { + "/crypto/ivgen/essiv/1", + .sector = 0x1, + .ivalg = QCRYPTO_IVGEN_ALG_ESSIV, + .cipheralg = QCRYPTO_CIPHER_ALG_AES_128, + .hashalg = QCRYPTO_HASH_ALG_SHA256, + .key = (const uint8_t *)"\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + .nkey = 16, + .iv = (const uint8_t *)"\xd4\x83\x71\xb2\xa1\x94\x53\x88" + "\x1c\x7a\x2d\06\x2d\x0b\x65\x46", + .niv = 16, + }, + /* Big ! */ + { + "/crypto/ivgen/essiv/1f2e3d4c", + .sector = 0x1f2e3d4cULL, + .ivalg = QCRYPTO_IVGEN_ALG_ESSIV, + .cipheralg = QCRYPTO_CIPHER_ALG_AES_128, + .hashalg = QCRYPTO_HASH_ALG_SHA256, + .key = (const uint8_t *)"\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + .nkey = 16, + .iv = (const uint8_t *)"\x5d\x36\x09\x5d\xc6\x9e\x5e\xe9" + "\xe3\x02\x8d\xd8\x7a\x3d\xe7\x8f", + .niv = 16, + }, + /* No Truncation */ + { + "/crypto/ivgen/essiv/1f2e3d4c5b6a7988", + .sector = 0x1f2e3d4c5b6a7988ULL, + .ivalg = QCRYPTO_IVGEN_ALG_ESSIV, + .cipheralg = QCRYPTO_CIPHER_ALG_AES_128, + .hashalg = QCRYPTO_HASH_ALG_SHA256, + .key = (const uint8_t *)"\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + .nkey = 16, + .iv = (const uint8_t *)"\x58\xbb\x81\x94\x51\x83\x23\x23" + "\x7a\x08\x93\xa9\xdc\xd2\xd9\xab", + .niv = 16, + }, +}; + + +static void test_ivgen(const void *opaque) +{ + const struct QCryptoIVGenTestData *data = opaque; + uint8_t *iv = g_new0(uint8_t, data->niv); + QCryptoIVGen *ivgen = qcrypto_ivgen_new( + data->ivalg, + data->cipheralg, + data->hashalg, + data->key, + data->nkey, + &error_abort); + + qcrypto_ivgen_calculate(ivgen, + data->sector, + iv, + data->niv, + &error_abort); + + g_assert(memcmp(iv, data->iv, data->niv) == 0); + + qcrypto_ivgen_free(ivgen); + g_free(iv); +} + +int main(int argc, char **argv) +{ + size_t i; + g_test_init(&argc, &argv, NULL); + for (i = 0; i < G_N_ELEMENTS(test_data); i++) { + if (test_data[i].ivalg == QCRYPTO_IVGEN_ALG_ESSIV && + !qcrypto_hash_supports(test_data[i].hashalg)) { + continue; + } + g_test_add_data_func(test_data[i].path, + &(test_data[i]), + test_ivgen); + } + return g_test_run(); +} diff --git a/tests/unit/test-crypto-pbkdf.c b/tests/unit/test-crypto-pbkdf.c new file mode 100644 index 0000000000..c50fd639d2 --- /dev/null +++ b/tests/unit/test-crypto-pbkdf.c @@ -0,0 +1,446 @@ +/* + * QEMU Crypto cipher algorithms + * + * Copyright (c) 2015-2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + * + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "crypto/init.h" +#ifndef _WIN32 +#include +#endif + +#if ((defined(CONFIG_NETTLE) || defined(CONFIG_GCRYPT)) && \ + (defined(_WIN32) || defined(RUSAGE_THREAD))) +#include "crypto/pbkdf.h" + +typedef struct QCryptoPbkdfTestData QCryptoPbkdfTestData; +struct QCryptoPbkdfTestData { + const char *path; + QCryptoHashAlgorithm hash; + unsigned int iterations; + const char *key; + size_t nkey; + const char *salt; + size_t nsalt; + const char *out; + size_t nout; + bool slow; +}; + +/* This test data comes from cryptsetup package + * + * $SRC/lib/crypto_backend/pbkdf2_generic.c + * + * under LGPLv2.1+ license + */ +static QCryptoPbkdfTestData test_data[] = { + /* RFC 3962 test data */ + { + .path = "/crypto/pbkdf/rfc3962/sha1/iter1", + .hash = QCRYPTO_HASH_ALG_SHA1, + .iterations = 1, + .key = "password", + .nkey = 8, + .salt = "ATHENA.MIT.EDUraeburn", + .nsalt = 21, + .out = "\xcd\xed\xb5\x28\x1b\xb2\xf8\x01" + "\x56\x5a\x11\x22\xb2\x56\x35\x15" + "\x0a\xd1\xf7\xa0\x4b\xb9\xf3\xa3" + "\x33\xec\xc0\xe2\xe1\xf7\x08\x37", + .nout = 32 + }, + { + .path = "/crypto/pbkdf/rfc3962/sha1/iter2", + .hash = QCRYPTO_HASH_ALG_SHA1, + .iterations = 2, + .key = "password", + .nkey = 8, + .salt = "ATHENA.MIT.EDUraeburn", + .nsalt = 21, + .out = "\x01\xdb\xee\x7f\x4a\x9e\x24\x3e" + "\x98\x8b\x62\xc7\x3c\xda\x93\x5d" + "\xa0\x53\x78\xb9\x32\x44\xec\x8f" + "\x48\xa9\x9e\x61\xad\x79\x9d\x86", + .nout = 32 + }, + { + .path = "/crypto/pbkdf/rfc3962/sha1/iter1200a", + .hash = QCRYPTO_HASH_ALG_SHA1, + .iterations = 1200, + .key = "password", + .nkey = 8, + .salt = "ATHENA.MIT.EDUraeburn", + .nsalt = 21, + .out = "\x5c\x08\xeb\x61\xfd\xf7\x1e\x4e" + "\x4e\xc3\xcf\x6b\xa1\xf5\x51\x2b" + "\xa7\xe5\x2d\xdb\xc5\xe5\x14\x2f" + "\x70\x8a\x31\xe2\xe6\x2b\x1e\x13", + .nout = 32 + }, + { + .path = "/crypto/pbkdf/rfc3962/sha1/iter5", + .hash = QCRYPTO_HASH_ALG_SHA1, + .iterations = 5, + .key = "password", + .nkey = 8, + .salt = "\0224VxxV4\022", /* "\x1234567878563412 */ + .nsalt = 8, + .out = "\xd1\xda\xa7\x86\x15\xf2\x87\xe6" + "\xa1\xc8\xb1\x20\xd7\x06\x2a\x49" + "\x3f\x98\xd2\x03\xe6\xbe\x49\xa6" + "\xad\xf4\xfa\x57\x4b\x6e\x64\xee", + .nout = 32 + }, + { + .path = "/crypto/pbkdf/rfc3962/sha1/iter1200b", + .hash = QCRYPTO_HASH_ALG_SHA1, + .iterations = 1200, + .key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + .nkey = 64, + .salt = "pass phrase equals block size", + .nsalt = 29, + .out = "\x13\x9c\x30\xc0\x96\x6b\xc3\x2b" + "\xa5\x5f\xdb\xf2\x12\x53\x0a\xc9" + "\xc5\xec\x59\xf1\xa4\x52\xf5\xcc" + "\x9a\xd9\x40\xfe\xa0\x59\x8e\xd1", + .nout = 32 + }, + { + .path = "/crypto/pbkdf/rfc3962/sha1/iter1200c", + .hash = QCRYPTO_HASH_ALG_SHA1, + .iterations = 1200, + .key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + .nkey = 65, + .salt = "pass phrase exceeds block size", + .nsalt = 30, + .out = "\x9c\xca\xd6\xd4\x68\x77\x0c\xd5" + "\x1b\x10\xe6\xa6\x87\x21\xbe\x61" + "\x1a\x8b\x4d\x28\x26\x01\xdb\x3b" + "\x36\xbe\x92\x46\x91\x5e\xc8\x2a", + .nout = 32 + }, + { + .path = "/crypto/pbkdf/rfc3962/sha1/iter50", + .hash = QCRYPTO_HASH_ALG_SHA1, + .iterations = 50, + .key = "\360\235\204\236", /* g-clef ("\xf09d849e) */ + .nkey = 4, + .salt = "EXAMPLE.COMpianist", + .nsalt = 18, + .out = "\x6b\x9c\xf2\x6d\x45\x45\x5a\x43" + "\xa5\xb8\xbb\x27\x6a\x40\x3b\x39" + "\xe7\xfe\x37\xa0\xc4\x1e\x02\xc2" + "\x81\xff\x30\x69\xe1\xe9\x4f\x52", + .nout = 32 + }, + + /* RFC-6070 test data */ + { + .path = "/crypto/pbkdf/rfc6070/sha1/iter1", + .hash = QCRYPTO_HASH_ALG_SHA1, + .iterations = 1, + .key = "password", + .nkey = 8, + .salt = "salt", + .nsalt = 4, + .out = "\x0c\x60\xc8\x0f\x96\x1f\x0e\x71\xf3\xa9" + "\xb5\x24\xaf\x60\x12\x06\x2f\xe0\x37\xa6", + .nout = 20 + }, + { + .path = "/crypto/pbkdf/rfc6070/sha1/iter2", + .hash = QCRYPTO_HASH_ALG_SHA1, + .iterations = 2, + .key = "password", + .nkey = 8, + .salt = "salt", + .nsalt = 4, + .out = "\xea\x6c\x01\x4d\xc7\x2d\x6f\x8c\xcd\x1e" + "\xd9\x2a\xce\x1d\x41\xf0\xd8\xde\x89\x57", + .nout = 20 + }, + { + .path = "/crypto/pbkdf/rfc6070/sha1/iter4096", + .hash = QCRYPTO_HASH_ALG_SHA1, + .iterations = 4096, + .key = "password", + .nkey = 8, + .salt = "salt", + .nsalt = 4, + .out = "\x4b\x00\x79\x01\xb7\x65\x48\x9a\xbe\xad" + "\x49\xd9\x26\xf7\x21\xd0\x65\xa4\x29\xc1", + .nout = 20 + }, + { + .path = "/crypto/pbkdf/rfc6070/sha1/iter16777216", + .hash = QCRYPTO_HASH_ALG_SHA1, + .iterations = 16777216, + .key = "password", + .nkey = 8, + .salt = "salt", + .nsalt = 4, + .out = "\xee\xfe\x3d\x61\xcd\x4d\xa4\xe4\xe9\x94" + "\x5b\x3d\x6b\xa2\x15\x8c\x26\x34\xe9\x84", + .nout = 20, + .slow = true, + }, + { + .path = "/crypto/pbkdf/rfc6070/sha1/iter4096a", + .hash = QCRYPTO_HASH_ALG_SHA1, + .iterations = 4096, + .key = "passwordPASSWORDpassword", + .nkey = 24, + .salt = "saltSALTsaltSALTsaltSALTsaltSALTsalt", + .nsalt = 36, + .out = "\x3d\x2e\xec\x4f\xe4\x1c\x84\x9b\x80\xc8" + "\xd8\x36\x62\xc0\xe4\x4a\x8b\x29\x1a\x96" + "\x4c\xf2\xf0\x70\x38", + .nout = 25 + }, + { + .path = "/crypto/pbkdf/rfc6070/sha1/iter4096b", + .hash = QCRYPTO_HASH_ALG_SHA1, + .iterations = 4096, + .key = "pass\0word", + .nkey = 9, + .salt = "sa\0lt", + .nsalt = 5, + .out = "\x56\xfa\x6a\xa7\x55\x48\x09\x9d\xcc\x37" + "\xd7\xf0\x34\x25\xe0\xc3", + .nout = 16 + }, + + /* non-RFC misc test data */ +#ifdef CONFIG_NETTLE + { + /* empty password test. + * Broken with libgcrypt <= 1.5.0, hence CONFIG_NETTLE */ + .path = "/crypto/pbkdf/nonrfc/sha1/iter2", + .hash = QCRYPTO_HASH_ALG_SHA1, + .iterations = 2, + .key = "", + .nkey = 0, + .salt = "salt", + .nsalt = 4, + .out = "\x13\x3a\x4c\xe8\x37\xb4\xd2\x52\x1e\xe2" + "\xbf\x03\xe1\x1c\x71\xca\x79\x4e\x07\x97", + .nout = 20 + }, +#endif + { + /* Password exceeds block size test */ + .path = "/crypto/pbkdf/nonrfc/sha256/iter1200", + .hash = QCRYPTO_HASH_ALG_SHA256, + .iterations = 1200, + .key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + .nkey = 65, + .salt = "pass phrase exceeds block size", + .nsalt = 30, + .out = "\x22\x34\x4b\xc4\xb6\xe3\x26\x75" + "\xa8\x09\x0f\x3e\xa8\x0b\xe0\x1d" + "\x5f\x95\x12\x6a\x2c\xdd\xc3\xfa" + "\xcc\x4a\x5e\x6d\xca\x04\xec\x58", + .nout = 32 + }, + { + .path = "/crypto/pbkdf/nonrfc/sha512/iter1200", + .hash = QCRYPTO_HASH_ALG_SHA512, + .iterations = 1200, + .key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + .nkey = 129, + .salt = "pass phrase exceeds block size", + .nsalt = 30, + .out = "\x0f\xb2\xed\x2c\x0e\x6e\xfb\x7d" + "\x7d\x8e\xdd\x58\x01\xb4\x59\x72" + "\x99\x92\x16\x30\x5e\xa4\x36\x8d" + "\x76\x14\x80\xf3\xe3\x7a\x22\xb9", + .nout = 32 + }, + { + .path = "/crypto/pbkdf/nonrfc/sha224/iter1200", + .hash = QCRYPTO_HASH_ALG_SHA224, + .iterations = 1200, + .key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + .nkey = 129, + .salt = "pass phrase exceeds block size", + .nsalt = 30, + .out = "\x13\x3b\x88\x0c\x0e\x52\xa2\x41" + "\x49\x33\x35\xa6\xc3\x83\xae\x23" + "\xf6\x77\x43\x9e\x5b\x30\x92\x3e" + "\x4a\x3a\xaa\x24\x69\x3c\xed\x20", + .nout = 32 + }, + { + .path = "/crypto/pbkdf/nonrfc/sha384/iter1200", + .hash = QCRYPTO_HASH_ALG_SHA384, + .iterations = 1200, + .key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + .nkey = 129, + .salt = "pass phrase exceeds block size", + .nsalt = 30, + .out = "\xfe\xe3\xe1\x84\xc9\x25\x3e\x10" + "\x47\xc8\x7d\x53\xc6\xa5\xe3\x77" + "\x29\x41\x76\xbd\x4b\xe3\x9b\xac" + "\x05\x6c\x11\xdd\x17\xc5\x93\x80", + .nout = 32 + }, + { + .path = "/crypto/pbkdf/nonrfc/ripemd160/iter1200", + .hash = QCRYPTO_HASH_ALG_RIPEMD160, + .iterations = 1200, + .key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + .nkey = 129, + .salt = "pass phrase exceeds block size", + .nsalt = 30, + .out = "\xd6\xcb\xd8\xa7\xdb\x0c\xa2\x2a" + "\x23\x5e\x47\xaf\xdb\xda\xa8\xef" + "\xe4\x01\x0d\x6f\xb5\x33\xc8\xbd" + "\xce\xbf\x91\x14\x8b\x5c\x48\x41", + .nout = 32 + }, +#if 0 + { + .path = "/crypto/pbkdf/nonrfc/whirlpool/iter1200", + .hash = QCRYPTO_HASH_ALG_WHIRLPOOL, + .iterations = 1200, + .key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + .nkey = 65, + .salt = "pass phrase exceeds block size", + .nsalt = 30, + .out = "\x9c\x1c\x74\xf5\x88\x26\xe7\x6a" + "\x53\x58\xf4\x0c\x39\xe7\x80\x89" + "\x07\xc0\x31\x19\x9a\x50\xa2\x48" + "\xf1\xd9\xfe\x78\x64\xe5\x84\x50", + .nout = 32 + } +#endif +}; + + +static inline char hex(int i) +{ + if (i < 10) { + return '0' + i; + } + return 'a' + (i - 10); +} + +static char *hex_string(const uint8_t *bytes, + size_t len) +{ + char *hexstr = g_new0(char, len * 2 + 1); + size_t i; + + for (i = 0; i < len; i++) { + hexstr[i * 2] = hex((bytes[i] >> 4) & 0xf); + hexstr[i * 2 + 1] = hex(bytes[i] & 0xf); + } + hexstr[len * 2] = '\0'; + + return hexstr; +} + +static void test_pbkdf(const void *opaque) +{ + const QCryptoPbkdfTestData *data = opaque; + size_t nout = data->nout; + uint8_t *out = g_new0(uint8_t, nout); + gchar *expect, *actual; + + qcrypto_pbkdf2(data->hash, + (uint8_t *)data->key, data->nkey, + (uint8_t *)data->salt, data->nsalt, + data->iterations, + (uint8_t *)out, nout, + &error_abort); + + expect = hex_string((const uint8_t *)data->out, data->nout); + actual = hex_string(out, nout); + + g_assert_cmpstr(actual, ==, expect); + + g_free(actual); + g_free(expect); + g_free(out); +} + + +static void test_pbkdf_timing(void) +{ + uint8_t key[32]; + uint8_t salt[32]; + int iters; + + memset(key, 0x5d, sizeof(key)); + memset(salt, 0x7c, sizeof(salt)); + + iters = qcrypto_pbkdf2_count_iters(QCRYPTO_HASH_ALG_SHA256, + key, sizeof(key), + salt, sizeof(salt), + 32, + &error_abort); + + g_assert(iters >= (1 << 15)); +} + + +int main(int argc, char **argv) +{ + size_t i; + + g_test_init(&argc, &argv, NULL); + + g_assert(qcrypto_init(NULL) == 0); + + for (i = 0; i < G_N_ELEMENTS(test_data); i++) { + if (!test_data[i].slow || + g_test_slow()) { + g_test_add_data_func(test_data[i].path, &test_data[i], test_pbkdf); + } + } + + if (g_test_slow()) { + g_test_add_func("/crypt0/pbkdf/timing", test_pbkdf_timing); + } + + return g_test_run(); +} +#else +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + return g_test_run(); +} +#endif diff --git a/tests/unit/test-crypto-secret.c b/tests/unit/test-crypto-secret.c new file mode 100644 index 0000000000..34a4aecc12 --- /dev/null +++ b/tests/unit/test-crypto-secret.c @@ -0,0 +1,614 @@ +/* + * QEMU Crypto secret handling + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + * + */ + +#include "qemu/osdep.h" + +#include "crypto/init.h" +#include "crypto/secret.h" +#include "qapi/error.h" +#include "qemu/module.h" +#ifdef CONFIG_KEYUTILS +#include "crypto/secret_keyring.h" +#include +#endif + +static void test_secret_direct(void) +{ + Object *sec = object_new_with_props( + TYPE_QCRYPTO_SECRET, + object_get_objects_root(), + "sec0", + &error_abort, + "data", "123456", + NULL); + + char *pw = qcrypto_secret_lookup_as_utf8("sec0", + &error_abort); + + g_assert_cmpstr(pw, ==, "123456"); + + object_unparent(sec); + g_free(pw); +} + + +static void test_secret_indirect_good(void) +{ + Object *sec; + char *fname = NULL; + int fd = g_file_open_tmp("qemu-test-crypto-secret-XXXXXX", + &fname, + NULL); + + g_assert(fd >= 0); + g_assert_nonnull(fname); + + g_assert(write(fd, "123456", 6) == 6); + + sec = object_new_with_props( + TYPE_QCRYPTO_SECRET, + object_get_objects_root(), + "sec0", + &error_abort, + "file", fname, + NULL); + + char *pw = qcrypto_secret_lookup_as_utf8("sec0", + &error_abort); + + g_assert_cmpstr(pw, ==, "123456"); + + object_unparent(sec); + g_free(pw); + close(fd); + unlink(fname); + g_free(fname); +} + + +static void test_secret_indirect_badfile(void) +{ + Object *sec = object_new_with_props( + TYPE_QCRYPTO_SECRET, + object_get_objects_root(), + "sec0", + NULL, + "file", "does-not-exist", + NULL); + + g_assert(sec == NULL); +} + + +static void test_secret_indirect_emptyfile(void) +{ + Object *sec; + char *fname = NULL; + int fd = g_file_open_tmp("qemu-test-crypto-secretXXXXXX", + &fname, + NULL); + + g_assert(fd >= 0); + g_assert_nonnull(fname); + + sec = object_new_with_props( + TYPE_QCRYPTO_SECRET, + object_get_objects_root(), + "sec0", + &error_abort, + "file", fname, + NULL); + + char *pw = qcrypto_secret_lookup_as_utf8("sec0", + &error_abort); + + g_assert_cmpstr(pw, ==, ""); + + object_unparent(sec); + g_free(pw); + close(fd); + unlink(fname); + g_free(fname); +} + +#ifdef CONFIG_KEYUTILS + +#define DESCRIPTION "qemu_test_secret" +#define PAYLOAD "Test Payload" + + +static void test_secret_keyring_good(void) +{ + char key_str[16]; + Object *sec; + int32_t key = add_key("user", DESCRIPTION, PAYLOAD, + strlen(PAYLOAD), KEY_SPEC_PROCESS_KEYRING); + + g_assert(key >= 0); + + snprintf(key_str, sizeof(key_str), "0x%08x", key); + sec = object_new_with_props( + TYPE_QCRYPTO_SECRET_KEYRING, + object_get_objects_root(), + "sec0", + &error_abort, + "serial", key_str, + NULL); + + assert(0 <= keyctl_unlink(key, KEY_SPEC_PROCESS_KEYRING)); + char *pw = qcrypto_secret_lookup_as_utf8("sec0", + &error_abort); + g_assert_cmpstr(pw, ==, PAYLOAD); + + object_unparent(sec); + g_free(pw); +} + + +static void test_secret_keyring_revoked_key(void) +{ + char key_str[16]; + Object *sec; + int32_t key = add_key("user", DESCRIPTION, PAYLOAD, + strlen(PAYLOAD), KEY_SPEC_PROCESS_KEYRING); + g_assert(key >= 0); + g_assert_false(keyctl_revoke(key)); + + snprintf(key_str, sizeof(key_str), "0x%08x", key); + sec = object_new_with_props( + TYPE_QCRYPTO_SECRET_KEYRING, + object_get_objects_root(), + "sec0", + NULL, + "serial", key_str, + NULL); + + g_assert(errno == EKEYREVOKED); + g_assert(sec == NULL); + + keyctl_unlink(key, KEY_SPEC_PROCESS_KEYRING); +} + + +static void test_secret_keyring_expired_key(void) +{ + char key_str[16]; + Object *sec; + int32_t key = add_key("user", DESCRIPTION, PAYLOAD, + strlen(PAYLOAD), KEY_SPEC_PROCESS_KEYRING); + g_assert(key >= 0); + g_assert_false(keyctl_set_timeout(key, 1)); + sleep(1); + + snprintf(key_str, sizeof(key_str), "0x%08x", key); + sec = object_new_with_props( + TYPE_QCRYPTO_SECRET_KEYRING, + object_get_objects_root(), + "sec0", + NULL, + "serial", key_str, + NULL); + + g_assert(errno == EKEYEXPIRED); + g_assert(sec == NULL); + + keyctl_unlink(key, KEY_SPEC_PROCESS_KEYRING); +} + + +static void test_secret_keyring_bad_serial_key(void) +{ + Object *sec; + + sec = object_new_with_props( + TYPE_QCRYPTO_SECRET_KEYRING, + object_get_objects_root(), + "sec0", + NULL, + "serial", "1", + NULL); + + g_assert(errno == ENOKEY); + g_assert(sec == NULL); +} + +/* + * TODO + * test_secret_keyring_bad_key_access_right() is not working yet. + * We don't know yet if this due a bug in the Linux kernel or + * whether it's normal syscall behavior. + * We've requested information from kernel maintainers. + * See: + * Thread: 'security/keys: remove possessor verify after key permission check' + */ + +static void test_secret_keyring_bad_key_access_right(void) +{ + char key_str[16]; + Object *sec; + + g_test_skip("TODO: Need responce from Linux kernel maintainers"); + return; + + int32_t key = add_key("user", DESCRIPTION, PAYLOAD, + strlen(PAYLOAD), KEY_SPEC_PROCESS_KEYRING); + g_assert(key >= 0); + g_assert_false(keyctl_setperm(key, KEY_POS_ALL & (~KEY_POS_READ))); + + snprintf(key_str, sizeof(key_str), "0x%08x", key); + + sec = object_new_with_props( + TYPE_QCRYPTO_SECRET_KEYRING, + object_get_objects_root(), + "sec0", + NULL, + "serial", key_str, + NULL); + + g_assert(errno == EACCES); + g_assert(sec == NULL); + + keyctl_unlink(key, KEY_SPEC_PROCESS_KEYRING); +} + +#endif /* CONFIG_KEYUTILS */ + +static void test_secret_noconv_base64_good(void) +{ + Object *sec = object_new_with_props( + TYPE_QCRYPTO_SECRET, + object_get_objects_root(), + "sec0", + &error_abort, + "data", "MTIzNDU2", + "format", "base64", + NULL); + + char *pw = qcrypto_secret_lookup_as_base64("sec0", + &error_abort); + + g_assert_cmpstr(pw, ==, "MTIzNDU2"); + + object_unparent(sec); + g_free(pw); +} + + +static void test_secret_noconv_base64_bad(void) +{ + Object *sec = object_new_with_props( + TYPE_QCRYPTO_SECRET, + object_get_objects_root(), + "sec0", + NULL, + "data", "MTI$NDU2", + "format", "base64", + NULL); + + g_assert(sec == NULL); +} + + +static void test_secret_noconv_utf8(void) +{ + Object *sec = object_new_with_props( + TYPE_QCRYPTO_SECRET, + object_get_objects_root(), + "sec0", + &error_abort, + "data", "123456", + "format", "raw", + NULL); + + char *pw = qcrypto_secret_lookup_as_utf8("sec0", + &error_abort); + + g_assert_cmpstr(pw, ==, "123456"); + + object_unparent(sec); + g_free(pw); +} + + +static void test_secret_conv_base64_utf8valid(void) +{ + Object *sec = object_new_with_props( + TYPE_QCRYPTO_SECRET, + object_get_objects_root(), + "sec0", + &error_abort, + "data", "MTIzNDU2", + "format", "base64", + NULL); + + char *pw = qcrypto_secret_lookup_as_utf8("sec0", + &error_abort); + + g_assert_cmpstr(pw, ==, "123456"); + + object_unparent(sec); + g_free(pw); +} + + +static void test_secret_conv_base64_utf8invalid(void) +{ + Object *sec = object_new_with_props( + TYPE_QCRYPTO_SECRET, + object_get_objects_root(), + "sec0", + &error_abort, + "data", "f0VMRgIBAQAAAA==", + "format", "base64", + NULL); + + char *pw = qcrypto_secret_lookup_as_utf8("sec0", + NULL); + g_assert(pw == NULL); + + object_unparent(sec); +} + + +static void test_secret_conv_utf8_base64(void) +{ + Object *sec = object_new_with_props( + TYPE_QCRYPTO_SECRET, + object_get_objects_root(), + "sec0", + &error_abort, + "data", "123456", + NULL); + + char *pw = qcrypto_secret_lookup_as_base64("sec0", + &error_abort); + + g_assert_cmpstr(pw, ==, "MTIzNDU2"); + + object_unparent(sec); + g_free(pw); +} + + +static void test_secret_crypt_raw(void) +{ + Object *master = object_new_with_props( + TYPE_QCRYPTO_SECRET, + object_get_objects_root(), + "master", + &error_abort, + "data", "9miloPQCzGy+TL6aonfzVcptibCmCIhKzrnlfwiWivk=", + "format", "base64", + NULL); + Object *sec = object_new_with_props( + TYPE_QCRYPTO_SECRET, + object_get_objects_root(), + "sec0", + &error_abort, + "data", + "\xCC\xBF\xF7\x09\x46\x19\x0B\x52\x2A\x3A\xB4\x6B\xCD\x7A\xB0\xB0", + "format", "raw", + "keyid", "master", + "iv", "0I7Gw/TKuA+Old2W2apQ3g==", + NULL); + + char *pw = qcrypto_secret_lookup_as_utf8("sec0", + &error_abort); + + g_assert_cmpstr(pw, ==, "123456"); + + object_unparent(sec); + object_unparent(master); + g_free(pw); +} + + +static void test_secret_crypt_base64(void) +{ + Object *master = object_new_with_props( + TYPE_QCRYPTO_SECRET, + object_get_objects_root(), + "master", + &error_abort, + "data", "9miloPQCzGy+TL6aonfzVcptibCmCIhKzrnlfwiWivk=", + "format", "base64", + NULL); + Object *sec = object_new_with_props( + TYPE_QCRYPTO_SECRET, + object_get_objects_root(), + "sec0", + &error_abort, + "data", "zL/3CUYZC1IqOrRrzXqwsA==", + "format", "base64", + "keyid", "master", + "iv", "0I7Gw/TKuA+Old2W2apQ3g==", + NULL); + + char *pw = qcrypto_secret_lookup_as_utf8("sec0", + &error_abort); + + g_assert_cmpstr(pw, ==, "123456"); + + object_unparent(sec); + object_unparent(master); + g_free(pw); +} + + +static void test_secret_crypt_short_key(void) +{ + Object *master = object_new_with_props( + TYPE_QCRYPTO_SECRET, + object_get_objects_root(), + "master", + &error_abort, + "data", "9miloPQCzGy+TL6aonfzVc", + "format", "base64", + NULL); + Object *sec = object_new_with_props( + TYPE_QCRYPTO_SECRET, + object_get_objects_root(), + "sec0", + NULL, + "data", "zL/3CUYZC1IqOrRrzXqwsA==", + "format", "raw", + "keyid", "master", + "iv", "0I7Gw/TKuA+Old2W2apQ3g==", + NULL); + + g_assert(sec == NULL); + object_unparent(master); +} + + +static void test_secret_crypt_short_iv(void) +{ + Object *master = object_new_with_props( + TYPE_QCRYPTO_SECRET, + object_get_objects_root(), + "master", + &error_abort, + "data", "9miloPQCzGy+TL6aonfzVcptibCmCIhKzrnlfwiWivk=", + "format", "base64", + NULL); + Object *sec = object_new_with_props( + TYPE_QCRYPTO_SECRET, + object_get_objects_root(), + "sec0", + NULL, + "data", "zL/3CUYZC1IqOrRrzXqwsA==", + "format", "raw", + "keyid", "master", + "iv", "0I7Gw/TKuA+Old2W2a", + NULL); + + g_assert(sec == NULL); + object_unparent(master); +} + + +static void test_secret_crypt_missing_iv(void) +{ + Object *master = object_new_with_props( + TYPE_QCRYPTO_SECRET, + object_get_objects_root(), + "master", + &error_abort, + "data", "9miloPQCzGy+TL6aonfzVcptibCmCIhKzrnlfwiWivk=", + "format", "base64", + NULL); + Object *sec = object_new_with_props( + TYPE_QCRYPTO_SECRET, + object_get_objects_root(), + "sec0", + NULL, + "data", "zL/3CUYZC1IqOrRrzXqwsA==", + "format", "raw", + "keyid", "master", + NULL); + + g_assert(sec == NULL); + object_unparent(master); +} + + +static void test_secret_crypt_bad_iv(void) +{ + Object *master = object_new_with_props( + TYPE_QCRYPTO_SECRET, + object_get_objects_root(), + "master", + &error_abort, + "data", "9miloPQCzGy+TL6aonfzVcptibCmCIhKzrnlfwiWivk=", + "format", "base64", + NULL); + Object *sec = object_new_with_props( + TYPE_QCRYPTO_SECRET, + object_get_objects_root(), + "sec0", + NULL, + "data", "zL/3CUYZC1IqOrRrzXqwsA==", + "format", "raw", + "keyid", "master", + "iv", "0I7Gw/TK$$uA+Old2W2a", + NULL); + + g_assert(sec == NULL); + object_unparent(master); +} + + +int main(int argc, char **argv) +{ + module_call_init(MODULE_INIT_QOM); + g_test_init(&argc, &argv, NULL); + + g_assert(qcrypto_init(NULL) == 0); + + g_test_add_func("/crypto/secret/direct", + test_secret_direct); + g_test_add_func("/crypto/secret/indirect/good", + test_secret_indirect_good); + g_test_add_func("/crypto/secret/indirect/badfile", + test_secret_indirect_badfile); + g_test_add_func("/crypto/secret/indirect/emptyfile", + test_secret_indirect_emptyfile); + +#ifdef CONFIG_KEYUTILS + g_test_add_func("/crypto/secret/keyring/good", + test_secret_keyring_good); + g_test_add_func("/crypto/secret/keyring/revoked_key", + test_secret_keyring_revoked_key); + g_test_add_func("/crypto/secret/keyring/expired_key", + test_secret_keyring_expired_key); + g_test_add_func("/crypto/secret/keyring/bad_serial_key", + test_secret_keyring_bad_serial_key); + g_test_add_func("/crypto/secret/keyring/bad_key_access_right", + test_secret_keyring_bad_key_access_right); +#endif /* CONFIG_KEYUTILS */ + + g_test_add_func("/crypto/secret/noconv/base64/good", + test_secret_noconv_base64_good); + g_test_add_func("/crypto/secret/noconv/base64/bad", + test_secret_noconv_base64_bad); + g_test_add_func("/crypto/secret/noconv/utf8", + test_secret_noconv_utf8); + g_test_add_func("/crypto/secret/conv/base64/utf8valid", + test_secret_conv_base64_utf8valid); + g_test_add_func("/crypto/secret/conv/base64/utf8invalid", + test_secret_conv_base64_utf8invalid); + g_test_add_func("/crypto/secret/conv/utf8/base64", + test_secret_conv_utf8_base64); + + g_test_add_func("/crypto/secret/crypt/raw", + test_secret_crypt_raw); + g_test_add_func("/crypto/secret/crypt/base64", + test_secret_crypt_base64); + g_test_add_func("/crypto/secret/crypt/shortkey", + test_secret_crypt_short_key); + g_test_add_func("/crypto/secret/crypt/shortiv", + test_secret_crypt_short_iv); + g_test_add_func("/crypto/secret/crypt/missingiv", + test_secret_crypt_missing_iv); + g_test_add_func("/crypto/secret/crypt/badiv", + test_secret_crypt_bad_iv); + + return g_test_run(); +} diff --git a/tests/unit/test-crypto-tlscredsx509.c b/tests/unit/test-crypto-tlscredsx509.c new file mode 100644 index 0000000000..f487349c32 --- /dev/null +++ b/tests/unit/test-crypto-tlscredsx509.c @@ -0,0 +1,718 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + * + * Author: Daniel P. Berrange + */ + +#include "qemu/osdep.h" + +#include "crypto-tls-x509-helpers.h" +#include "crypto/tlscredsx509.h" +#include "qapi/error.h" +#include "qemu/module.h" + +#ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT + +#define WORKDIR "tests/test-crypto-tlscredsx509-work/" +#define KEYFILE WORKDIR "key-ctx.pem" + +struct QCryptoTLSCredsTestData { + bool isServer; + const char *cacrt; + const char *crt; + bool expectFail; +}; + + +static QCryptoTLSCreds *test_tls_creds_create(QCryptoTLSCredsEndpoint endpoint, + const char *certdir, + Error **errp) +{ + Object *parent = object_get_objects_root(); + Object *creds = object_new_with_props( + TYPE_QCRYPTO_TLS_CREDS_X509, + parent, + "testtlscreds", + errp, + "endpoint", (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ? + "server" : "client"), + "dir", certdir, + "verify-peer", "yes", + "sanity-check", "yes", + NULL); + + if (!creds) { + return NULL; + } + return QCRYPTO_TLS_CREDS(creds); +} + +/* + * This tests sanity checking of our own certificates + * + * The code being tested is used when TLS creds are created, + * and aim to ensure QMEU has been configured with sane + * certificates. This allows us to give much much much + * clearer error messages to the admin when they misconfigure + * things. + */ +static void test_tls_creds(const void *opaque) +{ + struct QCryptoTLSCredsTestData *data = + (struct QCryptoTLSCredsTestData *)opaque; + QCryptoTLSCreds *creds; + +#define CERT_DIR "tests/test-crypto-tlscredsx509-certs/" + mkdir(CERT_DIR, 0700); + + unlink(CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT); + if (data->isServer) { + unlink(CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT); + unlink(CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY); + } else { + unlink(CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT); + unlink(CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY); + } + + if (access(data->cacrt, R_OK) == 0) { + g_assert(link(data->cacrt, + CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT) == 0); + } + if (data->isServer) { + if (access(data->crt, R_OK) == 0) { + g_assert(link(data->crt, + CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT) == 0); + } + g_assert(link(KEYFILE, + CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY) == 0); + } else { + if (access(data->crt, R_OK) == 0) { + g_assert(link(data->crt, + CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT) == 0); + } + g_assert(link(KEYFILE, + CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY) == 0); + } + + creds = test_tls_creds_create( + (data->isServer ? + QCRYPTO_TLS_CREDS_ENDPOINT_SERVER : + QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT), + CERT_DIR, + data->expectFail ? NULL : &error_abort); + + if (data->expectFail) { + g_assert(creds == NULL); + } else { + g_assert(creds != NULL); + } + + unlink(CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT); + if (data->isServer) { + unlink(CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT); + unlink(CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY); + } else { + unlink(CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT); + unlink(CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY); + } + rmdir(CERT_DIR); + if (creds) { + object_unparent(OBJECT(creds)); + } +} + +int main(int argc, char **argv) +{ + int ret; + + module_call_init(MODULE_INIT_QOM); + g_test_init(&argc, &argv, NULL); + g_setenv("GNUTLS_FORCE_FIPS_MODE", "2", 1); + + mkdir(WORKDIR, 0700); + + test_tls_init(KEYFILE); + +# define TLS_TEST_REG(name, isServer, caCrt, crt, expectFail) \ + struct QCryptoTLSCredsTestData name = { \ + isServer, caCrt, crt, expectFail \ + }; \ + g_test_add_data_func("/qcrypto/tlscredsx509/" # name, \ + &name, test_tls_creds); \ + + /* A perfect CA, perfect client & perfect server */ + + /* Basic:CA:critical */ + TLS_ROOT_REQ(cacertreq, + "UK", "qemu CA", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + + TLS_CERT_REQ(servercertreq, cacertreq, + "UK", "qemu.org", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); + TLS_CERT_REQ(clientcertreq, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL, + 0, 0); + + TLS_TEST_REG(perfectserver, true, + cacertreq.filename, servercertreq.filename, false); + TLS_TEST_REG(perfectclient, false, + cacertreq.filename, clientcertreq.filename, false); + + + /* Some other CAs which are good */ + + /* Basic:CA:critical */ + TLS_ROOT_REQ(cacert1req, + "UK", "qemu CA 1", NULL, NULL, NULL, NULL, + true, true, true, + false, false, 0, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(servercert1req, cacert1req, + "UK", "qemu.org", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); + + /* Basic:CA:not-critical */ + TLS_ROOT_REQ(cacert2req, + "UK", "qemu CA 2", NULL, NULL, NULL, NULL, + true, false, true, + false, false, 0, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(servercert2req, cacert2req, + "UK", "qemu.org", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); + + /* Key usage:cert-sign:critical */ + TLS_ROOT_REQ(cacert3req, + "UK", "qemu CA 3", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(servercert3req, cacert3req, + "UK", "qemu.org", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); + + TLS_TEST_REG(goodca1, true, + cacert1req.filename, servercert1req.filename, false); + TLS_TEST_REG(goodca2, true, + cacert2req.filename, servercert2req.filename, false); + TLS_TEST_REG(goodca3, true, + cacert3req.filename, servercert3req.filename, false); + + /* Now some bad certs */ + + /* Key usage:dig-sig:not-critical */ + TLS_ROOT_REQ(cacert4req, + "UK", "qemu CA 4", NULL, NULL, NULL, NULL, + true, true, true, + true, false, GNUTLS_KEY_DIGITAL_SIGNATURE, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(servercert4req, cacert4req, + "UK", "qemu.org", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); + /* no-basic */ + TLS_ROOT_REQ(cacert5req, + "UK", "qemu CA 5", NULL, NULL, NULL, NULL, + false, false, false, + false, false, 0, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(servercert5req, cacert5req, + "UK", "qemu.org", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); + /* Key usage:dig-sig:critical */ + TLS_ROOT_REQ(cacert6req, + "UK", "qemu CA 6", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_DIGITAL_SIGNATURE, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(servercert6req, cacert6req, + "UK", "qemu.org", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); + + TLS_TEST_REG(badca1, true, cacert4req.filename, servercert4req.filename, + true); + TLS_TEST_REG(badca2, true, + cacert5req.filename, servercert5req.filename, true); + TLS_TEST_REG(badca3, true, + cacert6req.filename, servercert6req.filename, true); + + + /* Various good servers */ + /* no usage or purpose */ + TLS_CERT_REQ(servercert7req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + false, false, 0, + false, false, NULL, NULL, + 0, 0); + /* usage:cert-sign+dig-sig+encipher:critical */ + TLS_CERT_REQ(servercert8req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT | + GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + /* usage:cert-sign:not-critical */ + TLS_CERT_REQ(servercert9req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + true, false, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + /* purpose:server:critical */ + TLS_CERT_REQ(servercert10req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + false, false, 0, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); + /* purpose:server:not-critical */ + TLS_CERT_REQ(servercert11req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + false, false, 0, + true, false, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); + /* purpose:client+server:critical */ + TLS_CERT_REQ(servercert12req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + false, false, 0, + true, true, + GNUTLS_KP_TLS_WWW_CLIENT, GNUTLS_KP_TLS_WWW_SERVER, + 0, 0); + /* purpose:client+server:not-critical */ + TLS_CERT_REQ(servercert13req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + false, false, 0, + true, false, + GNUTLS_KP_TLS_WWW_CLIENT, GNUTLS_KP_TLS_WWW_SERVER, + 0, 0); + + TLS_TEST_REG(goodserver1, true, + cacertreq.filename, servercert7req.filename, false); + TLS_TEST_REG(goodserver2, true, + cacertreq.filename, servercert8req.filename, false); + TLS_TEST_REG(goodserver3, true, + cacertreq.filename, servercert9req.filename, false); + TLS_TEST_REG(goodserver4, true, + cacertreq.filename, servercert10req.filename, false); + TLS_TEST_REG(goodserver5, true, + cacertreq.filename, servercert11req.filename, false); + TLS_TEST_REG(goodserver6, true, + cacertreq.filename, servercert12req.filename, false); + TLS_TEST_REG(goodserver7, true, + cacertreq.filename, servercert13req.filename, false); + + /* Bad servers */ + + /* usage:cert-sign:critical */ + TLS_CERT_REQ(servercert14req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + /* purpose:client:critical */ + TLS_CERT_REQ(servercert15req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + false, false, 0, + true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL, + 0, 0); + /* usage: none:critical */ + TLS_CERT_REQ(servercert16req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + true, true, 0, + false, false, NULL, NULL, + 0, 0); + + TLS_TEST_REG(badserver1, true, + cacertreq.filename, servercert14req.filename, true); + TLS_TEST_REG(badserver2, true, + cacertreq.filename, servercert15req.filename, true); + TLS_TEST_REG(badserver3, true, + cacertreq.filename, servercert16req.filename, true); + + + + /* Various good clients */ + /* no usage or purpose */ + TLS_CERT_REQ(clientcert1req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + false, false, 0, + false, false, NULL, NULL, + 0, 0); + /* usage:cert-sign+dig-sig+encipher:critical */ + TLS_CERT_REQ(clientcert2req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT | + GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + /* usage:cert-sign:not-critical */ + TLS_CERT_REQ(clientcert3req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + true, false, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + /* purpose:client:critical */ + TLS_CERT_REQ(clientcert4req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + false, false, 0, + true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL, + 0, 0); + /* purpose:client:not-critical */ + TLS_CERT_REQ(clientcert5req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + false, false, 0, + true, false, GNUTLS_KP_TLS_WWW_CLIENT, NULL, + 0, 0); + /* purpose:client+client:critical */ + TLS_CERT_REQ(clientcert6req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + false, false, 0, + true, true, + GNUTLS_KP_TLS_WWW_CLIENT, GNUTLS_KP_TLS_WWW_SERVER, + 0, 0); + /* purpose:client+client:not-critical */ + TLS_CERT_REQ(clientcert7req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + false, false, 0, + true, false, + GNUTLS_KP_TLS_WWW_CLIENT, GNUTLS_KP_TLS_WWW_SERVER, + 0, 0); + + TLS_TEST_REG(goodclient1, false, + cacertreq.filename, clientcert1req.filename, false); + TLS_TEST_REG(goodclient2, false, + cacertreq.filename, clientcert2req.filename, false); + TLS_TEST_REG(goodclient3, false, + cacertreq.filename, clientcert3req.filename, false); + TLS_TEST_REG(goodclient4, false, + cacertreq.filename, clientcert4req.filename, false); + TLS_TEST_REG(goodclient5, false, + cacertreq.filename, clientcert5req.filename, false); + TLS_TEST_REG(goodclient6, false, + cacertreq.filename, clientcert6req.filename, false); + TLS_TEST_REG(goodclient7, false, + cacertreq.filename, clientcert7req.filename, false); + + /* Bad clients */ + + /* usage:cert-sign:critical */ + TLS_CERT_REQ(clientcert8req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + /* purpose:client:critical */ + TLS_CERT_REQ(clientcert9req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + false, false, 0, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); + /* usage: none:critical */ + TLS_CERT_REQ(clientcert10req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + true, true, 0, + false, false, NULL, NULL, + 0, 0); + + TLS_TEST_REG(badclient1, false, + cacertreq.filename, clientcert8req.filename, true); + TLS_TEST_REG(badclient2, false, + cacertreq.filename, clientcert9req.filename, true); + TLS_TEST_REG(badclient3, false, + cacertreq.filename, clientcert10req.filename, true); + + + + /* Expired stuff */ + + TLS_ROOT_REQ(cacertexpreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, -1); + TLS_CERT_REQ(servercertexpreq, cacertexpreq, + "UK", "qemu.org", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); + TLS_CERT_REQ(servercertexp1req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, -1); + TLS_CERT_REQ(clientcertexp1req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL, + 0, -1); + + TLS_TEST_REG(expired1, true, + cacertexpreq.filename, servercertexpreq.filename, true); + TLS_TEST_REG(expired2, true, + cacertreq.filename, servercertexp1req.filename, true); + TLS_TEST_REG(expired3, false, + cacertreq.filename, clientcertexp1req.filename, true); + + + /* Not activated stuff */ + + TLS_ROOT_REQ(cacertnewreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 1, 2); + TLS_CERT_REQ(servercertnewreq, cacertnewreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); + TLS_CERT_REQ(servercertnew1req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 1, 2); + TLS_CERT_REQ(clientcertnew1req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL, + 1, 2); + + TLS_TEST_REG(inactive1, true, + cacertnewreq.filename, servercertnewreq.filename, true); + TLS_TEST_REG(inactive2, true, + cacertreq.filename, servercertnew1req.filename, true); + TLS_TEST_REG(inactive3, false, + cacertreq.filename, clientcertnew1req.filename, true); + + TLS_ROOT_REQ(cacertrootreq, + "UK", "qemu root", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(cacertlevel1areq, cacertrootreq, + "UK", "qemu level 1a", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(cacertlevel1breq, cacertrootreq, + "UK", "qemu level 1b", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(cacertlevel2areq, cacertlevel1areq, + "UK", "qemu level 2a", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(servercertlevel3areq, cacertlevel2areq, + "UK", "qemu.org", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); + TLS_CERT_REQ(clientcertlevel2breq, cacertlevel1breq, + "UK", "qemu client level 2b", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL, + 0, 0); + + gnutls_x509_crt_t certchain[] = { + cacertrootreq.crt, + cacertlevel1areq.crt, + cacertlevel1breq.crt, + cacertlevel2areq.crt, + }; + + test_tls_write_cert_chain(WORKDIR "cacertchain-ctx.pem", + certchain, + G_N_ELEMENTS(certchain)); + + TLS_TEST_REG(chain1, true, + WORKDIR "cacertchain-ctx.pem", + servercertlevel3areq.filename, false); + TLS_TEST_REG(chain2, false, + WORKDIR "cacertchain-ctx.pem", + clientcertlevel2breq.filename, false); + + /* Some missing certs - first two are fatal, the last + * is ok + */ + TLS_TEST_REG(missingca, true, + "cacertdoesnotexist.pem", + servercert1req.filename, true); + TLS_TEST_REG(missingserver, true, + cacert1req.filename, + "servercertdoesnotexist.pem", true); + TLS_TEST_REG(missingclient, false, + cacert1req.filename, + "clientcertdoesnotexist.pem", false); + + ret = g_test_run(); + + test_tls_discard_cert(&cacertreq); + test_tls_discard_cert(&cacert1req); + test_tls_discard_cert(&cacert2req); + test_tls_discard_cert(&cacert3req); + test_tls_discard_cert(&cacert4req); + test_tls_discard_cert(&cacert5req); + test_tls_discard_cert(&cacert6req); + + test_tls_discard_cert(&servercertreq); + test_tls_discard_cert(&servercert1req); + test_tls_discard_cert(&servercert2req); + test_tls_discard_cert(&servercert3req); + test_tls_discard_cert(&servercert4req); + test_tls_discard_cert(&servercert5req); + test_tls_discard_cert(&servercert6req); + test_tls_discard_cert(&servercert7req); + test_tls_discard_cert(&servercert8req); + test_tls_discard_cert(&servercert9req); + test_tls_discard_cert(&servercert10req); + test_tls_discard_cert(&servercert11req); + test_tls_discard_cert(&servercert12req); + test_tls_discard_cert(&servercert13req); + test_tls_discard_cert(&servercert14req); + test_tls_discard_cert(&servercert15req); + test_tls_discard_cert(&servercert16req); + + test_tls_discard_cert(&clientcertreq); + test_tls_discard_cert(&clientcert1req); + test_tls_discard_cert(&clientcert2req); + test_tls_discard_cert(&clientcert3req); + test_tls_discard_cert(&clientcert4req); + test_tls_discard_cert(&clientcert5req); + test_tls_discard_cert(&clientcert6req); + test_tls_discard_cert(&clientcert7req); + test_tls_discard_cert(&clientcert8req); + test_tls_discard_cert(&clientcert9req); + test_tls_discard_cert(&clientcert10req); + + test_tls_discard_cert(&cacertexpreq); + test_tls_discard_cert(&servercertexpreq); + test_tls_discard_cert(&servercertexp1req); + test_tls_discard_cert(&clientcertexp1req); + + test_tls_discard_cert(&cacertnewreq); + test_tls_discard_cert(&servercertnewreq); + test_tls_discard_cert(&servercertnew1req); + test_tls_discard_cert(&clientcertnew1req); + + test_tls_discard_cert(&cacertrootreq); + test_tls_discard_cert(&cacertlevel1areq); + test_tls_discard_cert(&cacertlevel1breq); + test_tls_discard_cert(&cacertlevel2areq); + test_tls_discard_cert(&servercertlevel3areq); + test_tls_discard_cert(&clientcertlevel2breq); + unlink(WORKDIR "cacertchain-ctx.pem"); + + test_tls_cleanup(KEYFILE); + rmdir(WORKDIR); + + return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; +} + +#else /* ! QCRYPTO_HAVE_TLS_TEST_SUPPORT */ + +int +main(void) +{ + return EXIT_SUCCESS; +} + +#endif /* ! QCRYPTO_HAVE_TLS_TEST_SUPPORT */ diff --git a/tests/unit/test-crypto-tlssession.c b/tests/unit/test-crypto-tlssession.c new file mode 100644 index 0000000000..8b2453fa79 --- /dev/null +++ b/tests/unit/test-crypto-tlssession.c @@ -0,0 +1,660 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + * + * Author: Daniel P. Berrange + */ + +#include "qemu/osdep.h" + +#include "crypto-tls-x509-helpers.h" +#include "crypto-tls-psk-helpers.h" +#include "crypto/tlscredsx509.h" +#include "crypto/tlscredspsk.h" +#include "crypto/tlssession.h" +#include "qom/object_interfaces.h" +#include "qapi/error.h" +#include "qemu/module.h" +#include "qemu/sockets.h" +#include "authz/list.h" + +#ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT + +#define WORKDIR "tests/test-crypto-tlssession-work/" +#define PSKFILE WORKDIR "keys.psk" +#define KEYFILE WORKDIR "key-ctx.pem" + +static ssize_t testWrite(const char *buf, size_t len, void *opaque) +{ + int *fd = opaque; + + return write(*fd, buf, len); +} + +static ssize_t testRead(char *buf, size_t len, void *opaque) +{ + int *fd = opaque; + + return read(*fd, buf, len); +} + +static QCryptoTLSCreds *test_tls_creds_psk_create( + QCryptoTLSCredsEndpoint endpoint, + const char *dir) +{ + Object *parent = object_get_objects_root(); + Object *creds = object_new_with_props( + TYPE_QCRYPTO_TLS_CREDS_PSK, + parent, + (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ? + "testtlscredsserver" : "testtlscredsclient"), + &error_abort, + "endpoint", (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ? + "server" : "client"), + "dir", dir, + "priority", "NORMAL", + NULL + ); + return QCRYPTO_TLS_CREDS(creds); +} + + +static void test_crypto_tls_session_psk(void) +{ + QCryptoTLSCreds *clientCreds; + QCryptoTLSCreds *serverCreds; + QCryptoTLSSession *clientSess = NULL; + QCryptoTLSSession *serverSess = NULL; + int channel[2]; + bool clientShake = false; + bool serverShake = false; + int ret; + + /* We'll use this for our fake client-server connection */ + ret = socketpair(AF_UNIX, SOCK_STREAM, 0, channel); + g_assert(ret == 0); + + /* + * We have an evil loop to do the handshake in a single + * thread, so we need these non-blocking to avoid deadlock + * of ourselves + */ + qemu_set_nonblock(channel[0]); + qemu_set_nonblock(channel[1]); + + clientCreds = test_tls_creds_psk_create( + QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, + WORKDIR); + g_assert(clientCreds != NULL); + + serverCreds = test_tls_creds_psk_create( + QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, + WORKDIR); + g_assert(serverCreds != NULL); + + /* Now the real part of the test, setup the sessions */ + clientSess = qcrypto_tls_session_new( + clientCreds, NULL, NULL, + QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, &error_abort); + g_assert(clientSess != NULL); + + serverSess = qcrypto_tls_session_new( + serverCreds, NULL, NULL, + QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, &error_abort); + g_assert(serverSess != NULL); + + /* For handshake to work, we need to set the I/O callbacks + * to read/write over the socketpair + */ + qcrypto_tls_session_set_callbacks(serverSess, + testWrite, testRead, + &channel[0]); + qcrypto_tls_session_set_callbacks(clientSess, + testWrite, testRead, + &channel[1]); + + /* + * Finally we loop around & around doing handshake on each + * session until we get an error, or the handshake completes. + * This relies on the socketpair being nonblocking to avoid + * deadlocking ourselves upon handshake + */ + do { + int rv; + if (!serverShake) { + rv = qcrypto_tls_session_handshake(serverSess, + &error_abort); + g_assert(rv >= 0); + if (qcrypto_tls_session_get_handshake_status(serverSess) == + QCRYPTO_TLS_HANDSHAKE_COMPLETE) { + serverShake = true; + } + } + if (!clientShake) { + rv = qcrypto_tls_session_handshake(clientSess, + &error_abort); + g_assert(rv >= 0); + if (qcrypto_tls_session_get_handshake_status(clientSess) == + QCRYPTO_TLS_HANDSHAKE_COMPLETE) { + clientShake = true; + } + } + } while (!clientShake || !serverShake); + + + /* Finally make sure the server & client validation is successful. */ + g_assert(qcrypto_tls_session_check_credentials(serverSess, + &error_abort) == 0); + g_assert(qcrypto_tls_session_check_credentials(clientSess, + &error_abort) == 0); + + object_unparent(OBJECT(serverCreds)); + object_unparent(OBJECT(clientCreds)); + + qcrypto_tls_session_free(serverSess); + qcrypto_tls_session_free(clientSess); + + close(channel[0]); + close(channel[1]); +} + + +struct QCryptoTLSSessionTestData { + const char *servercacrt; + const char *clientcacrt; + const char *servercrt; + const char *clientcrt; + bool expectServerFail; + bool expectClientFail; + const char *hostname; + const char *const *wildcards; +}; + +static QCryptoTLSCreds *test_tls_creds_x509_create( + QCryptoTLSCredsEndpoint endpoint, + const char *certdir) +{ + Object *parent = object_get_objects_root(); + Object *creds = object_new_with_props( + TYPE_QCRYPTO_TLS_CREDS_X509, + parent, + (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ? + "testtlscredsserver" : "testtlscredsclient"), + &error_abort, + "endpoint", (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ? + "server" : "client"), + "dir", certdir, + "verify-peer", "yes", + "priority", "NORMAL", + /* We skip initial sanity checks here because we + * want to make sure that problems are being + * detected at the TLS session validation stage, + * and the test-crypto-tlscreds test already + * validate the sanity check code. + */ + "sanity-check", "no", + NULL + ); + return QCRYPTO_TLS_CREDS(creds); +} + + +/* + * This tests validation checking of peer certificates + * + * This is replicating the checks that are done for an + * active TLS session after handshake completes. To + * simulate that we create our TLS contexts, skipping + * sanity checks. We then get a socketpair, and + * initiate a TLS session across them. Finally do + * do actual cert validation tests + */ +static void test_crypto_tls_session_x509(const void *opaque) +{ + struct QCryptoTLSSessionTestData *data = + (struct QCryptoTLSSessionTestData *)opaque; + QCryptoTLSCreds *clientCreds; + QCryptoTLSCreds *serverCreds; + QCryptoTLSSession *clientSess = NULL; + QCryptoTLSSession *serverSess = NULL; + QAuthZList *auth; + const char * const *wildcards; + int channel[2]; + bool clientShake = false; + bool serverShake = false; + int ret; + + /* We'll use this for our fake client-server connection */ + ret = socketpair(AF_UNIX, SOCK_STREAM, 0, channel); + g_assert(ret == 0); + + /* + * We have an evil loop to do the handshake in a single + * thread, so we need these non-blocking to avoid deadlock + * of ourselves + */ + qemu_set_nonblock(channel[0]); + qemu_set_nonblock(channel[1]); + +#define CLIENT_CERT_DIR "tests/test-crypto-tlssession-client/" +#define SERVER_CERT_DIR "tests/test-crypto-tlssession-server/" + mkdir(CLIENT_CERT_DIR, 0700); + mkdir(SERVER_CERT_DIR, 0700); + + unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT); + unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT); + unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY); + + unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT); + unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT); + unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY); + + g_assert(link(data->servercacrt, + SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT) == 0); + g_assert(link(data->servercrt, + SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT) == 0); + g_assert(link(KEYFILE, + SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY) == 0); + + g_assert(link(data->clientcacrt, + CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT) == 0); + g_assert(link(data->clientcrt, + CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT) == 0); + g_assert(link(KEYFILE, + CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY) == 0); + + clientCreds = test_tls_creds_x509_create( + QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, + CLIENT_CERT_DIR); + g_assert(clientCreds != NULL); + + serverCreds = test_tls_creds_x509_create( + QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, + SERVER_CERT_DIR); + g_assert(serverCreds != NULL); + + auth = qauthz_list_new("tlssessionacl", + QAUTHZ_LIST_POLICY_DENY, + &error_abort); + wildcards = data->wildcards; + while (wildcards && *wildcards) { + qauthz_list_append_rule(auth, *wildcards, + QAUTHZ_LIST_POLICY_ALLOW, + QAUTHZ_LIST_FORMAT_GLOB, + &error_abort); + wildcards++; + } + + /* Now the real part of the test, setup the sessions */ + clientSess = qcrypto_tls_session_new( + clientCreds, data->hostname, NULL, + QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, &error_abort); + g_assert(clientSess != NULL); + + serverSess = qcrypto_tls_session_new( + serverCreds, NULL, + data->wildcards ? "tlssessionacl" : NULL, + QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, &error_abort); + g_assert(serverSess != NULL); + + /* For handshake to work, we need to set the I/O callbacks + * to read/write over the socketpair + */ + qcrypto_tls_session_set_callbacks(serverSess, + testWrite, testRead, + &channel[0]); + qcrypto_tls_session_set_callbacks(clientSess, + testWrite, testRead, + &channel[1]); + + /* + * Finally we loop around & around doing handshake on each + * session until we get an error, or the handshake completes. + * This relies on the socketpair being nonblocking to avoid + * deadlocking ourselves upon handshake + */ + do { + int rv; + if (!serverShake) { + rv = qcrypto_tls_session_handshake(serverSess, + &error_abort); + g_assert(rv >= 0); + if (qcrypto_tls_session_get_handshake_status(serverSess) == + QCRYPTO_TLS_HANDSHAKE_COMPLETE) { + serverShake = true; + } + } + if (!clientShake) { + rv = qcrypto_tls_session_handshake(clientSess, + &error_abort); + g_assert(rv >= 0); + if (qcrypto_tls_session_get_handshake_status(clientSess) == + QCRYPTO_TLS_HANDSHAKE_COMPLETE) { + clientShake = true; + } + } + } while (!clientShake || !serverShake); + + + /* Finally make sure the server validation does what + * we were expecting + */ + if (qcrypto_tls_session_check_credentials( + serverSess, data->expectServerFail ? NULL : &error_abort) < 0) { + g_assert(data->expectServerFail); + } else { + g_assert(!data->expectServerFail); + } + + /* + * And the same for the client validation check + */ + if (qcrypto_tls_session_check_credentials( + clientSess, data->expectClientFail ? NULL : &error_abort) < 0) { + g_assert(data->expectClientFail); + } else { + g_assert(!data->expectClientFail); + } + + unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT); + unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT); + unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY); + + unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT); + unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT); + unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY); + + rmdir(CLIENT_CERT_DIR); + rmdir(SERVER_CERT_DIR); + + object_unparent(OBJECT(serverCreds)); + object_unparent(OBJECT(clientCreds)); + object_unparent(OBJECT(auth)); + + qcrypto_tls_session_free(serverSess); + qcrypto_tls_session_free(clientSess); + + close(channel[0]); + close(channel[1]); +} + + +int main(int argc, char **argv) +{ + int ret; + + module_call_init(MODULE_INIT_QOM); + g_test_init(&argc, &argv, NULL); + g_setenv("GNUTLS_FORCE_FIPS_MODE", "2", 1); + + mkdir(WORKDIR, 0700); + + test_tls_init(KEYFILE); + test_tls_psk_init(PSKFILE); + + /* Simple initial test using Pre-Shared Keys. */ + g_test_add_func("/qcrypto/tlssession/psk", + test_crypto_tls_session_psk); + + /* More complex tests using X.509 certificates. */ +# define TEST_SESS_REG(name, caCrt, \ + serverCrt, clientCrt, \ + expectServerFail, expectClientFail, \ + hostname, wildcards) \ + struct QCryptoTLSSessionTestData name = { \ + caCrt, caCrt, serverCrt, clientCrt, \ + expectServerFail, expectClientFail, \ + hostname, wildcards \ + }; \ + g_test_add_data_func("/qcrypto/tlssession/" # name, \ + &name, test_crypto_tls_session_x509); \ + + +# define TEST_SESS_REG_EXT(name, serverCaCrt, clientCaCrt, \ + serverCrt, clientCrt, \ + expectServerFail, expectClientFail, \ + hostname, wildcards) \ + struct QCryptoTLSSessionTestData name = { \ + serverCaCrt, clientCaCrt, serverCrt, clientCrt, \ + expectServerFail, expectClientFail, \ + hostname, wildcards \ + }; \ + g_test_add_data_func("/qcrypto/tlssession/" # name, \ + &name, test_crypto_tls_session_x509); \ + + /* A perfect CA, perfect client & perfect server */ + + /* Basic:CA:critical */ + TLS_ROOT_REQ(cacertreq, + "UK", "qemu CA", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + + TLS_ROOT_REQ(altcacertreq, + "UK", "qemu CA 1", NULL, NULL, NULL, NULL, + true, true, true, + false, false, 0, + false, false, NULL, NULL, + 0, 0); + + TLS_CERT_REQ(servercertreq, cacertreq, + "UK", "qemu.org", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); + TLS_CERT_REQ(clientcertreq, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL, + 0, 0); + + TLS_CERT_REQ(clientcertaltreq, altcacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL, + 0, 0); + + TEST_SESS_REG(basicca, cacertreq.filename, + servercertreq.filename, clientcertreq.filename, + false, false, "qemu.org", NULL); + TEST_SESS_REG_EXT(differentca, cacertreq.filename, + altcacertreq.filename, servercertreq.filename, + clientcertaltreq.filename, true, true, "qemu.org", NULL); + + + /* When an altname is set, the CN is ignored, so it must be duplicated + * as an altname for it to match */ + TLS_CERT_REQ(servercertalt1req, cacertreq, + "UK", "qemu.org", "www.qemu.org", "qemu.org", + "192.168.122.1", "fec0::dead:beaf", + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); + /* This intentionally doesn't replicate */ + TLS_CERT_REQ(servercertalt2req, cacertreq, + "UK", "qemu.org", "www.qemu.org", "wiki.qemu.org", + "192.168.122.1", "fec0::dead:beaf", + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); + + TEST_SESS_REG(altname1, cacertreq.filename, + servercertalt1req.filename, clientcertreq.filename, + false, false, "qemu.org", NULL); + TEST_SESS_REG(altname2, cacertreq.filename, + servercertalt1req.filename, clientcertreq.filename, + false, false, "www.qemu.org", NULL); + TEST_SESS_REG(altname3, cacertreq.filename, + servercertalt1req.filename, clientcertreq.filename, + false, true, "wiki.qemu.org", NULL); + + TEST_SESS_REG(altname4, cacertreq.filename, + servercertalt2req.filename, clientcertreq.filename, + false, true, "qemu.org", NULL); + TEST_SESS_REG(altname5, cacertreq.filename, + servercertalt2req.filename, clientcertreq.filename, + false, false, "www.qemu.org", NULL); + TEST_SESS_REG(altname6, cacertreq.filename, + servercertalt2req.filename, clientcertreq.filename, + false, false, "wiki.qemu.org", NULL); + + const char *const wildcards1[] = { + "C=UK,CN=dogfood", + NULL, + }; + const char *const wildcards2[] = { + "C=UK,CN=qemu", + NULL, + }; + const char *const wildcards3[] = { + "C=UK,CN=dogfood", + "C=UK,CN=qemu", + NULL, + }; + const char *const wildcards4[] = { + "C=UK,CN=qemustuff", + NULL, + }; + const char *const wildcards5[] = { + "C=UK,CN=qemu*", + NULL, + }; + const char *const wildcards6[] = { + "C=UK,CN=*emu*", + NULL, + }; + + TEST_SESS_REG(wildcard1, cacertreq.filename, + servercertreq.filename, clientcertreq.filename, + true, false, "qemu.org", wildcards1); + TEST_SESS_REG(wildcard2, cacertreq.filename, + servercertreq.filename, clientcertreq.filename, + false, false, "qemu.org", wildcards2); + TEST_SESS_REG(wildcard3, cacertreq.filename, + servercertreq.filename, clientcertreq.filename, + false, false, "qemu.org", wildcards3); + TEST_SESS_REG(wildcard4, cacertreq.filename, + servercertreq.filename, clientcertreq.filename, + true, false, "qemu.org", wildcards4); + TEST_SESS_REG(wildcard5, cacertreq.filename, + servercertreq.filename, clientcertreq.filename, + false, false, "qemu.org", wildcards5); + TEST_SESS_REG(wildcard6, cacertreq.filename, + servercertreq.filename, clientcertreq.filename, + false, false, "qemu.org", wildcards6); + + TLS_ROOT_REQ(cacertrootreq, + "UK", "qemu root", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(cacertlevel1areq, cacertrootreq, + "UK", "qemu level 1a", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(cacertlevel1breq, cacertrootreq, + "UK", "qemu level 1b", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(cacertlevel2areq, cacertlevel1areq, + "UK", "qemu level 2a", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(servercertlevel3areq, cacertlevel2areq, + "UK", "qemu.org", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); + TLS_CERT_REQ(clientcertlevel2breq, cacertlevel1breq, + "UK", "qemu client level 2b", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL, + 0, 0); + + gnutls_x509_crt_t certchain[] = { + cacertrootreq.crt, + cacertlevel1areq.crt, + cacertlevel1breq.crt, + cacertlevel2areq.crt, + }; + + test_tls_write_cert_chain(WORKDIR "cacertchain-sess.pem", + certchain, + G_N_ELEMENTS(certchain)); + + TEST_SESS_REG(cachain, WORKDIR "cacertchain-sess.pem", + servercertlevel3areq.filename, clientcertlevel2breq.filename, + false, false, "qemu.org", NULL); + + ret = g_test_run(); + + test_tls_discard_cert(&clientcertreq); + test_tls_discard_cert(&clientcertaltreq); + + test_tls_discard_cert(&servercertreq); + test_tls_discard_cert(&servercertalt1req); + test_tls_discard_cert(&servercertalt2req); + + test_tls_discard_cert(&cacertreq); + test_tls_discard_cert(&altcacertreq); + + test_tls_discard_cert(&cacertrootreq); + test_tls_discard_cert(&cacertlevel1areq); + test_tls_discard_cert(&cacertlevel1breq); + test_tls_discard_cert(&cacertlevel2areq); + test_tls_discard_cert(&servercertlevel3areq); + test_tls_discard_cert(&clientcertlevel2breq); + unlink(WORKDIR "cacertchain-sess.pem"); + + test_tls_psk_cleanup(PSKFILE); + test_tls_cleanup(KEYFILE); + rmdir(WORKDIR); + + return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; +} + +#else /* ! QCRYPTO_HAVE_TLS_TEST_SUPPORT */ + +int +main(void) +{ + return EXIT_SUCCESS; +} + +#endif /* ! QCRYPTO_HAVE_TLS_TEST_SUPPORT */ diff --git a/tests/unit/test-crypto-xts.c b/tests/unit/test-crypto-xts.c new file mode 100644 index 0000000000..7acbc956fd --- /dev/null +++ b/tests/unit/test-crypto-xts.c @@ -0,0 +1,529 @@ +/* + * QEMU Crypto XTS cipher mode + * + * Copyright (c) 2015-2018 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + * + * This code is originally derived from public domain / WTFPL code in + * LibTomCrypt crytographic library http://libtom.org. The XTS code + * was donated by Elliptic Semiconductor Inc (www.ellipticsemi.com) + * to the LibTom Projects + * + */ + +#include "qemu/osdep.h" +#include "crypto/init.h" +#include "crypto/xts.h" +#include "crypto/aes.h" + +typedef struct { + const char *path; + int keylen; + unsigned char key1[32]; + unsigned char key2[32]; + uint64_t seqnum; + unsigned long PTLEN; + unsigned char PTX[512], CTX[512]; +} QCryptoXTSTestData; + +static const QCryptoXTSTestData test_data[] = { + /* #1 32 byte key, 32 byte PTX */ + { + "/crypto/xts/t-1-key-32-ptx-32", + 32, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + 0, + 32, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x91, 0x7c, 0xf6, 0x9e, 0xbd, 0x68, 0xb2, 0xec, + 0x9b, 0x9f, 0xe9, 0xa3, 0xea, 0xdd, 0xa6, 0x92, + 0xcd, 0x43, 0xd2, 0xf5, 0x95, 0x98, 0xed, 0x85, + 0x8c, 0x02, 0xc2, 0x65, 0x2f, 0xbf, 0x92, 0x2e }, + }, + + /* #2, 32 byte key, 32 byte PTX */ + { + "/crypto/xts/t-2-key-32-ptx-32", + 32, + { 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11 }, + { 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22 }, + 0x3333333333LL, + 32, + { 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44 }, + { 0xc4, 0x54, 0x18, 0x5e, 0x6a, 0x16, 0x93, 0x6e, + 0x39, 0x33, 0x40, 0x38, 0xac, 0xef, 0x83, 0x8b, + 0xfb, 0x18, 0x6f, 0xff, 0x74, 0x80, 0xad, 0xc4, + 0x28, 0x93, 0x82, 0xec, 0xd6, 0xd3, 0x94, 0xf0 }, + }, + + /* #5 from xts.7, 32 byte key, 32 byte PTX */ + { + "/crypto/xts/t-5-key-32-ptx-32", + 32, + { 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, + 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0 }, + { 0xbf, 0xbe, 0xbd, 0xbc, 0xbb, 0xba, 0xb9, 0xb8, + 0xb7, 0xb6, 0xb5, 0xb4, 0xb3, 0xb2, 0xb1, 0xb0 }, + 0x123456789aLL, + 32, + { 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44 }, + { 0xb0, 0x1f, 0x86, 0xf8, 0xed, 0xc1, 0x86, 0x37, + 0x06, 0xfa, 0x8a, 0x42, 0x53, 0xe3, 0x4f, 0x28, + 0xaf, 0x31, 0x9d, 0xe3, 0x83, 0x34, 0x87, 0x0f, + 0x4d, 0xd1, 0xf9, 0x4c, 0xbe, 0x98, 0x32, 0xf1 }, + }, + + /* #4, 32 byte key, 512 byte PTX */ + { + "/crypto/xts/t-4-key-32-ptx-512", + 32, + { 0x27, 0x18, 0x28, 0x18, 0x28, 0x45, 0x90, 0x45, + 0x23, 0x53, 0x60, 0x28, 0x74, 0x71, 0x35, 0x26 }, + { 0x31, 0x41, 0x59, 0x26, 0x53, 0x58, 0x97, 0x93, + 0x23, 0x84, 0x62, 0x64, 0x33, 0x83, 0x27, 0x95 }, + 0, + 512, + { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, + }, + { + 0x27, 0xa7, 0x47, 0x9b, 0xef, 0xa1, 0xd4, 0x76, + 0x48, 0x9f, 0x30, 0x8c, 0xd4, 0xcf, 0xa6, 0xe2, + 0xa9, 0x6e, 0x4b, 0xbe, 0x32, 0x08, 0xff, 0x25, + 0x28, 0x7d, 0xd3, 0x81, 0x96, 0x16, 0xe8, 0x9c, + 0xc7, 0x8c, 0xf7, 0xf5, 0xe5, 0x43, 0x44, 0x5f, + 0x83, 0x33, 0xd8, 0xfa, 0x7f, 0x56, 0x00, 0x00, + 0x05, 0x27, 0x9f, 0xa5, 0xd8, 0xb5, 0xe4, 0xad, + 0x40, 0xe7, 0x36, 0xdd, 0xb4, 0xd3, 0x54, 0x12, + 0x32, 0x80, 0x63, 0xfd, 0x2a, 0xab, 0x53, 0xe5, + 0xea, 0x1e, 0x0a, 0x9f, 0x33, 0x25, 0x00, 0xa5, + 0xdf, 0x94, 0x87, 0xd0, 0x7a, 0x5c, 0x92, 0xcc, + 0x51, 0x2c, 0x88, 0x66, 0xc7, 0xe8, 0x60, 0xce, + 0x93, 0xfd, 0xf1, 0x66, 0xa2, 0x49, 0x12, 0xb4, + 0x22, 0x97, 0x61, 0x46, 0xae, 0x20, 0xce, 0x84, + 0x6b, 0xb7, 0xdc, 0x9b, 0xa9, 0x4a, 0x76, 0x7a, + 0xae, 0xf2, 0x0c, 0x0d, 0x61, 0xad, 0x02, 0x65, + 0x5e, 0xa9, 0x2d, 0xc4, 0xc4, 0xe4, 0x1a, 0x89, + 0x52, 0xc6, 0x51, 0xd3, 0x31, 0x74, 0xbe, 0x51, + 0xa1, 0x0c, 0x42, 0x11, 0x10, 0xe6, 0xd8, 0x15, + 0x88, 0xed, 0xe8, 0x21, 0x03, 0xa2, 0x52, 0xd8, + 0xa7, 0x50, 0xe8, 0x76, 0x8d, 0xef, 0xff, 0xed, + 0x91, 0x22, 0x81, 0x0a, 0xae, 0xb9, 0x9f, 0x91, + 0x72, 0xaf, 0x82, 0xb6, 0x04, 0xdc, 0x4b, 0x8e, + 0x51, 0xbc, 0xb0, 0x82, 0x35, 0xa6, 0xf4, 0x34, + 0x13, 0x32, 0xe4, 0xca, 0x60, 0x48, 0x2a, 0x4b, + 0xa1, 0xa0, 0x3b, 0x3e, 0x65, 0x00, 0x8f, 0xc5, + 0xda, 0x76, 0xb7, 0x0b, 0xf1, 0x69, 0x0d, 0xb4, + 0xea, 0xe2, 0x9c, 0x5f, 0x1b, 0xad, 0xd0, 0x3c, + 0x5c, 0xcf, 0x2a, 0x55, 0xd7, 0x05, 0xdd, 0xcd, + 0x86, 0xd4, 0x49, 0x51, 0x1c, 0xeb, 0x7e, 0xc3, + 0x0b, 0xf1, 0x2b, 0x1f, 0xa3, 0x5b, 0x91, 0x3f, + 0x9f, 0x74, 0x7a, 0x8a, 0xfd, 0x1b, 0x13, 0x0e, + 0x94, 0xbf, 0xf9, 0x4e, 0xff, 0xd0, 0x1a, 0x91, + 0x73, 0x5c, 0xa1, 0x72, 0x6a, 0xcd, 0x0b, 0x19, + 0x7c, 0x4e, 0x5b, 0x03, 0x39, 0x36, 0x97, 0xe1, + 0x26, 0x82, 0x6f, 0xb6, 0xbb, 0xde, 0x8e, 0xcc, + 0x1e, 0x08, 0x29, 0x85, 0x16, 0xe2, 0xc9, 0xed, + 0x03, 0xff, 0x3c, 0x1b, 0x78, 0x60, 0xf6, 0xde, + 0x76, 0xd4, 0xce, 0xcd, 0x94, 0xc8, 0x11, 0x98, + 0x55, 0xef, 0x52, 0x97, 0xca, 0x67, 0xe9, 0xf3, + 0xe7, 0xff, 0x72, 0xb1, 0xe9, 0x97, 0x85, 0xca, + 0x0a, 0x7e, 0x77, 0x20, 0xc5, 0xb3, 0x6d, 0xc6, + 0xd7, 0x2c, 0xac, 0x95, 0x74, 0xc8, 0xcb, 0xbc, + 0x2f, 0x80, 0x1e, 0x23, 0xe5, 0x6f, 0xd3, 0x44, + 0xb0, 0x7f, 0x22, 0x15, 0x4b, 0xeb, 0xa0, 0xf0, + 0x8c, 0xe8, 0x89, 0x1e, 0x64, 0x3e, 0xd9, 0x95, + 0xc9, 0x4d, 0x9a, 0x69, 0xc9, 0xf1, 0xb5, 0xf4, + 0x99, 0x02, 0x7a, 0x78, 0x57, 0x2a, 0xee, 0xbd, + 0x74, 0xd2, 0x0c, 0xc3, 0x98, 0x81, 0xc2, 0x13, + 0xee, 0x77, 0x0b, 0x10, 0x10, 0xe4, 0xbe, 0xa7, + 0x18, 0x84, 0x69, 0x77, 0xae, 0x11, 0x9f, 0x7a, + 0x02, 0x3a, 0xb5, 0x8c, 0xca, 0x0a, 0xd7, 0x52, + 0xaf, 0xe6, 0x56, 0xbb, 0x3c, 0x17, 0x25, 0x6a, + 0x9f, 0x6e, 0x9b, 0xf1, 0x9f, 0xdd, 0x5a, 0x38, + 0xfc, 0x82, 0xbb, 0xe8, 0x72, 0xc5, 0x53, 0x9e, + 0xdb, 0x60, 0x9e, 0xf4, 0xf7, 0x9c, 0x20, 0x3e, + 0xbb, 0x14, 0x0f, 0x2e, 0x58, 0x3c, 0xb2, 0xad, + 0x15, 0xb4, 0xaa, 0x5b, 0x65, 0x50, 0x16, 0xa8, + 0x44, 0x92, 0x77, 0xdb, 0xd4, 0x77, 0xef, 0x2c, + 0x8d, 0x6c, 0x01, 0x7d, 0xb7, 0x38, 0xb1, 0x8d, + 0xeb, 0x4a, 0x42, 0x7d, 0x19, 0x23, 0xce, 0x3f, + 0xf2, 0x62, 0x73, 0x57, 0x79, 0xa4, 0x18, 0xf2, + 0x0a, 0x28, 0x2d, 0xf9, 0x20, 0x14, 0x7b, 0xea, + 0xbe, 0x42, 0x1e, 0xe5, 0x31, 0x9d, 0x05, 0x68, + } + }, + + /* #7, 32 byte key, 17 byte PTX */ + { + "/crypto/xts/t-7-key-32-ptx-17", + 32, + { 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, + 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0 }, + { 0xbf, 0xbe, 0xbd, 0xbc, 0xbb, 0xba, 0xb9, 0xb8, + 0xb7, 0xb6, 0xb5, 0xb4, 0xb3, 0xb2, 0xb1, 0xb0 }, + 0x123456789aLL, + 17, + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 }, + { 0x6c, 0x16, 0x25, 0xdb, 0x46, 0x71, 0x52, 0x2d, + 0x3d, 0x75, 0x99, 0x60, 0x1d, 0xe7, 0xca, 0x09, 0xed }, + }, + + /* #15, 32 byte key, 25 byte PTX */ + { + "/crypto/xts/t-15-key-32-ptx-25", + 32, + { 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, + 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0 }, + { 0xbf, 0xbe, 0xbd, 0xbc, 0xbb, 0xba, 0xb9, 0xb8, + 0xb7, 0xb6, 0xb5, 0xb4, 0xb3, 0xb2, 0xb1, 0xb0 }, + 0x123456789aLL, + 25, + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18 }, + { 0x8f, 0x4d, 0xcb, 0xad, 0x55, 0x55, 0x8d, 0x7b, + 0x4e, 0x01, 0xd9, 0x37, 0x9c, 0xd4, 0xea, 0x22, + 0xed, 0xbf, 0x9d, 0xac, 0xe4, 0x5d, 0x6f, 0x6a, 0x73 }, + }, + + /* #21, 32 byte key, 31 byte PTX */ + { + "/crypto/xts/t-21-key-32-ptx-31", + 32, + { 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, + 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0 }, + { 0xbf, 0xbe, 0xbd, 0xbc, 0xbb, 0xba, 0xb9, 0xb8, + 0xb7, 0xb6, 0xb5, 0xb4, 0xb3, 0xb2, 0xb1, 0xb0 }, + 0x123456789aLL, + 31, + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e }, + { 0xd0, 0x5b, 0xc0, 0x90, 0xa8, 0xe0, 0x4f, 0x1b, + 0x3d, 0x3e, 0xcd, 0xd5, 0xba, 0xec, 0x0f, 0xd4, + 0xed, 0xbf, 0x9d, 0xac, 0xe4, 0x5d, 0x6f, 0x6a, + 0x73, 0x06, 0xe6, 0x4b, 0xe5, 0xdd, 0x82 }, + }, +}; + +#define STORE64L(x, y) \ + do { \ + (y)[7] = (unsigned char)(((x) >> 56) & 255); \ + (y)[6] = (unsigned char)(((x) >> 48) & 255); \ + (y)[5] = (unsigned char)(((x) >> 40) & 255); \ + (y)[4] = (unsigned char)(((x) >> 32) & 255); \ + (y)[3] = (unsigned char)(((x) >> 24) & 255); \ + (y)[2] = (unsigned char)(((x) >> 16) & 255); \ + (y)[1] = (unsigned char)(((x) >> 8) & 255); \ + (y)[0] = (unsigned char)((x) & 255); \ + } while (0) + +struct TestAES { + AES_KEY enc; + AES_KEY dec; +}; + +static void test_xts_aes_encrypt(const void *ctx, + size_t length, + uint8_t *dst, + const uint8_t *src) +{ + const struct TestAES *aesctx = ctx; + + AES_encrypt(src, dst, &aesctx->enc); +} + + +static void test_xts_aes_decrypt(const void *ctx, + size_t length, + uint8_t *dst, + const uint8_t *src) +{ + const struct TestAES *aesctx = ctx; + + AES_decrypt(src, dst, &aesctx->dec); +} + + +static void test_xts(const void *opaque) +{ + const QCryptoXTSTestData *data = opaque; + uint8_t out[512], Torg[16], T[16]; + uint64_t seq; + struct TestAES aesdata; + struct TestAES aestweak; + + AES_set_encrypt_key(data->key1, data->keylen / 2 * 8, &aesdata.enc); + AES_set_decrypt_key(data->key1, data->keylen / 2 * 8, &aesdata.dec); + AES_set_encrypt_key(data->key2, data->keylen / 2 * 8, &aestweak.enc); + AES_set_decrypt_key(data->key2, data->keylen / 2 * 8, &aestweak.dec); + + seq = data->seqnum; + STORE64L(seq, Torg); + memset(Torg + 8, 0, 8); + + memcpy(T, Torg, sizeof(T)); + xts_encrypt(&aesdata, &aestweak, + test_xts_aes_encrypt, + test_xts_aes_decrypt, + T, data->PTLEN, out, data->PTX); + + g_assert(memcmp(out, data->CTX, data->PTLEN) == 0); + + memcpy(T, Torg, sizeof(T)); + xts_decrypt(&aesdata, &aestweak, + test_xts_aes_encrypt, + test_xts_aes_decrypt, + T, data->PTLEN, out, data->CTX); + + g_assert(memcmp(out, data->PTX, data->PTLEN) == 0); +} + + +static void test_xts_split(const void *opaque) +{ + const QCryptoXTSTestData *data = opaque; + uint8_t out[512], Torg[16], T[16]; + uint64_t seq; + unsigned long len = data->PTLEN / 2; + struct TestAES aesdata; + struct TestAES aestweak; + + AES_set_encrypt_key(data->key1, data->keylen / 2 * 8, &aesdata.enc); + AES_set_decrypt_key(data->key1, data->keylen / 2 * 8, &aesdata.dec); + AES_set_encrypt_key(data->key2, data->keylen / 2 * 8, &aestweak.enc); + AES_set_decrypt_key(data->key2, data->keylen / 2 * 8, &aestweak.dec); + + seq = data->seqnum; + STORE64L(seq, Torg); + memset(Torg + 8, 0, 8); + + memcpy(T, Torg, sizeof(T)); + xts_encrypt(&aesdata, &aestweak, + test_xts_aes_encrypt, + test_xts_aes_decrypt, + T, len, out, data->PTX); + xts_encrypt(&aesdata, &aestweak, + test_xts_aes_encrypt, + test_xts_aes_decrypt, + T, len, &out[len], &data->PTX[len]); + + g_assert(memcmp(out, data->CTX, data->PTLEN) == 0); + + memcpy(T, Torg, sizeof(T)); + xts_decrypt(&aesdata, &aestweak, + test_xts_aes_encrypt, + test_xts_aes_decrypt, + T, len, out, data->CTX); + xts_decrypt(&aesdata, &aestweak, + test_xts_aes_encrypt, + test_xts_aes_decrypt, + T, len, &out[len], &data->CTX[len]); + + g_assert(memcmp(out, data->PTX, data->PTLEN) == 0); +} + + +static void test_xts_unaligned(const void *opaque) +{ +#define BAD_ALIGN 3 + const QCryptoXTSTestData *data = opaque; + uint8_t in[512 + BAD_ALIGN], out[512 + BAD_ALIGN]; + uint8_t Torg[16], T[16 + BAD_ALIGN]; + uint64_t seq; + struct TestAES aesdata; + struct TestAES aestweak; + + AES_set_encrypt_key(data->key1, data->keylen / 2 * 8, &aesdata.enc); + AES_set_decrypt_key(data->key1, data->keylen / 2 * 8, &aesdata.dec); + AES_set_encrypt_key(data->key2, data->keylen / 2 * 8, &aestweak.enc); + AES_set_decrypt_key(data->key2, data->keylen / 2 * 8, &aestweak.dec); + + seq = data->seqnum; + STORE64L(seq, Torg); + memset(Torg + 8, 0, 8); + + /* IV not aligned */ + memcpy(T + BAD_ALIGN, Torg, 16); + memcpy(in, data->PTX, data->PTLEN); + xts_encrypt(&aesdata, &aestweak, + test_xts_aes_encrypt, + test_xts_aes_decrypt, + T + BAD_ALIGN, data->PTLEN, out, in); + + g_assert(memcmp(out, data->CTX, data->PTLEN) == 0); + + /* plain text not aligned */ + memcpy(T, Torg, 16); + memcpy(in + BAD_ALIGN, data->PTX, data->PTLEN); + xts_encrypt(&aesdata, &aestweak, + test_xts_aes_encrypt, + test_xts_aes_decrypt, + T, data->PTLEN, out, in + BAD_ALIGN); + + g_assert(memcmp(out, data->CTX, data->PTLEN) == 0); + + /* cipher text not aligned */ + memcpy(T, Torg, 16); + memcpy(in, data->PTX, data->PTLEN); + xts_encrypt(&aesdata, &aestweak, + test_xts_aes_encrypt, + test_xts_aes_decrypt, + T, data->PTLEN, out + BAD_ALIGN, in); + + g_assert(memcmp(out + BAD_ALIGN, data->CTX, data->PTLEN) == 0); + + + /* IV not aligned */ + memcpy(T + BAD_ALIGN, Torg, 16); + memcpy(in, data->CTX, data->PTLEN); + xts_decrypt(&aesdata, &aestweak, + test_xts_aes_encrypt, + test_xts_aes_decrypt, + T + BAD_ALIGN, data->PTLEN, out, in); + + g_assert(memcmp(out, data->PTX, data->PTLEN) == 0); + + /* cipher text not aligned */ + memcpy(T, Torg, 16); + memcpy(in + BAD_ALIGN, data->CTX, data->PTLEN); + xts_decrypt(&aesdata, &aestweak, + test_xts_aes_encrypt, + test_xts_aes_decrypt, + T, data->PTLEN, out, in + BAD_ALIGN); + + g_assert(memcmp(out, data->PTX, data->PTLEN) == 0); + + /* plain text not aligned */ + memcpy(T, Torg, 16); + memcpy(in, data->CTX, data->PTLEN); + xts_decrypt(&aesdata, &aestweak, + test_xts_aes_encrypt, + test_xts_aes_decrypt, + T, data->PTLEN, out + BAD_ALIGN, in); + + g_assert(memcmp(out + BAD_ALIGN, data->PTX, data->PTLEN) == 0); +} + + +int main(int argc, char **argv) +{ + size_t i; + + g_test_init(&argc, &argv, NULL); + + g_assert(qcrypto_init(NULL) == 0); + + for (i = 0; i < G_N_ELEMENTS(test_data); i++) { + gchar *path = g_strdup_printf("%s/basic", test_data[i].path); + g_test_add_data_func(path, &test_data[i], test_xts); + g_free(path); + + /* skip the cases where the length is smaller than 2*blocklen + * or the length is not a multiple of 32 + */ + if ((test_data[i].PTLEN >= 32) && !(test_data[i].PTLEN % 32)) { + path = g_strdup_printf("%s/split", test_data[i].path); + g_test_add_data_func(path, &test_data[i], test_xts_split); + g_free(path); + } + + path = g_strdup_printf("%s/unaligned", test_data[i].path); + g_test_add_data_func(path, &test_data[i], test_xts_unaligned); + g_free(path); + } + + return g_test_run(); +} diff --git a/tests/unit/test-cutils.c b/tests/unit/test-cutils.c new file mode 100644 index 0000000000..1aa8351520 --- /dev/null +++ b/tests/unit/test-cutils.c @@ -0,0 +1,2460 @@ +/* + * cutils.c unit-tests + * + * Copyright (C) 2013 Red Hat Inc. + * + * Authors: + * Eduardo Habkost + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "qemu/cutils.h" +#include "qemu/units.h" + +static void test_parse_uint_null(void) +{ + unsigned long long i = 999; + char f = 'X'; + char *endptr = &f; + int r; + + r = parse_uint(NULL, &i, &endptr, 0); + + g_assert_cmpint(r, ==, -EINVAL); + g_assert_cmpint(i, ==, 0); + g_assert(endptr == NULL); +} + +static void test_parse_uint_empty(void) +{ + unsigned long long i = 999; + char f = 'X'; + char *endptr = &f; + const char *str = ""; + int r; + + r = parse_uint(str, &i, &endptr, 0); + + g_assert_cmpint(r, ==, -EINVAL); + g_assert_cmpint(i, ==, 0); + g_assert(endptr == str); +} + +static void test_parse_uint_whitespace(void) +{ + unsigned long long i = 999; + char f = 'X'; + char *endptr = &f; + const char *str = " \t "; + int r; + + r = parse_uint(str, &i, &endptr, 0); + + g_assert_cmpint(r, ==, -EINVAL); + g_assert_cmpint(i, ==, 0); + g_assert(endptr == str); +} + + +static void test_parse_uint_invalid(void) +{ + unsigned long long i = 999; + char f = 'X'; + char *endptr = &f; + const char *str = " \t xxx"; + int r; + + r = parse_uint(str, &i, &endptr, 0); + + g_assert_cmpint(r, ==, -EINVAL); + g_assert_cmpint(i, ==, 0); + g_assert(endptr == str); +} + + +static void test_parse_uint_trailing(void) +{ + unsigned long long i = 999; + char f = 'X'; + char *endptr = &f; + const char *str = "123xxx"; + int r; + + r = parse_uint(str, &i, &endptr, 0); + + g_assert_cmpint(r, ==, 0); + g_assert_cmpint(i, ==, 123); + g_assert(endptr == str + 3); +} + +static void test_parse_uint_correct(void) +{ + unsigned long long i = 999; + char f = 'X'; + char *endptr = &f; + const char *str = "123"; + int r; + + r = parse_uint(str, &i, &endptr, 0); + + g_assert_cmpint(r, ==, 0); + g_assert_cmpint(i, ==, 123); + g_assert(endptr == str + strlen(str)); +} + +static void test_parse_uint_octal(void) +{ + unsigned long long i = 999; + char f = 'X'; + char *endptr = &f; + const char *str = "0123"; + int r; + + r = parse_uint(str, &i, &endptr, 0); + + g_assert_cmpint(r, ==, 0); + g_assert_cmpint(i, ==, 0123); + g_assert(endptr == str + strlen(str)); +} + +static void test_parse_uint_decimal(void) +{ + unsigned long long i = 999; + char f = 'X'; + char *endptr = &f; + const char *str = "0123"; + int r; + + r = parse_uint(str, &i, &endptr, 10); + + g_assert_cmpint(r, ==, 0); + g_assert_cmpint(i, ==, 123); + g_assert(endptr == str + strlen(str)); +} + + +static void test_parse_uint_llong_max(void) +{ + unsigned long long i = 999; + char f = 'X'; + char *endptr = &f; + char *str = g_strdup_printf("%llu", (unsigned long long)LLONG_MAX + 1); + int r; + + r = parse_uint(str, &i, &endptr, 0); + + g_assert_cmpint(r, ==, 0); + g_assert_cmpint(i, ==, (unsigned long long)LLONG_MAX + 1); + g_assert(endptr == str + strlen(str)); + + g_free(str); +} + +static void test_parse_uint_overflow(void) +{ + unsigned long long i = 999; + char f = 'X'; + char *endptr = &f; + const char *str = "99999999999999999999999999999999999999"; + int r; + + r = parse_uint(str, &i, &endptr, 0); + + g_assert_cmpint(r, ==, -ERANGE); + g_assert_cmpint(i, ==, ULLONG_MAX); + g_assert(endptr == str + strlen(str)); +} + +static void test_parse_uint_negative(void) +{ + unsigned long long i = 999; + char f = 'X'; + char *endptr = &f; + const char *str = " \t -321"; + int r; + + r = parse_uint(str, &i, &endptr, 0); + + g_assert_cmpint(r, ==, -ERANGE); + g_assert_cmpint(i, ==, 0); + g_assert(endptr == str + strlen(str)); +} + + +static void test_parse_uint_full_trailing(void) +{ + unsigned long long i = 999; + const char *str = "123xxx"; + int r; + + r = parse_uint_full(str, &i, 0); + + g_assert_cmpint(r, ==, -EINVAL); + g_assert_cmpint(i, ==, 0); +} + +static void test_parse_uint_full_correct(void) +{ + unsigned long long i = 999; + const char *str = "123"; + int r; + + r = parse_uint_full(str, &i, 0); + + g_assert_cmpint(r, ==, 0); + g_assert_cmpint(i, ==, 123); +} + +static void test_qemu_strtoi_correct(void) +{ + const char *str = "12345 foo"; + char f = 'X'; + const char *endptr = &f; + int res = 999; + int err; + + err = qemu_strtoi(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 12345); + g_assert(endptr == str + 5); +} + +static void test_qemu_strtoi_null(void) +{ + char f = 'X'; + const char *endptr = &f; + int res = 999; + int err; + + err = qemu_strtoi(NULL, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == NULL); +} + +static void test_qemu_strtoi_empty(void) +{ + const char *str = ""; + char f = 'X'; + const char *endptr = &f; + int res = 999; + int err; + + err = qemu_strtoi(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); +} + +static void test_qemu_strtoi_whitespace(void) +{ + const char *str = " \t "; + char f = 'X'; + const char *endptr = &f; + int res = 999; + int err; + + err = qemu_strtoi(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); +} + +static void test_qemu_strtoi_invalid(void) +{ + const char *str = " xxxx \t abc"; + char f = 'X'; + const char *endptr = &f; + int res = 999; + int err; + + err = qemu_strtoi(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); +} + +static void test_qemu_strtoi_trailing(void) +{ + const char *str = "123xxx"; + char f = 'X'; + const char *endptr = &f; + int res = 999; + int err; + + err = qemu_strtoi(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 123); + g_assert(endptr == str + 3); +} + +static void test_qemu_strtoi_octal(void) +{ + const char *str = "0123"; + char f = 'X'; + const char *endptr = &f; + int res = 999; + int err; + + err = qemu_strtoi(str, &endptr, 8, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0123); + g_assert(endptr == str + strlen(str)); + + res = 999; + endptr = &f; + err = qemu_strtoi(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0123); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtoi_decimal(void) +{ + const char *str = "0123"; + char f = 'X'; + const char *endptr = &f; + int res = 999; + int err; + + err = qemu_strtoi(str, &endptr, 10, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 123); + g_assert(endptr == str + strlen(str)); + + str = "123"; + res = 999; + endptr = &f; + err = qemu_strtoi(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 123); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtoi_hex(void) +{ + const char *str = "0123"; + char f = 'X'; + const char *endptr = &f; + int res = 999; + int err; + + err = qemu_strtoi(str, &endptr, 16, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0x123); + g_assert(endptr == str + strlen(str)); + + str = "0x123"; + res = 999; + endptr = &f; + err = qemu_strtoi(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0x123); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtoi_max(void) +{ + char *str = g_strdup_printf("%d", INT_MAX); + char f = 'X'; + const char *endptr = &f; + int res = 999; + int err; + + err = qemu_strtoi(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, INT_MAX); + g_assert(endptr == str + strlen(str)); + g_free(str); +} + +static void test_qemu_strtoi_overflow(void) +{ + char *str = g_strdup_printf("%lld", (long long)INT_MAX + 1ll); + char f = 'X'; + const char *endptr = &f; + int res = 999; + int err; + + err = qemu_strtoi(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, INT_MAX); + g_assert(endptr == str + strlen(str)); + g_free(str); +} + +static void test_qemu_strtoi_underflow(void) +{ + char *str = g_strdup_printf("%lld", (long long)INT_MIN - 1ll); + char f = 'X'; + const char *endptr = &f; + int res = 999; + int err; + + err = qemu_strtoi(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, INT_MIN); + g_assert(endptr == str + strlen(str)); + g_free(str); +} + +static void test_qemu_strtoi_negative(void) +{ + const char *str = " \t -321"; + char f = 'X'; + const char *endptr = &f; + int res = 999; + int err; + + err = qemu_strtoi(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, -321); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtoi_full_correct(void) +{ + const char *str = "123"; + int res = 999; + int err; + + err = qemu_strtoi(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 123); +} + +static void test_qemu_strtoi_full_null(void) +{ + char f = 'X'; + const char *endptr = &f; + int res = 999; + int err; + + err = qemu_strtoi(NULL, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == NULL); +} + +static void test_qemu_strtoi_full_empty(void) +{ + const char *str = ""; + int res = 999L; + int err; + + err = qemu_strtoi(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); +} + +static void test_qemu_strtoi_full_negative(void) +{ + const char *str = " \t -321"; + int res = 999; + int err; + + err = qemu_strtoi(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, -321); +} + +static void test_qemu_strtoi_full_trailing(void) +{ + const char *str = "123xxx"; + int res; + int err; + + err = qemu_strtoi(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); +} + +static void test_qemu_strtoi_full_max(void) +{ + char *str = g_strdup_printf("%d", INT_MAX); + int res; + int err; + + err = qemu_strtoi(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, INT_MAX); + g_free(str); +} + +static void test_qemu_strtoui_correct(void) +{ + const char *str = "12345 foo"; + char f = 'X'; + const char *endptr = &f; + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, 12345); + g_assert(endptr == str + 5); +} + +static void test_qemu_strtoui_null(void) +{ + char f = 'X'; + const char *endptr = &f; + unsigned int res = 999; + int err; + + err = qemu_strtoui(NULL, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == NULL); +} + +static void test_qemu_strtoui_empty(void) +{ + const char *str = ""; + char f = 'X'; + const char *endptr = &f; + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); +} + +static void test_qemu_strtoui_whitespace(void) +{ + const char *str = " \t "; + char f = 'X'; + const char *endptr = &f; + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); +} + +static void test_qemu_strtoui_invalid(void) +{ + const char *str = " xxxx \t abc"; + char f = 'X'; + const char *endptr = &f; + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); +} + +static void test_qemu_strtoui_trailing(void) +{ + const char *str = "123xxx"; + char f = 'X'; + const char *endptr = &f; + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, 123); + g_assert(endptr == str + 3); +} + +static void test_qemu_strtoui_octal(void) +{ + const char *str = "0123"; + char f = 'X'; + const char *endptr = &f; + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, &endptr, 8, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, 0123); + g_assert(endptr == str + strlen(str)); + + res = 999; + endptr = &f; + err = qemu_strtoui(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, 0123); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtoui_decimal(void) +{ + const char *str = "0123"; + char f = 'X'; + const char *endptr = &f; + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, &endptr, 10, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, 123); + g_assert(endptr == str + strlen(str)); + + str = "123"; + res = 999; + endptr = &f; + err = qemu_strtoui(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, 123); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtoui_hex(void) +{ + const char *str = "0123"; + char f = 'X'; + const char *endptr = &f; + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, &endptr, 16, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmphex(res, ==, 0x123); + g_assert(endptr == str + strlen(str)); + + str = "0x123"; + res = 999; + endptr = &f; + err = qemu_strtoui(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmphex(res, ==, 0x123); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtoui_max(void) +{ + char *str = g_strdup_printf("%u", UINT_MAX); + char f = 'X'; + const char *endptr = &f; + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmphex(res, ==, UINT_MAX); + g_assert(endptr == str + strlen(str)); + g_free(str); +} + +static void test_qemu_strtoui_overflow(void) +{ + char *str = g_strdup_printf("%lld", (long long)UINT_MAX + 1ll); + char f = 'X'; + const char *endptr = &f; + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmphex(res, ==, UINT_MAX); + g_assert(endptr == str + strlen(str)); + g_free(str); +} + +static void test_qemu_strtoui_underflow(void) +{ + char *str = g_strdup_printf("%lld", (long long)INT_MIN - 1ll); + char f = 'X'; + const char *endptr = &f; + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, (unsigned int)-1); + g_assert(endptr == str + strlen(str)); + g_free(str); +} + +static void test_qemu_strtoui_negative(void) +{ + const char *str = " \t -321"; + char f = 'X'; + const char *endptr = &f; + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, (unsigned int)-321); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtoui_full_correct(void) +{ + const char *str = "123"; + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, 123); +} + +static void test_qemu_strtoui_full_null(void) +{ + unsigned int res = 999; + int err; + + err = qemu_strtoui(NULL, NULL, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); +} + +static void test_qemu_strtoui_full_empty(void) +{ + const char *str = ""; + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); +} +static void test_qemu_strtoui_full_negative(void) +{ + const char *str = " \t -321"; + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, NULL, 0, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, (unsigned int)-321); +} + +static void test_qemu_strtoui_full_trailing(void) +{ + const char *str = "123xxx"; + unsigned int res; + int err; + + err = qemu_strtoui(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); +} + +static void test_qemu_strtoui_full_max(void) +{ + char *str = g_strdup_printf("%u", UINT_MAX); + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmphex(res, ==, UINT_MAX); + g_free(str); +} + +static void test_qemu_strtol_correct(void) +{ + const char *str = "12345 foo"; + char f = 'X'; + const char *endptr = &f; + long res = 999; + int err; + + err = qemu_strtol(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 12345); + g_assert(endptr == str + 5); +} + +static void test_qemu_strtol_null(void) +{ + char f = 'X'; + const char *endptr = &f; + long res = 999; + int err; + + err = qemu_strtol(NULL, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == NULL); +} + +static void test_qemu_strtol_empty(void) +{ + const char *str = ""; + char f = 'X'; + const char *endptr = &f; + long res = 999; + int err; + + err = qemu_strtol(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); +} + +static void test_qemu_strtol_whitespace(void) +{ + const char *str = " \t "; + char f = 'X'; + const char *endptr = &f; + long res = 999; + int err; + + err = qemu_strtol(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); +} + +static void test_qemu_strtol_invalid(void) +{ + const char *str = " xxxx \t abc"; + char f = 'X'; + const char *endptr = &f; + long res = 999; + int err; + + err = qemu_strtol(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); +} + +static void test_qemu_strtol_trailing(void) +{ + const char *str = "123xxx"; + char f = 'X'; + const char *endptr = &f; + long res = 999; + int err; + + err = qemu_strtol(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 123); + g_assert(endptr == str + 3); +} + +static void test_qemu_strtol_octal(void) +{ + const char *str = "0123"; + char f = 'X'; + const char *endptr = &f; + long res = 999; + int err; + + err = qemu_strtol(str, &endptr, 8, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0123); + g_assert(endptr == str + strlen(str)); + + res = 999; + endptr = &f; + err = qemu_strtol(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0123); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtol_decimal(void) +{ + const char *str = "0123"; + char f = 'X'; + const char *endptr = &f; + long res = 999; + int err; + + err = qemu_strtol(str, &endptr, 10, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 123); + g_assert(endptr == str + strlen(str)); + + str = "123"; + res = 999; + endptr = &f; + err = qemu_strtol(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 123); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtol_hex(void) +{ + const char *str = "0123"; + char f = 'X'; + const char *endptr = &f; + long res = 999; + int err; + + err = qemu_strtol(str, &endptr, 16, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0x123); + g_assert(endptr == str + strlen(str)); + + str = "0x123"; + res = 999; + endptr = &f; + err = qemu_strtol(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0x123); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtol_max(void) +{ + char *str = g_strdup_printf("%ld", LONG_MAX); + char f = 'X'; + const char *endptr = &f; + long res = 999; + int err; + + err = qemu_strtol(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, LONG_MAX); + g_assert(endptr == str + strlen(str)); + g_free(str); +} + +static void test_qemu_strtol_overflow(void) +{ + const char *str = "99999999999999999999999999999999999999999999"; + char f = 'X'; + const char *endptr = &f; + long res = 999; + int err; + + err = qemu_strtol(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, LONG_MAX); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtol_underflow(void) +{ + const char *str = "-99999999999999999999999999999999999999999999"; + char f = 'X'; + const char *endptr = &f; + long res = 999; + int err; + + err = qemu_strtol(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, LONG_MIN); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtol_negative(void) +{ + const char *str = " \t -321"; + char f = 'X'; + const char *endptr = &f; + long res = 999; + int err; + + err = qemu_strtol(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, -321); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtol_full_correct(void) +{ + const char *str = "123"; + long res = 999; + int err; + + err = qemu_strtol(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 123); +} + +static void test_qemu_strtol_full_null(void) +{ + char f = 'X'; + const char *endptr = &f; + long res = 999; + int err; + + err = qemu_strtol(NULL, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == NULL); +} + +static void test_qemu_strtol_full_empty(void) +{ + const char *str = ""; + long res = 999L; + int err; + + err = qemu_strtol(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); +} + +static void test_qemu_strtol_full_negative(void) +{ + const char *str = " \t -321"; + long res = 999; + int err; + + err = qemu_strtol(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, -321); +} + +static void test_qemu_strtol_full_trailing(void) +{ + const char *str = "123xxx"; + long res; + int err; + + err = qemu_strtol(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); +} + +static void test_qemu_strtol_full_max(void) +{ + char *str = g_strdup_printf("%ld", LONG_MAX); + long res; + int err; + + err = qemu_strtol(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, LONG_MAX); + g_free(str); +} + +static void test_qemu_strtoul_correct(void) +{ + const char *str = "12345 foo"; + char f = 'X'; + const char *endptr = &f; + unsigned long res = 999; + int err; + + err = qemu_strtoul(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, 12345); + g_assert(endptr == str + 5); +} + +static void test_qemu_strtoul_null(void) +{ + char f = 'X'; + const char *endptr = &f; + unsigned long res = 999; + int err; + + err = qemu_strtoul(NULL, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == NULL); +} + +static void test_qemu_strtoul_empty(void) +{ + const char *str = ""; + char f = 'X'; + const char *endptr = &f; + unsigned long res = 999; + int err; + + err = qemu_strtoul(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); +} + +static void test_qemu_strtoul_whitespace(void) +{ + const char *str = " \t "; + char f = 'X'; + const char *endptr = &f; + unsigned long res = 999; + int err; + + err = qemu_strtoul(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); +} + +static void test_qemu_strtoul_invalid(void) +{ + const char *str = " xxxx \t abc"; + char f = 'X'; + const char *endptr = &f; + unsigned long res = 999; + int err; + + err = qemu_strtoul(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); +} + +static void test_qemu_strtoul_trailing(void) +{ + const char *str = "123xxx"; + char f = 'X'; + const char *endptr = &f; + unsigned long res = 999; + int err; + + err = qemu_strtoul(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, 123); + g_assert(endptr == str + 3); +} + +static void test_qemu_strtoul_octal(void) +{ + const char *str = "0123"; + char f = 'X'; + const char *endptr = &f; + unsigned long res = 999; + int err; + + err = qemu_strtoul(str, &endptr, 8, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, 0123); + g_assert(endptr == str + strlen(str)); + + res = 999; + endptr = &f; + err = qemu_strtoul(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, 0123); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtoul_decimal(void) +{ + const char *str = "0123"; + char f = 'X'; + const char *endptr = &f; + unsigned long res = 999; + int err; + + err = qemu_strtoul(str, &endptr, 10, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, 123); + g_assert(endptr == str + strlen(str)); + + str = "123"; + res = 999; + endptr = &f; + err = qemu_strtoul(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, 123); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtoul_hex(void) +{ + const char *str = "0123"; + char f = 'X'; + const char *endptr = &f; + unsigned long res = 999; + int err; + + err = qemu_strtoul(str, &endptr, 16, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmphex(res, ==, 0x123); + g_assert(endptr == str + strlen(str)); + + str = "0x123"; + res = 999; + endptr = &f; + err = qemu_strtoul(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmphex(res, ==, 0x123); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtoul_max(void) +{ + char *str = g_strdup_printf("%lu", ULONG_MAX); + char f = 'X'; + const char *endptr = &f; + unsigned long res = 999; + int err; + + err = qemu_strtoul(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmphex(res, ==, ULONG_MAX); + g_assert(endptr == str + strlen(str)); + g_free(str); +} + +static void test_qemu_strtoul_overflow(void) +{ + const char *str = "99999999999999999999999999999999999999999999"; + char f = 'X'; + const char *endptr = &f; + unsigned long res = 999; + int err; + + err = qemu_strtoul(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmphex(res, ==, ULONG_MAX); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtoul_underflow(void) +{ + const char *str = "-99999999999999999999999999999999999999999999"; + char f = 'X'; + const char *endptr = &f; + unsigned long res = 999; + int err; + + err = qemu_strtoul(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, -1ul); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtoul_negative(void) +{ + const char *str = " \t -321"; + char f = 'X'; + const char *endptr = &f; + unsigned long res = 999; + int err; + + err = qemu_strtoul(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, -321ul); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtoul_full_correct(void) +{ + const char *str = "123"; + unsigned long res = 999; + int err; + + err = qemu_strtoul(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, 123); +} + +static void test_qemu_strtoul_full_null(void) +{ + unsigned long res = 999; + int err; + + err = qemu_strtoul(NULL, NULL, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); +} + +static void test_qemu_strtoul_full_empty(void) +{ + const char *str = ""; + unsigned long res = 999; + int err; + + err = qemu_strtoul(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); +} +static void test_qemu_strtoul_full_negative(void) +{ + const char *str = " \t -321"; + unsigned long res = 999; + int err; + + err = qemu_strtoul(str, NULL, 0, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, -321ul); +} + +static void test_qemu_strtoul_full_trailing(void) +{ + const char *str = "123xxx"; + unsigned long res; + int err; + + err = qemu_strtoul(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); +} + +static void test_qemu_strtoul_full_max(void) +{ + char *str = g_strdup_printf("%lu", ULONG_MAX); + unsigned long res = 999; + int err; + + err = qemu_strtoul(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmphex(res, ==, ULONG_MAX); + g_free(str); +} + +static void test_qemu_strtoi64_correct(void) +{ + const char *str = "12345 foo"; + char f = 'X'; + const char *endptr = &f; + int64_t res = 999; + int err; + + err = qemu_strtoi64(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 12345); + g_assert(endptr == str + 5); +} + +static void test_qemu_strtoi64_null(void) +{ + char f = 'X'; + const char *endptr = &f; + int64_t res = 999; + int err; + + err = qemu_strtoi64(NULL, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == NULL); +} + +static void test_qemu_strtoi64_empty(void) +{ + const char *str = ""; + char f = 'X'; + const char *endptr = &f; + int64_t res = 999; + int err; + + err = qemu_strtoi64(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); +} + +static void test_qemu_strtoi64_whitespace(void) +{ + const char *str = " \t "; + char f = 'X'; + const char *endptr = &f; + int64_t res = 999; + int err; + + err = qemu_strtoi64(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); +} + +static void test_qemu_strtoi64_invalid(void) +{ + const char *str = " xxxx \t abc"; + char f = 'X'; + const char *endptr = &f; + int64_t res = 999; + int err; + + err = qemu_strtoi64(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); +} + +static void test_qemu_strtoi64_trailing(void) +{ + const char *str = "123xxx"; + char f = 'X'; + const char *endptr = &f; + int64_t res = 999; + int err; + + err = qemu_strtoi64(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 123); + g_assert(endptr == str + 3); +} + +static void test_qemu_strtoi64_octal(void) +{ + const char *str = "0123"; + char f = 'X'; + const char *endptr = &f; + int64_t res = 999; + int err; + + err = qemu_strtoi64(str, &endptr, 8, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0123); + g_assert(endptr == str + strlen(str)); + + endptr = &f; + res = 999; + err = qemu_strtoi64(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0123); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtoi64_decimal(void) +{ + const char *str = "0123"; + char f = 'X'; + const char *endptr = &f; + int64_t res = 999; + int err; + + err = qemu_strtoi64(str, &endptr, 10, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 123); + g_assert(endptr == str + strlen(str)); + + str = "123"; + endptr = &f; + res = 999; + err = qemu_strtoi64(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 123); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtoi64_hex(void) +{ + const char *str = "0123"; + char f = 'X'; + const char *endptr = &f; + int64_t res = 999; + int err; + + err = qemu_strtoi64(str, &endptr, 16, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0x123); + g_assert(endptr == str + strlen(str)); + + str = "0x123"; + endptr = &f; + res = 999; + err = qemu_strtoi64(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0x123); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtoi64_max(void) +{ + char *str = g_strdup_printf("%lld", LLONG_MAX); + char f = 'X'; + const char *endptr = &f; + int64_t res = 999; + int err; + + err = qemu_strtoi64(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, LLONG_MAX); + g_assert(endptr == str + strlen(str)); + g_free(str); +} + +static void test_qemu_strtoi64_overflow(void) +{ + const char *str = "99999999999999999999999999999999999999999999"; + char f = 'X'; + const char *endptr = &f; + int64_t res = 999; + int err; + + err = qemu_strtoi64(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, LLONG_MAX); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtoi64_underflow(void) +{ + const char *str = "-99999999999999999999999999999999999999999999"; + char f = 'X'; + const char *endptr = &f; + int64_t res = 999; + int err; + + err = qemu_strtoi64(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, LLONG_MIN); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtoi64_negative(void) +{ + const char *str = " \t -321"; + char f = 'X'; + const char *endptr = &f; + int64_t res = 999; + int err; + + err = qemu_strtoi64(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, -321); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtoi64_full_correct(void) +{ + const char *str = "123"; + int64_t res = 999; + int err; + + err = qemu_strtoi64(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 123); +} + +static void test_qemu_strtoi64_full_null(void) +{ + int64_t res = 999; + int err; + + err = qemu_strtoi64(NULL, NULL, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); +} + +static void test_qemu_strtoi64_full_empty(void) +{ + const char *str = ""; + int64_t res = 999; + int err; + + err = qemu_strtoi64(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); +} + +static void test_qemu_strtoi64_full_negative(void) +{ + const char *str = " \t -321"; + int64_t res = 999; + int err; + + err = qemu_strtoi64(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, -321); +} + +static void test_qemu_strtoi64_full_trailing(void) +{ + const char *str = "123xxx"; + int64_t res = 999; + int err; + + err = qemu_strtoi64(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); +} + +static void test_qemu_strtoi64_full_max(void) +{ + + char *str = g_strdup_printf("%lld", LLONG_MAX); + int64_t res; + int err; + + err = qemu_strtoi64(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, LLONG_MAX); + g_free(str); +} + +static void test_qemu_strtou64_correct(void) +{ + const char *str = "12345 foo"; + char f = 'X'; + const char *endptr = &f; + uint64_t res = 999; + int err; + + err = qemu_strtou64(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, 12345); + g_assert(endptr == str + 5); +} + +static void test_qemu_strtou64_null(void) +{ + char f = 'X'; + const char *endptr = &f; + uint64_t res = 999; + int err; + + err = qemu_strtou64(NULL, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == NULL); +} + +static void test_qemu_strtou64_empty(void) +{ + const char *str = ""; + char f = 'X'; + const char *endptr = &f; + uint64_t res = 999; + int err; + + err = qemu_strtou64(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); +} + +static void test_qemu_strtou64_whitespace(void) +{ + const char *str = " \t "; + char f = 'X'; + const char *endptr = &f; + uint64_t res = 999; + int err; + + err = qemu_strtou64(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); +} + +static void test_qemu_strtou64_invalid(void) +{ + const char *str = " xxxx \t abc"; + char f = 'X'; + const char *endptr = &f; + uint64_t res = 999; + int err; + + err = qemu_strtou64(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); +} + +static void test_qemu_strtou64_trailing(void) +{ + const char *str = "123xxx"; + char f = 'X'; + const char *endptr = &f; + uint64_t res = 999; + int err; + + err = qemu_strtou64(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, 123); + g_assert(endptr == str + 3); +} + +static void test_qemu_strtou64_octal(void) +{ + const char *str = "0123"; + char f = 'X'; + const char *endptr = &f; + uint64_t res = 999; + int err; + + err = qemu_strtou64(str, &endptr, 8, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, 0123); + g_assert(endptr == str + strlen(str)); + + endptr = &f; + res = 999; + err = qemu_strtou64(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, 0123); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtou64_decimal(void) +{ + const char *str = "0123"; + char f = 'X'; + const char *endptr = &f; + uint64_t res = 999; + int err; + + err = qemu_strtou64(str, &endptr, 10, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, 123); + g_assert(endptr == str + strlen(str)); + + str = "123"; + endptr = &f; + res = 999; + err = qemu_strtou64(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, 123); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtou64_hex(void) +{ + const char *str = "0123"; + char f = 'X'; + const char *endptr = &f; + uint64_t res = 999; + int err; + + err = qemu_strtou64(str, &endptr, 16, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmphex(res, ==, 0x123); + g_assert(endptr == str + strlen(str)); + + str = "0x123"; + endptr = &f; + res = 999; + err = qemu_strtou64(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmphex(res, ==, 0x123); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtou64_max(void) +{ + char *str = g_strdup_printf("%llu", ULLONG_MAX); + char f = 'X'; + const char *endptr = &f; + uint64_t res = 999; + int err; + + err = qemu_strtou64(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmphex(res, ==, ULLONG_MAX); + g_assert(endptr == str + strlen(str)); + g_free(str); +} + +static void test_qemu_strtou64_overflow(void) +{ + const char *str = "99999999999999999999999999999999999999999999"; + char f = 'X'; + const char *endptr = &f; + uint64_t res = 999; + int err; + + err = qemu_strtou64(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmphex(res, ==, ULLONG_MAX); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtou64_underflow(void) +{ + const char *str = "-99999999999999999999999999999999999999999999"; + char f = 'X'; + const char *endptr = &f; + uint64_t res = 999; + int err; + + err = qemu_strtou64(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmphex(res, ==, -1ull); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtou64_negative(void) +{ + const char *str = " \t -321"; + char f = 'X'; + const char *endptr = &f; + uint64_t res = 999; + int err; + + err = qemu_strtou64(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, -321ull); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtou64_full_correct(void) +{ + const char *str = "18446744073709551614"; + uint64_t res = 999; + int err; + + err = qemu_strtou64(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, 18446744073709551614ull); +} + +static void test_qemu_strtou64_full_null(void) +{ + uint64_t res = 999; + int err; + + err = qemu_strtou64(NULL, NULL, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); +} + +static void test_qemu_strtou64_full_empty(void) +{ + const char *str = ""; + uint64_t res = 999; + int err; + + err = qemu_strtou64(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); +} + +static void test_qemu_strtou64_full_negative(void) +{ + const char *str = " \t -321"; + uint64_t res = 999; + int err; + + err = qemu_strtou64(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, -321ull); +} + +static void test_qemu_strtou64_full_trailing(void) +{ + const char *str = "18446744073709551614xxxxxx"; + uint64_t res = 999; + int err; + + err = qemu_strtou64(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); +} + +static void test_qemu_strtou64_full_max(void) +{ + char *str = g_strdup_printf("%lld", ULLONG_MAX); + uint64_t res = 999; + int err; + + err = qemu_strtou64(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmphex(res, ==, ULLONG_MAX); + g_free(str); +} + +static void test_qemu_strtosz_simple(void) +{ + const char *str; + const char *endptr; + int err; + uint64_t res = 0xbaadf00d; + + str = "0"; + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0); + g_assert(endptr == str + 1); + + str = "12345"; + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 12345); + g_assert(endptr == str + 5); + + err = qemu_strtosz(str, NULL, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 12345); + + /* Note: precision is 53 bits since we're parsing with strtod() */ + + str = "9007199254740991"; /* 2^53-1 */ + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0x1fffffffffffff); + g_assert(endptr == str + 16); + + str = "9007199254740992"; /* 2^53 */ + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0x20000000000000); + g_assert(endptr == str + 16); + + str = "9007199254740993"; /* 2^53+1 */ + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0x20000000000000); /* rounded to 53 bits */ + g_assert(endptr == str + 16); + + str = "18446744073709549568"; /* 0xfffffffffffff800 (53 msbs set) */ + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0xfffffffffffff800); + g_assert(endptr == str + 20); + + str = "18446744073709550591"; /* 0xfffffffffffffbff */ + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0xfffffffffffff800); /* rounded to 53 bits */ + g_assert(endptr == str + 20); + + /* 0x7ffffffffffffe00..0x7fffffffffffffff get rounded to + * 0x8000000000000000, thus -ERANGE; see test_qemu_strtosz_erange() */ +} + +static void test_qemu_strtosz_units(void) +{ + const char *none = "1"; + const char *b = "1B"; + const char *k = "1K"; + const char *m = "1M"; + const char *g = "1G"; + const char *t = "1T"; + const char *p = "1P"; + const char *e = "1E"; + int err; + const char *endptr; + uint64_t res = 0xbaadf00d; + + /* default is M */ + err = qemu_strtosz_MiB(none, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, MiB); + g_assert(endptr == none + 1); + + err = qemu_strtosz(b, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 1); + g_assert(endptr == b + 2); + + err = qemu_strtosz(k, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, KiB); + g_assert(endptr == k + 2); + + err = qemu_strtosz(m, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, MiB); + g_assert(endptr == m + 2); + + err = qemu_strtosz(g, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, GiB); + g_assert(endptr == g + 2); + + err = qemu_strtosz(t, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, TiB); + g_assert(endptr == t + 2); + + err = qemu_strtosz(p, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, PiB); + g_assert(endptr == p + 2); + + err = qemu_strtosz(e, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, EiB); + g_assert(endptr == e + 2); +} + +static void test_qemu_strtosz_float(void) +{ + const char *str = "12.345M"; + int err; + const char *endptr; + uint64_t res = 0xbaadf00d; + + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 12.345 * MiB); + g_assert(endptr == str + 7); +} + +static void test_qemu_strtosz_invalid(void) +{ + const char *str; + const char *endptr; + int err; + uint64_t res = 0xbaadf00d; + + str = ""; + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); + + str = " \t "; + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); + + str = "crap"; + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); + + str = "inf"; + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); + + str = "NaN"; + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); +} + +static void test_qemu_strtosz_trailing(void) +{ + const char *str; + const char *endptr; + int err; + uint64_t res = 0xbaadf00d; + + str = "123xxx"; + err = qemu_strtosz_MiB(str, &endptr, &res); + g_assert_cmpint(res, ==, 123 * MiB); + g_assert(endptr == str + 3); + + err = qemu_strtosz(str, NULL, &res); + g_assert_cmpint(err, ==, -EINVAL); + + str = "1kiB"; + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 1024); + g_assert(endptr == str + 2); + + err = qemu_strtosz(str, NULL, &res); + g_assert_cmpint(err, ==, -EINVAL); +} + +static void test_qemu_strtosz_erange(void) +{ + const char *str; + const char *endptr; + int err; + uint64_t res = 0xbaadf00d; + + str = "-1"; + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert(endptr == str + 2); + + str = "18446744073709550592"; /* 0xfffffffffffffc00 */ + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert(endptr == str + 20); + + str = "18446744073709551615"; /* 2^64-1 */ + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert(endptr == str + 20); + + str = "18446744073709551616"; /* 2^64 */ + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert(endptr == str + 20); + + str = "20E"; + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert(endptr == str + 3); +} + +static void test_qemu_strtosz_metric(void) +{ + const char *str = "12345k"; + int err; + const char *endptr; + uint64_t res = 0xbaadf00d; + + err = qemu_strtosz_metric(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 12345000); + g_assert(endptr == str + 6); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/cutils/parse_uint/null", test_parse_uint_null); + g_test_add_func("/cutils/parse_uint/empty", test_parse_uint_empty); + g_test_add_func("/cutils/parse_uint/whitespace", + test_parse_uint_whitespace); + g_test_add_func("/cutils/parse_uint/invalid", test_parse_uint_invalid); + g_test_add_func("/cutils/parse_uint/trailing", test_parse_uint_trailing); + g_test_add_func("/cutils/parse_uint/correct", test_parse_uint_correct); + g_test_add_func("/cutils/parse_uint/octal", test_parse_uint_octal); + g_test_add_func("/cutils/parse_uint/decimal", test_parse_uint_decimal); + g_test_add_func("/cutils/parse_uint/llong_max", test_parse_uint_llong_max); + g_test_add_func("/cutils/parse_uint/overflow", test_parse_uint_overflow); + g_test_add_func("/cutils/parse_uint/negative", test_parse_uint_negative); + g_test_add_func("/cutils/parse_uint_full/trailing", + test_parse_uint_full_trailing); + g_test_add_func("/cutils/parse_uint_full/correct", + test_parse_uint_full_correct); + + /* qemu_strtoi() tests */ + g_test_add_func("/cutils/qemu_strtoi/correct", + test_qemu_strtoi_correct); + g_test_add_func("/cutils/qemu_strtoi/null", + test_qemu_strtoi_null); + g_test_add_func("/cutils/qemu_strtoi/empty", + test_qemu_strtoi_empty); + g_test_add_func("/cutils/qemu_strtoi/whitespace", + test_qemu_strtoi_whitespace); + g_test_add_func("/cutils/qemu_strtoi/invalid", + test_qemu_strtoi_invalid); + g_test_add_func("/cutils/qemu_strtoi/trailing", + test_qemu_strtoi_trailing); + g_test_add_func("/cutils/qemu_strtoi/octal", + test_qemu_strtoi_octal); + g_test_add_func("/cutils/qemu_strtoi/decimal", + test_qemu_strtoi_decimal); + g_test_add_func("/cutils/qemu_strtoi/hex", + test_qemu_strtoi_hex); + g_test_add_func("/cutils/qemu_strtoi/max", + test_qemu_strtoi_max); + g_test_add_func("/cutils/qemu_strtoi/overflow", + test_qemu_strtoi_overflow); + g_test_add_func("/cutils/qemu_strtoi/underflow", + test_qemu_strtoi_underflow); + g_test_add_func("/cutils/qemu_strtoi/negative", + test_qemu_strtoi_negative); + g_test_add_func("/cutils/qemu_strtoi_full/correct", + test_qemu_strtoi_full_correct); + g_test_add_func("/cutils/qemu_strtoi_full/null", + test_qemu_strtoi_full_null); + g_test_add_func("/cutils/qemu_strtoi_full/empty", + test_qemu_strtoi_full_empty); + g_test_add_func("/cutils/qemu_strtoi_full/negative", + test_qemu_strtoi_full_negative); + g_test_add_func("/cutils/qemu_strtoi_full/trailing", + test_qemu_strtoi_full_trailing); + g_test_add_func("/cutils/qemu_strtoi_full/max", + test_qemu_strtoi_full_max); + + /* qemu_strtoui() tests */ + g_test_add_func("/cutils/qemu_strtoui/correct", + test_qemu_strtoui_correct); + g_test_add_func("/cutils/qemu_strtoui/null", + test_qemu_strtoui_null); + g_test_add_func("/cutils/qemu_strtoui/empty", + test_qemu_strtoui_empty); + g_test_add_func("/cutils/qemu_strtoui/whitespace", + test_qemu_strtoui_whitespace); + g_test_add_func("/cutils/qemu_strtoui/invalid", + test_qemu_strtoui_invalid); + g_test_add_func("/cutils/qemu_strtoui/trailing", + test_qemu_strtoui_trailing); + g_test_add_func("/cutils/qemu_strtoui/octal", + test_qemu_strtoui_octal); + g_test_add_func("/cutils/qemu_strtoui/decimal", + test_qemu_strtoui_decimal); + g_test_add_func("/cutils/qemu_strtoui/hex", + test_qemu_strtoui_hex); + g_test_add_func("/cutils/qemu_strtoui/max", + test_qemu_strtoui_max); + g_test_add_func("/cutils/qemu_strtoui/overflow", + test_qemu_strtoui_overflow); + g_test_add_func("/cutils/qemu_strtoui/underflow", + test_qemu_strtoui_underflow); + g_test_add_func("/cutils/qemu_strtoui/negative", + test_qemu_strtoui_negative); + g_test_add_func("/cutils/qemu_strtoui_full/correct", + test_qemu_strtoui_full_correct); + g_test_add_func("/cutils/qemu_strtoui_full/null", + test_qemu_strtoui_full_null); + g_test_add_func("/cutils/qemu_strtoui_full/empty", + test_qemu_strtoui_full_empty); + g_test_add_func("/cutils/qemu_strtoui_full/negative", + test_qemu_strtoui_full_negative); + g_test_add_func("/cutils/qemu_strtoui_full/trailing", + test_qemu_strtoui_full_trailing); + g_test_add_func("/cutils/qemu_strtoui_full/max", + test_qemu_strtoui_full_max); + + /* qemu_strtol() tests */ + g_test_add_func("/cutils/qemu_strtol/correct", + test_qemu_strtol_correct); + g_test_add_func("/cutils/qemu_strtol/null", + test_qemu_strtol_null); + g_test_add_func("/cutils/qemu_strtol/empty", + test_qemu_strtol_empty); + g_test_add_func("/cutils/qemu_strtol/whitespace", + test_qemu_strtol_whitespace); + g_test_add_func("/cutils/qemu_strtol/invalid", + test_qemu_strtol_invalid); + g_test_add_func("/cutils/qemu_strtol/trailing", + test_qemu_strtol_trailing); + g_test_add_func("/cutils/qemu_strtol/octal", + test_qemu_strtol_octal); + g_test_add_func("/cutils/qemu_strtol/decimal", + test_qemu_strtol_decimal); + g_test_add_func("/cutils/qemu_strtol/hex", + test_qemu_strtol_hex); + g_test_add_func("/cutils/qemu_strtol/max", + test_qemu_strtol_max); + g_test_add_func("/cutils/qemu_strtol/overflow", + test_qemu_strtol_overflow); + g_test_add_func("/cutils/qemu_strtol/underflow", + test_qemu_strtol_underflow); + g_test_add_func("/cutils/qemu_strtol/negative", + test_qemu_strtol_negative); + g_test_add_func("/cutils/qemu_strtol_full/correct", + test_qemu_strtol_full_correct); + g_test_add_func("/cutils/qemu_strtol_full/null", + test_qemu_strtol_full_null); + g_test_add_func("/cutils/qemu_strtol_full/empty", + test_qemu_strtol_full_empty); + g_test_add_func("/cutils/qemu_strtol_full/negative", + test_qemu_strtol_full_negative); + g_test_add_func("/cutils/qemu_strtol_full/trailing", + test_qemu_strtol_full_trailing); + g_test_add_func("/cutils/qemu_strtol_full/max", + test_qemu_strtol_full_max); + + /* qemu_strtoul() tests */ + g_test_add_func("/cutils/qemu_strtoul/correct", + test_qemu_strtoul_correct); + g_test_add_func("/cutils/qemu_strtoul/null", + test_qemu_strtoul_null); + g_test_add_func("/cutils/qemu_strtoul/empty", + test_qemu_strtoul_empty); + g_test_add_func("/cutils/qemu_strtoul/whitespace", + test_qemu_strtoul_whitespace); + g_test_add_func("/cutils/qemu_strtoul/invalid", + test_qemu_strtoul_invalid); + g_test_add_func("/cutils/qemu_strtoul/trailing", + test_qemu_strtoul_trailing); + g_test_add_func("/cutils/qemu_strtoul/octal", + test_qemu_strtoul_octal); + g_test_add_func("/cutils/qemu_strtoul/decimal", + test_qemu_strtoul_decimal); + g_test_add_func("/cutils/qemu_strtoul/hex", + test_qemu_strtoul_hex); + g_test_add_func("/cutils/qemu_strtoul/max", + test_qemu_strtoul_max); + g_test_add_func("/cutils/qemu_strtoul/overflow", + test_qemu_strtoul_overflow); + g_test_add_func("/cutils/qemu_strtoul/underflow", + test_qemu_strtoul_underflow); + g_test_add_func("/cutils/qemu_strtoul/negative", + test_qemu_strtoul_negative); + g_test_add_func("/cutils/qemu_strtoul_full/correct", + test_qemu_strtoul_full_correct); + g_test_add_func("/cutils/qemu_strtoul_full/null", + test_qemu_strtoul_full_null); + g_test_add_func("/cutils/qemu_strtoul_full/empty", + test_qemu_strtoul_full_empty); + g_test_add_func("/cutils/qemu_strtoul_full/negative", + test_qemu_strtoul_full_negative); + g_test_add_func("/cutils/qemu_strtoul_full/trailing", + test_qemu_strtoul_full_trailing); + g_test_add_func("/cutils/qemu_strtoul_full/max", + test_qemu_strtoul_full_max); + + /* qemu_strtoi64() tests */ + g_test_add_func("/cutils/qemu_strtoi64/correct", + test_qemu_strtoi64_correct); + g_test_add_func("/cutils/qemu_strtoi64/null", + test_qemu_strtoi64_null); + g_test_add_func("/cutils/qemu_strtoi64/empty", + test_qemu_strtoi64_empty); + g_test_add_func("/cutils/qemu_strtoi64/whitespace", + test_qemu_strtoi64_whitespace); + g_test_add_func("/cutils/qemu_strtoi64/invalid" + , + test_qemu_strtoi64_invalid); + g_test_add_func("/cutils/qemu_strtoi64/trailing", + test_qemu_strtoi64_trailing); + g_test_add_func("/cutils/qemu_strtoi64/octal", + test_qemu_strtoi64_octal); + g_test_add_func("/cutils/qemu_strtoi64/decimal", + test_qemu_strtoi64_decimal); + g_test_add_func("/cutils/qemu_strtoi64/hex", + test_qemu_strtoi64_hex); + g_test_add_func("/cutils/qemu_strtoi64/max", + test_qemu_strtoi64_max); + g_test_add_func("/cutils/qemu_strtoi64/overflow", + test_qemu_strtoi64_overflow); + g_test_add_func("/cutils/qemu_strtoi64/underflow", + test_qemu_strtoi64_underflow); + g_test_add_func("/cutils/qemu_strtoi64/negative", + test_qemu_strtoi64_negative); + g_test_add_func("/cutils/qemu_strtoi64_full/correct", + test_qemu_strtoi64_full_correct); + g_test_add_func("/cutils/qemu_strtoi64_full/null", + test_qemu_strtoi64_full_null); + g_test_add_func("/cutils/qemu_strtoi64_full/empty", + test_qemu_strtoi64_full_empty); + g_test_add_func("/cutils/qemu_strtoi64_full/negative", + test_qemu_strtoi64_full_negative); + g_test_add_func("/cutils/qemu_strtoi64_full/trailing", + test_qemu_strtoi64_full_trailing); + g_test_add_func("/cutils/qemu_strtoi64_full/max", + test_qemu_strtoi64_full_max); + + /* qemu_strtou64() tests */ + g_test_add_func("/cutils/qemu_strtou64/correct", + test_qemu_strtou64_correct); + g_test_add_func("/cutils/qemu_strtou64/null", + test_qemu_strtou64_null); + g_test_add_func("/cutils/qemu_strtou64/empty", + test_qemu_strtou64_empty); + g_test_add_func("/cutils/qemu_strtou64/whitespace", + test_qemu_strtou64_whitespace); + g_test_add_func("/cutils/qemu_strtou64/invalid", + test_qemu_strtou64_invalid); + g_test_add_func("/cutils/qemu_strtou64/trailing", + test_qemu_strtou64_trailing); + g_test_add_func("/cutils/qemu_strtou64/octal", + test_qemu_strtou64_octal); + g_test_add_func("/cutils/qemu_strtou64/decimal", + test_qemu_strtou64_decimal); + g_test_add_func("/cutils/qemu_strtou64/hex", + test_qemu_strtou64_hex); + g_test_add_func("/cutils/qemu_strtou64/max", + test_qemu_strtou64_max); + g_test_add_func("/cutils/qemu_strtou64/overflow", + test_qemu_strtou64_overflow); + g_test_add_func("/cutils/qemu_strtou64/underflow", + test_qemu_strtou64_underflow); + g_test_add_func("/cutils/qemu_strtou64/negative", + test_qemu_strtou64_negative); + g_test_add_func("/cutils/qemu_strtou64_full/correct", + test_qemu_strtou64_full_correct); + g_test_add_func("/cutils/qemu_strtou64_full/null", + test_qemu_strtou64_full_null); + g_test_add_func("/cutils/qemu_strtou64_full/empty", + test_qemu_strtou64_full_empty); + g_test_add_func("/cutils/qemu_strtou64_full/negative", + test_qemu_strtou64_full_negative); + g_test_add_func("/cutils/qemu_strtou64_full/trailing", + test_qemu_strtou64_full_trailing); + g_test_add_func("/cutils/qemu_strtou64_full/max", + test_qemu_strtou64_full_max); + + g_test_add_func("/cutils/strtosz/simple", + test_qemu_strtosz_simple); + g_test_add_func("/cutils/strtosz/units", + test_qemu_strtosz_units); + g_test_add_func("/cutils/strtosz/float", + test_qemu_strtosz_float); + g_test_add_func("/cutils/strtosz/invalid", + test_qemu_strtosz_invalid); + g_test_add_func("/cutils/strtosz/trailing", + test_qemu_strtosz_trailing); + g_test_add_func("/cutils/strtosz/erange", + test_qemu_strtosz_erange); + g_test_add_func("/cutils/strtosz/metric", + test_qemu_strtosz_metric); + + return g_test_run(); +} diff --git a/tests/unit/test-fdmon-epoll.c b/tests/unit/test-fdmon-epoll.c new file mode 100644 index 0000000000..11fd8a2fa9 --- /dev/null +++ b/tests/unit/test-fdmon-epoll.c @@ -0,0 +1,73 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * fdmon-epoll tests + * + * Copyright (c) 2020 Red Hat, Inc. + */ + +#include "qemu/osdep.h" +#include "block/aio.h" +#include "qapi/error.h" +#include "qemu/main-loop.h" + +static AioContext *ctx; + +static void dummy_fd_handler(EventNotifier *notifier) +{ + event_notifier_test_and_clear(notifier); +} + +static void add_event_notifiers(EventNotifier *notifiers, size_t n) +{ + for (size_t i = 0; i < n; i++) { + event_notifier_init(¬ifiers[i], false); + aio_set_event_notifier(ctx, ¬ifiers[i], false, + dummy_fd_handler, NULL); + } +} + +static void remove_event_notifiers(EventNotifier *notifiers, size_t n) +{ + for (size_t i = 0; i < n; i++) { + aio_set_event_notifier(ctx, ¬ifiers[i], false, NULL, NULL); + event_notifier_cleanup(¬ifiers[i]); + } +} + +/* Check that fd handlers work when external clients are disabled */ +static void test_external_disabled(void) +{ + EventNotifier notifiers[100]; + + /* fdmon-epoll is only enabled when many fd handlers are registered */ + add_event_notifiers(notifiers, G_N_ELEMENTS(notifiers)); + + event_notifier_set(¬ifiers[0]); + assert(aio_poll(ctx, true)); + + aio_disable_external(ctx); + event_notifier_set(¬ifiers[0]); + assert(aio_poll(ctx, true)); + aio_enable_external(ctx); + + remove_event_notifiers(notifiers, G_N_ELEMENTS(notifiers)); +} + +int main(int argc, char **argv) +{ + /* + * This code relies on the fact that fdmon-io_uring disables itself when + * the glib main loop is in use. The main loop uses fdmon-poll and upgrades + * to fdmon-epoll when the number of fds exceeds a threshold. + */ + qemu_init_main_loop(&error_fatal); + ctx = qemu_get_aio_context(); + + while (g_main_context_iteration(NULL, false)) { + /* Do nothing */ + } + + g_test_init(&argc, &argv, NULL); + g_test_add_func("/fdmon-epoll/external-disabled", test_external_disabled); + return g_test_run(); +} diff --git a/tests/unit/test-hbitmap.c b/tests/unit/test-hbitmap.c new file mode 100644 index 0000000000..b6726cf76b --- /dev/null +++ b/tests/unit/test-hbitmap.c @@ -0,0 +1,1119 @@ +/* + * Hierarchical bitmap unit-tests. + * + * Copyright (C) 2012 Red Hat Inc. + * + * Author: Paolo Bonzini + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu/hbitmap.h" +#include "qemu/bitmap.h" +#include "block/block.h" + +#define LOG_BITS_PER_LONG (BITS_PER_LONG == 32 ? 5 : 6) + +#define L1 BITS_PER_LONG +#define L2 (BITS_PER_LONG * L1) +#define L3 (BITS_PER_LONG * L2) + +typedef struct TestHBitmapData { + HBitmap *hb; + unsigned long *bits; + size_t size; + size_t old_size; + int granularity; +} TestHBitmapData; + + +/* Check that the HBitmap and the shadow bitmap contain the same data, + * ignoring the same "first" bits. + */ +static void hbitmap_test_check(TestHBitmapData *data, + uint64_t first) +{ + uint64_t count = 0; + size_t pos; + int bit; + HBitmapIter hbi; + int64_t i, next; + + hbitmap_iter_init(&hbi, data->hb, first); + + i = first; + for (;;) { + next = hbitmap_iter_next(&hbi); + if (next < 0) { + next = data->size; + } + + while (i < next) { + pos = i >> LOG_BITS_PER_LONG; + bit = i & (BITS_PER_LONG - 1); + i++; + g_assert_cmpint(data->bits[pos] & (1UL << bit), ==, 0); + } + + if (next == data->size) { + break; + } + + pos = i >> LOG_BITS_PER_LONG; + bit = i & (BITS_PER_LONG - 1); + i++; + count++; + g_assert_cmpint(data->bits[pos] & (1UL << bit), !=, 0); + } + + if (first == 0) { + g_assert_cmpint(count << data->granularity, ==, hbitmap_count(data->hb)); + } +} + +/* This is provided instead of a test setup function so that the sizes + are kept in the test functions (and not in main()) */ +static void hbitmap_test_init(TestHBitmapData *data, + uint64_t size, int granularity) +{ + size_t n; + data->hb = hbitmap_alloc(size, granularity); + + n = DIV_ROUND_UP(size, BITS_PER_LONG); + if (n == 0) { + n = 1; + } + data->bits = g_new0(unsigned long, n); + data->size = size; + data->granularity = granularity; + if (size) { + hbitmap_test_check(data, 0); + } +} + +static inline size_t hbitmap_test_array_size(size_t bits) +{ + size_t n = DIV_ROUND_UP(bits, BITS_PER_LONG); + return n ? n : 1; +} + +static void hbitmap_test_truncate_impl(TestHBitmapData *data, + size_t size) +{ + size_t n; + size_t m; + data->old_size = data->size; + data->size = size; + + if (data->size == data->old_size) { + return; + } + + n = hbitmap_test_array_size(size); + m = hbitmap_test_array_size(data->old_size); + data->bits = g_realloc(data->bits, sizeof(unsigned long) * n); + if (n > m) { + memset(&data->bits[m], 0x00, sizeof(unsigned long) * (n - m)); + } + + /* If we shrink to an uneven multiple of sizeof(unsigned long), + * scrub the leftover memory. */ + if (data->size < data->old_size) { + m = size % (sizeof(unsigned long) * 8); + if (m) { + unsigned long mask = (1ULL << m) - 1; + data->bits[n-1] &= mask; + } + } + + hbitmap_truncate(data->hb, size); +} + +static void hbitmap_test_teardown(TestHBitmapData *data, + const void *unused) +{ + if (data->hb) { + hbitmap_free(data->hb); + data->hb = NULL; + } + g_free(data->bits); + data->bits = NULL; +} + +/* Set a range in the HBitmap and in the shadow "simple" bitmap. + * The two bitmaps are then tested against each other. + */ +static void hbitmap_test_set(TestHBitmapData *data, + uint64_t first, uint64_t count) +{ + hbitmap_set(data->hb, first, count); + while (count-- != 0) { + size_t pos = first >> LOG_BITS_PER_LONG; + int bit = first & (BITS_PER_LONG - 1); + first++; + + data->bits[pos] |= 1UL << bit; + } + + if (data->granularity == 0) { + hbitmap_test_check(data, 0); + } +} + +/* Reset a range in the HBitmap and in the shadow "simple" bitmap. + */ +static void hbitmap_test_reset(TestHBitmapData *data, + uint64_t first, uint64_t count) +{ + hbitmap_reset(data->hb, first, count); + while (count-- != 0) { + size_t pos = first >> LOG_BITS_PER_LONG; + int bit = first & (BITS_PER_LONG - 1); + first++; + + data->bits[pos] &= ~(1UL << bit); + } + + if (data->granularity == 0) { + hbitmap_test_check(data, 0); + } +} + +static void hbitmap_test_reset_all(TestHBitmapData *data) +{ + size_t n; + + hbitmap_reset_all(data->hb); + + n = DIV_ROUND_UP(data->size, BITS_PER_LONG); + if (n == 0) { + n = 1; + } + memset(data->bits, 0, n * sizeof(unsigned long)); + + if (data->granularity == 0) { + hbitmap_test_check(data, 0); + } +} + +static void hbitmap_test_check_get(TestHBitmapData *data) +{ + uint64_t count = 0; + uint64_t i; + + for (i = 0; i < data->size; i++) { + size_t pos = i >> LOG_BITS_PER_LONG; + int bit = i & (BITS_PER_LONG - 1); + unsigned long val = data->bits[pos] & (1UL << bit); + count += hbitmap_get(data->hb, i); + g_assert_cmpint(hbitmap_get(data->hb, i), ==, val != 0); + } + g_assert_cmpint(count, ==, hbitmap_count(data->hb)); +} + +static void test_hbitmap_zero(TestHBitmapData *data, + const void *unused) +{ + hbitmap_test_init(data, 0, 0); +} + +static void test_hbitmap_unaligned(TestHBitmapData *data, + const void *unused) +{ + hbitmap_test_init(data, L3 + 23, 0); + hbitmap_test_set(data, 0, 1); + hbitmap_test_set(data, L3 + 22, 1); +} + +static void test_hbitmap_iter_empty(TestHBitmapData *data, + const void *unused) +{ + hbitmap_test_init(data, L1, 0); +} + +static void test_hbitmap_iter_partial(TestHBitmapData *data, + const void *unused) +{ + hbitmap_test_init(data, L3, 0); + hbitmap_test_set(data, 0, L3); + hbitmap_test_check(data, 1); + hbitmap_test_check(data, L1 - 1); + hbitmap_test_check(data, L1); + hbitmap_test_check(data, L1 * 2 - 1); + hbitmap_test_check(data, L2 - 1); + hbitmap_test_check(data, L2); + hbitmap_test_check(data, L2 + 1); + hbitmap_test_check(data, L2 + L1); + hbitmap_test_check(data, L2 + L1 * 2 - 1); + hbitmap_test_check(data, L2 * 2 - 1); + hbitmap_test_check(data, L2 * 2); + hbitmap_test_check(data, L2 * 2 + 1); + hbitmap_test_check(data, L2 * 2 + L1); + hbitmap_test_check(data, L2 * 2 + L1 * 2 - 1); + hbitmap_test_check(data, L3 / 2); +} + +static void test_hbitmap_set_all(TestHBitmapData *data, + const void *unused) +{ + hbitmap_test_init(data, L3, 0); + hbitmap_test_set(data, 0, L3); +} + +static void test_hbitmap_get_all(TestHBitmapData *data, + const void *unused) +{ + hbitmap_test_init(data, L3, 0); + hbitmap_test_set(data, 0, L3); + hbitmap_test_check_get(data); +} + +static void test_hbitmap_get_some(TestHBitmapData *data, + const void *unused) +{ + hbitmap_test_init(data, 2 * L2, 0); + hbitmap_test_set(data, 10, 1); + hbitmap_test_check_get(data); + hbitmap_test_set(data, L1 - 1, 1); + hbitmap_test_check_get(data); + hbitmap_test_set(data, L1, 1); + hbitmap_test_check_get(data); + hbitmap_test_set(data, L2 - 1, 1); + hbitmap_test_check_get(data); + hbitmap_test_set(data, L2, 1); + hbitmap_test_check_get(data); +} + +static void test_hbitmap_set_one(TestHBitmapData *data, + const void *unused) +{ + hbitmap_test_init(data, 2 * L2, 0); + hbitmap_test_set(data, 10, 1); + hbitmap_test_set(data, L1 - 1, 1); + hbitmap_test_set(data, L1, 1); + hbitmap_test_set(data, L2 - 1, 1); + hbitmap_test_set(data, L2, 1); +} + +static void test_hbitmap_set_two_elem(TestHBitmapData *data, + const void *unused) +{ + hbitmap_test_init(data, 2 * L2, 0); + hbitmap_test_set(data, L1 - 1, 2); + hbitmap_test_set(data, L1 * 2 - 1, 4); + hbitmap_test_set(data, L1 * 4, L1 + 1); + hbitmap_test_set(data, L1 * 8 - 1, L1 + 1); + hbitmap_test_set(data, L2 - 1, 2); + hbitmap_test_set(data, L2 + L1 - 1, 8); + hbitmap_test_set(data, L2 + L1 * 4, L1 + 1); + hbitmap_test_set(data, L2 + L1 * 8 - 1, L1 + 1); +} + +static void test_hbitmap_set(TestHBitmapData *data, + const void *unused) +{ + hbitmap_test_init(data, L3 * 2, 0); + hbitmap_test_set(data, L1 - 1, L1 + 2); + hbitmap_test_set(data, L1 * 3 - 1, L1 + 2); + hbitmap_test_set(data, L1 * 5, L1 * 2 + 1); + hbitmap_test_set(data, L1 * 8 - 1, L1 * 2 + 1); + hbitmap_test_set(data, L2 - 1, L1 + 2); + hbitmap_test_set(data, L2 + L1 * 2 - 1, L1 + 2); + hbitmap_test_set(data, L2 + L1 * 4, L1 * 2 + 1); + hbitmap_test_set(data, L2 + L1 * 7 - 1, L1 * 2 + 1); + hbitmap_test_set(data, L2 * 2 - 1, L3 * 2 - L2 * 2); +} + +static void test_hbitmap_set_twice(TestHBitmapData *data, + const void *unused) +{ + hbitmap_test_init(data, L1 * 3, 0); + hbitmap_test_set(data, 0, L1 * 3); + hbitmap_test_set(data, L1, 1); +} + +static void test_hbitmap_set_overlap(TestHBitmapData *data, + const void *unused) +{ + hbitmap_test_init(data, L3 * 2, 0); + hbitmap_test_set(data, L1 - 1, L1 + 2); + hbitmap_test_set(data, L1 * 2 - 1, L1 * 2 + 2); + hbitmap_test_set(data, 0, L1 * 3); + hbitmap_test_set(data, L1 * 8 - 1, L2); + hbitmap_test_set(data, L2, L1); + hbitmap_test_set(data, L2 - L1 - 1, L1 * 8 + 2); + hbitmap_test_set(data, L2, L3 - L2 + 1); + hbitmap_test_set(data, L3 - L1, L1 * 3); + hbitmap_test_set(data, L3 - 1, 3); + hbitmap_test_set(data, L3 - 1, L2); +} + +static void test_hbitmap_reset_empty(TestHBitmapData *data, + const void *unused) +{ + hbitmap_test_init(data, L3, 0); + hbitmap_test_reset(data, 0, L3); +} + +static void test_hbitmap_reset(TestHBitmapData *data, + const void *unused) +{ + hbitmap_test_init(data, L3 * 2, 0); + hbitmap_test_set(data, L1 - 1, L1 + 2); + hbitmap_test_reset(data, L1 * 2 - 1, L1 * 2 + 2); + hbitmap_test_set(data, 0, L1 * 3); + hbitmap_test_reset(data, L1 * 8 - 1, L2); + hbitmap_test_set(data, L2, L1); + hbitmap_test_reset(data, L2 - L1 - 1, L1 * 8 + 2); + hbitmap_test_set(data, L2, L3 - L2 + 1); + hbitmap_test_reset(data, L3 - L1, L1 * 3); + hbitmap_test_set(data, L3 - 1, 3); + hbitmap_test_reset(data, L3 - 1, L2); + hbitmap_test_set(data, 0, L3 * 2); + hbitmap_test_reset(data, 0, L1); + hbitmap_test_reset(data, 0, L2); + hbitmap_test_reset(data, L3, L3); + hbitmap_test_set(data, L3 / 2, L3); +} + +static void test_hbitmap_reset_all(TestHBitmapData *data, + const void *unused) +{ + hbitmap_test_init(data, L3 * 2, 0); + hbitmap_test_set(data, L1 - 1, L1 + 2); + hbitmap_test_reset_all(data); + hbitmap_test_set(data, 0, L1 * 3); + hbitmap_test_reset_all(data); + hbitmap_test_set(data, L2, L1); + hbitmap_test_reset_all(data); + hbitmap_test_set(data, L2, L3 - L2 + 1); + hbitmap_test_reset_all(data); + hbitmap_test_set(data, L3 - 1, 3); + hbitmap_test_reset_all(data); + hbitmap_test_set(data, 0, L3 * 2); + hbitmap_test_reset_all(data); + hbitmap_test_set(data, L3 / 2, L3); + hbitmap_test_reset_all(data); +} + +static void test_hbitmap_granularity(TestHBitmapData *data, + const void *unused) +{ + /* Note that hbitmap_test_check has to be invoked manually in this test. */ + hbitmap_test_init(data, L1, 1); + hbitmap_test_set(data, 0, 1); + g_assert_cmpint(hbitmap_count(data->hb), ==, 2); + hbitmap_test_check(data, 0); + hbitmap_test_set(data, 2, 1); + g_assert_cmpint(hbitmap_count(data->hb), ==, 4); + hbitmap_test_check(data, 0); + hbitmap_test_set(data, 0, 3); + g_assert_cmpint(hbitmap_count(data->hb), ==, 4); + hbitmap_test_reset(data, 0, 2); + g_assert_cmpint(hbitmap_count(data->hb), ==, 2); +} + +static void test_hbitmap_iter_granularity(TestHBitmapData *data, + const void *unused) +{ + HBitmapIter hbi; + + /* Note that hbitmap_test_check has to be invoked manually in this test. */ + hbitmap_test_init(data, 131072 << 7, 7); + hbitmap_iter_init(&hbi, data->hb, 0); + g_assert_cmpint(hbitmap_iter_next(&hbi), <, 0); + + hbitmap_test_set(data, ((L2 + L1 + 1) << 7) + 8, 8); + hbitmap_iter_init(&hbi, data->hb, 0); + g_assert_cmpint(hbitmap_iter_next(&hbi), ==, (L2 + L1 + 1) << 7); + g_assert_cmpint(hbitmap_iter_next(&hbi), <, 0); + + hbitmap_iter_init(&hbi, data->hb, (L2 + L1 + 2) << 7); + g_assert_cmpint(hbitmap_iter_next(&hbi), <, 0); + + hbitmap_test_set(data, (131072 << 7) - 8, 8); + hbitmap_iter_init(&hbi, data->hb, 0); + g_assert_cmpint(hbitmap_iter_next(&hbi), ==, (L2 + L1 + 1) << 7); + g_assert_cmpint(hbitmap_iter_next(&hbi), ==, 131071 << 7); + g_assert_cmpint(hbitmap_iter_next(&hbi), <, 0); + + hbitmap_iter_init(&hbi, data->hb, (L2 + L1 + 2) << 7); + g_assert_cmpint(hbitmap_iter_next(&hbi), ==, 131071 << 7); + g_assert_cmpint(hbitmap_iter_next(&hbi), <, 0); +} + +static void hbitmap_test_set_boundary_bits(TestHBitmapData *data, ssize_t diff) +{ + size_t size = data->size; + + /* First bit */ + hbitmap_test_set(data, 0, 1); + if (diff < 0) { + /* Last bit in new, shortened map */ + hbitmap_test_set(data, size + diff - 1, 1); + + /* First bit to be truncated away */ + hbitmap_test_set(data, size + diff, 1); + } + /* Last bit */ + hbitmap_test_set(data, size - 1, 1); + if (data->granularity == 0) { + hbitmap_test_check_get(data); + } +} + +static void hbitmap_test_check_boundary_bits(TestHBitmapData *data) +{ + size_t size = MIN(data->size, data->old_size); + + if (data->granularity == 0) { + hbitmap_test_check_get(data); + hbitmap_test_check(data, 0); + } else { + /* If a granularity was set, note that every distinct + * (bit >> granularity) value that was set will increase + * the bit pop count by 2^granularity, not just 1. + * + * The hbitmap_test_check facility does not currently tolerate + * non-zero granularities, so test the boundaries and the population + * count manually. + */ + g_assert(hbitmap_get(data->hb, 0)); + g_assert(hbitmap_get(data->hb, size - 1)); + g_assert_cmpint(2 << data->granularity, ==, hbitmap_count(data->hb)); + } +} + +/* Generic truncate test. */ +static void hbitmap_test_truncate(TestHBitmapData *data, + size_t size, + ssize_t diff, + int granularity) +{ + hbitmap_test_init(data, size, granularity); + hbitmap_test_set_boundary_bits(data, diff); + hbitmap_test_truncate_impl(data, size + diff); + hbitmap_test_check_boundary_bits(data); +} + +static void test_hbitmap_truncate_nop(TestHBitmapData *data, + const void *unused) +{ + hbitmap_test_truncate(data, L2, 0, 0); +} + +/** + * Grow by an amount smaller than the granularity, without crossing + * a granularity alignment boundary. Effectively a NOP. + */ +static void test_hbitmap_truncate_grow_negligible(TestHBitmapData *data, + const void *unused) +{ + size_t size = L2 - 1; + size_t diff = 1; + int granularity = 1; + + hbitmap_test_truncate(data, size, diff, granularity); +} + +/** + * Shrink by an amount smaller than the granularity, without crossing + * a granularity alignment boundary. Effectively a NOP. + */ +static void test_hbitmap_truncate_shrink_negligible(TestHBitmapData *data, + const void *unused) +{ + size_t size = L2; + ssize_t diff = -1; + int granularity = 1; + + hbitmap_test_truncate(data, size, diff, granularity); +} + +/** + * Grow by an amount smaller than the granularity, but crossing over + * a granularity alignment boundary. + */ +static void test_hbitmap_truncate_grow_tiny(TestHBitmapData *data, + const void *unused) +{ + size_t size = L2 - 2; + ssize_t diff = 1; + int granularity = 1; + + hbitmap_test_truncate(data, size, diff, granularity); +} + +/** + * Shrink by an amount smaller than the granularity, but crossing over + * a granularity alignment boundary. + */ +static void test_hbitmap_truncate_shrink_tiny(TestHBitmapData *data, + const void *unused) +{ + size_t size = L2 - 1; + ssize_t diff = -1; + int granularity = 1; + + hbitmap_test_truncate(data, size, diff, granularity); +} + +/** + * Grow by an amount smaller than sizeof(long), and not crossing over + * a sizeof(long) alignment boundary. + */ +static void test_hbitmap_truncate_grow_small(TestHBitmapData *data, + const void *unused) +{ + size_t size = L2 + 1; + size_t diff = sizeof(long) / 2; + + hbitmap_test_truncate(data, size, diff, 0); +} + +/** + * Shrink by an amount smaller than sizeof(long), and not crossing over + * a sizeof(long) alignment boundary. + */ +static void test_hbitmap_truncate_shrink_small(TestHBitmapData *data, + const void *unused) +{ + size_t size = L2; + size_t diff = sizeof(long) / 2; + + hbitmap_test_truncate(data, size, -diff, 0); +} + +/** + * Grow by an amount smaller than sizeof(long), while crossing over + * a sizeof(long) alignment boundary. + */ +static void test_hbitmap_truncate_grow_medium(TestHBitmapData *data, + const void *unused) +{ + size_t size = L2 - 1; + size_t diff = sizeof(long) / 2; + + hbitmap_test_truncate(data, size, diff, 0); +} + +/** + * Shrink by an amount smaller than sizeof(long), while crossing over + * a sizeof(long) alignment boundary. + */ +static void test_hbitmap_truncate_shrink_medium(TestHBitmapData *data, + const void *unused) +{ + size_t size = L2 + 1; + size_t diff = sizeof(long) / 2; + + hbitmap_test_truncate(data, size, -diff, 0); +} + +/** + * Grow by an amount larger than sizeof(long). + */ +static void test_hbitmap_truncate_grow_large(TestHBitmapData *data, + const void *unused) +{ + size_t size = L2; + size_t diff = 8 * sizeof(long); + + hbitmap_test_truncate(data, size, diff, 0); +} + +/** + * Shrink by an amount larger than sizeof(long). + */ +static void test_hbitmap_truncate_shrink_large(TestHBitmapData *data, + const void *unused) +{ + size_t size = L2; + size_t diff = 8 * sizeof(long); + + hbitmap_test_truncate(data, size, -diff, 0); +} + +static void test_hbitmap_serialize_align(TestHBitmapData *data, + const void *unused) +{ + int r; + + hbitmap_test_init(data, L3 * 2, 3); + g_assert(hbitmap_is_serializable(data->hb)); + + r = hbitmap_serialization_align(data->hb); + g_assert_cmpint(r, ==, 64 << 3); +} + +static void hbitmap_test_serialize_range(TestHBitmapData *data, + uint8_t *buf, size_t buf_size, + uint64_t pos, uint64_t count) +{ + size_t i; + unsigned long *el = (unsigned long *)buf; + + assert(hbitmap_granularity(data->hb) == 0); + hbitmap_reset_all(data->hb); + memset(buf, 0, buf_size); + if (count) { + hbitmap_set(data->hb, pos, count); + } + + g_assert(hbitmap_is_serializable(data->hb)); + hbitmap_serialize_part(data->hb, buf, 0, data->size); + + /* Serialized buffer is inherently LE, convert it back manually to test */ + for (i = 0; i < buf_size / sizeof(unsigned long); i++) { + el[i] = (BITS_PER_LONG == 32 ? le32_to_cpu(el[i]) : le64_to_cpu(el[i])); + } + + for (i = 0; i < data->size; i++) { + int is_set = test_bit(i, (unsigned long *)buf); + if (i >= pos && i < pos + count) { + g_assert(is_set); + } else { + g_assert(!is_set); + } + } + + /* Re-serialize for deserialization testing */ + memset(buf, 0, buf_size); + hbitmap_serialize_part(data->hb, buf, 0, data->size); + hbitmap_reset_all(data->hb); + + g_assert(hbitmap_is_serializable(data->hb)); + hbitmap_deserialize_part(data->hb, buf, 0, data->size, true); + + for (i = 0; i < data->size; i++) { + int is_set = hbitmap_get(data->hb, i); + if (i >= pos && i < pos + count) { + g_assert(is_set); + } else { + g_assert(!is_set); + } + } +} + +static void test_hbitmap_serialize_basic(TestHBitmapData *data, + const void *unused) +{ + int i, j; + size_t buf_size; + uint8_t *buf; + uint64_t positions[] = { 0, 1, L1 - 1, L1, L2 - 1, L2, L2 + 1, L3 - 1 }; + int num_positions = ARRAY_SIZE(positions); + + hbitmap_test_init(data, L3, 0); + g_assert(hbitmap_is_serializable(data->hb)); + buf_size = hbitmap_serialization_size(data->hb, 0, data->size); + buf = g_malloc0(buf_size); + + for (i = 0; i < num_positions; i++) { + for (j = 0; j < num_positions; j++) { + hbitmap_test_serialize_range(data, buf, buf_size, + positions[i], + MIN(positions[j], L3 - positions[i])); + } + } + + g_free(buf); +} + +static void test_hbitmap_serialize_part(TestHBitmapData *data, + const void *unused) +{ + int i, j, k; + size_t buf_size; + uint8_t *buf; + uint64_t positions[] = { 0, 1, L1 - 1, L1, L2 - 1, L2, L2 + 1, L3 - 1 }; + int num_positions = ARRAY_SIZE(positions); + + hbitmap_test_init(data, L3, 0); + buf_size = L2; + buf = g_malloc0(buf_size); + + for (i = 0; i < num_positions; i++) { + hbitmap_set(data->hb, positions[i], 1); + } + + g_assert(hbitmap_is_serializable(data->hb)); + + for (i = 0; i < data->size; i += buf_size) { + unsigned long *el = (unsigned long *)buf; + hbitmap_serialize_part(data->hb, buf, i, buf_size); + for (j = 0; j < buf_size / sizeof(unsigned long); j++) { + el[j] = (BITS_PER_LONG == 32 ? le32_to_cpu(el[j]) : le64_to_cpu(el[j])); + } + + for (j = 0; j < buf_size; j++) { + bool should_set = false; + for (k = 0; k < num_positions; k++) { + if (positions[k] == j + i) { + should_set = true; + break; + } + } + g_assert_cmpint(should_set, ==, test_bit(j, (unsigned long *)buf)); + } + } + + g_free(buf); +} + +static void test_hbitmap_serialize_zeroes(TestHBitmapData *data, + const void *unused) +{ + int i; + HBitmapIter iter; + int64_t next; + uint64_t min_l1 = MAX(L1, 64); + uint64_t positions[] = { 0, min_l1, L2, L3 - min_l1}; + int num_positions = ARRAY_SIZE(positions); + + hbitmap_test_init(data, L3, 0); + + for (i = 0; i < num_positions; i++) { + hbitmap_set(data->hb, positions[i], L1); + } + + g_assert(hbitmap_is_serializable(data->hb)); + + for (i = 0; i < num_positions; i++) { + hbitmap_deserialize_zeroes(data->hb, positions[i], min_l1, true); + hbitmap_iter_init(&iter, data->hb, 0); + next = hbitmap_iter_next(&iter); + if (i == num_positions - 1) { + g_assert_cmpint(next, ==, -1); + } else { + g_assert_cmpint(next, ==, positions[i + 1]); + } + } +} + +static void hbitmap_test_add(const char *testpath, + void (*test_func)(TestHBitmapData *data, const void *user_data)) +{ + g_test_add(testpath, TestHBitmapData, NULL, NULL, test_func, + hbitmap_test_teardown); +} + +static void test_hbitmap_iter_and_reset(TestHBitmapData *data, + const void *unused) +{ + HBitmapIter hbi; + + hbitmap_test_init(data, L1 * 2, 0); + hbitmap_set(data->hb, 0, data->size); + + hbitmap_iter_init(&hbi, data->hb, BITS_PER_LONG - 1); + + hbitmap_iter_next(&hbi); + + hbitmap_reset_all(data->hb); + hbitmap_iter_next(&hbi); +} + +static void test_hbitmap_next_x_check_range(TestHBitmapData *data, + int64_t start, + int64_t count) +{ + int64_t next_zero = hbitmap_next_zero(data->hb, start, count); + int64_t next_dirty = hbitmap_next_dirty(data->hb, start, count); + int64_t next; + int64_t end = start >= data->size || data->size - start < count ? + data->size : start + count; + bool first_bit = hbitmap_get(data->hb, start); + + for (next = start; + next < end && hbitmap_get(data->hb, next) == first_bit; + next++) + { + ; + } + + if (next == end) { + next = -1; + } + + g_assert_cmpint(next_dirty, ==, first_bit ? start : next); + g_assert_cmpint(next_zero, ==, first_bit ? next : start); +} + +static void test_hbitmap_next_x_check(TestHBitmapData *data, int64_t start) +{ + test_hbitmap_next_x_check_range(data, start, INT64_MAX); +} + +static void test_hbitmap_next_x_do(TestHBitmapData *data, int granularity) +{ + hbitmap_test_init(data, L3, granularity); + test_hbitmap_next_x_check(data, 0); + test_hbitmap_next_x_check(data, L3 - 1); + test_hbitmap_next_x_check_range(data, 0, 1); + test_hbitmap_next_x_check_range(data, L3 - 1, 1); + + hbitmap_set(data->hb, L2, 1); + test_hbitmap_next_x_check(data, 0); + test_hbitmap_next_x_check(data, L2 - 1); + test_hbitmap_next_x_check(data, L2); + test_hbitmap_next_x_check(data, L2 + 1); + test_hbitmap_next_x_check_range(data, 0, 1); + test_hbitmap_next_x_check_range(data, 0, L2); + test_hbitmap_next_x_check_range(data, L2 - 1, 1); + test_hbitmap_next_x_check_range(data, L2 - 1, 2); + test_hbitmap_next_x_check_range(data, L2, 1); + test_hbitmap_next_x_check_range(data, L2 + 1, 1); + + hbitmap_set(data->hb, L2 + 5, L1); + test_hbitmap_next_x_check(data, 0); + test_hbitmap_next_x_check(data, L2 - L1); + test_hbitmap_next_x_check(data, L2 + 1); + test_hbitmap_next_x_check(data, L2 + 2); + test_hbitmap_next_x_check(data, L2 + 5); + test_hbitmap_next_x_check(data, L2 + L1 - 1); + test_hbitmap_next_x_check(data, L2 + L1); + test_hbitmap_next_x_check(data, L2 + L1 + 1); + test_hbitmap_next_x_check_range(data, L2 - 2, L1); + test_hbitmap_next_x_check_range(data, L2, 4); + test_hbitmap_next_x_check_range(data, L2, 6); + test_hbitmap_next_x_check_range(data, L2 + 1, 3); + test_hbitmap_next_x_check_range(data, L2 + 4, L1); + test_hbitmap_next_x_check_range(data, L2 + 5, L1); + test_hbitmap_next_x_check_range(data, L2 + 5 + L1 - 1, 1); + test_hbitmap_next_x_check_range(data, L2 + 5 + L1, 1); + test_hbitmap_next_x_check_range(data, L2 + 5 + L1 + 1, 1); + + hbitmap_set(data->hb, L2 * 2, L3 - L2 * 2); + test_hbitmap_next_x_check(data, L2 * 2 - L1); + test_hbitmap_next_x_check(data, L2 * 2 - 2); + test_hbitmap_next_x_check(data, L2 * 2 - 1); + test_hbitmap_next_x_check(data, L2 * 2); + test_hbitmap_next_x_check(data, L2 * 2 + 1); + test_hbitmap_next_x_check(data, L2 * 2 + L1); + test_hbitmap_next_x_check(data, L3 - 1); + test_hbitmap_next_x_check_range(data, L2 * 2 - L1, L1 + 1); + test_hbitmap_next_x_check_range(data, L2 * 2, L2); + + hbitmap_set(data->hb, 0, L3); + test_hbitmap_next_x_check(data, 0); +} + +static void test_hbitmap_next_x_0(TestHBitmapData *data, const void *unused) +{ + test_hbitmap_next_x_do(data, 0); +} + +static void test_hbitmap_next_x_4(TestHBitmapData *data, const void *unused) +{ + test_hbitmap_next_x_do(data, 4); +} + +static void test_hbitmap_next_x_after_truncate(TestHBitmapData *data, + const void *unused) +{ + hbitmap_test_init(data, L1, 0); + hbitmap_test_truncate_impl(data, L1 * 2); + hbitmap_set(data->hb, 0, L1); + test_hbitmap_next_x_check(data, 0); +} + +static void test_hbitmap_next_dirty_area_check_limited(TestHBitmapData *data, + int64_t offset, + int64_t count, + int64_t max_dirty) +{ + int64_t off1, off2; + int64_t len1 = 0, len2; + bool ret1, ret2; + int64_t end; + + ret1 = hbitmap_next_dirty_area(data->hb, + offset, count == INT64_MAX ? INT64_MAX : offset + count, max_dirty, + &off1, &len1); + + end = offset > data->size || data->size - offset < count ? data->size : + offset + count; + + for (off2 = offset; off2 < end && !hbitmap_get(data->hb, off2); off2++) { + ; + } + + for (len2 = 1; (off2 + len2 < end && len2 < max_dirty && + hbitmap_get(data->hb, off2 + len2)); len2++) + { + ; + } + + ret2 = off2 < end; + g_assert_cmpint(ret1, ==, ret2); + + if (ret2) { + g_assert_cmpint(off1, ==, off2); + g_assert_cmpint(len1, ==, len2); + } +} + +static void test_hbitmap_next_dirty_area_check(TestHBitmapData *data, + int64_t offset, int64_t count) +{ + test_hbitmap_next_dirty_area_check_limited(data, offset, count, INT64_MAX); +} + +static void test_hbitmap_next_dirty_area_do(TestHBitmapData *data, + int granularity) +{ + hbitmap_test_init(data, L3, granularity); + test_hbitmap_next_dirty_area_check(data, 0, INT64_MAX); + test_hbitmap_next_dirty_area_check(data, 0, 1); + test_hbitmap_next_dirty_area_check(data, L3 - 1, 1); + test_hbitmap_next_dirty_area_check_limited(data, 0, INT64_MAX, 1); + + hbitmap_set(data->hb, L2, 1); + test_hbitmap_next_dirty_area_check(data, 0, 1); + test_hbitmap_next_dirty_area_check(data, 0, L2); + test_hbitmap_next_dirty_area_check(data, 0, INT64_MAX); + test_hbitmap_next_dirty_area_check(data, L2 - 1, INT64_MAX); + test_hbitmap_next_dirty_area_check(data, L2 - 1, 1); + test_hbitmap_next_dirty_area_check(data, L2 - 1, 2); + test_hbitmap_next_dirty_area_check(data, L2 - 1, 3); + test_hbitmap_next_dirty_area_check(data, L2, INT64_MAX); + test_hbitmap_next_dirty_area_check(data, L2, 1); + test_hbitmap_next_dirty_area_check(data, L2 + 1, 1); + test_hbitmap_next_dirty_area_check_limited(data, 0, INT64_MAX, 1); + test_hbitmap_next_dirty_area_check_limited(data, L2 - 1, 2, 1); + + hbitmap_set(data->hb, L2 + 5, L1); + test_hbitmap_next_dirty_area_check(data, 0, INT64_MAX); + test_hbitmap_next_dirty_area_check(data, L2 - 2, 8); + test_hbitmap_next_dirty_area_check(data, L2 + 1, 5); + test_hbitmap_next_dirty_area_check(data, L2 + 1, 3); + test_hbitmap_next_dirty_area_check(data, L2 + 4, L1); + test_hbitmap_next_dirty_area_check(data, L2 + 5, L1); + test_hbitmap_next_dirty_area_check(data, L2 + 7, L1); + test_hbitmap_next_dirty_area_check(data, L2 + L1, L1); + test_hbitmap_next_dirty_area_check(data, L2, 0); + test_hbitmap_next_dirty_area_check(data, L2 + 1, 0); + test_hbitmap_next_dirty_area_check_limited(data, L2 + 3, INT64_MAX, 3); + test_hbitmap_next_dirty_area_check_limited(data, L2 + 3, 7, 10); + + hbitmap_set(data->hb, L2 * 2, L3 - L2 * 2); + test_hbitmap_next_dirty_area_check(data, 0, INT64_MAX); + test_hbitmap_next_dirty_area_check(data, L2, INT64_MAX); + test_hbitmap_next_dirty_area_check(data, L2 + 1, INT64_MAX); + test_hbitmap_next_dirty_area_check(data, L2 + 5 + L1 - 1, INT64_MAX); + test_hbitmap_next_dirty_area_check(data, L2 + 5 + L1, 5); + test_hbitmap_next_dirty_area_check(data, L2 * 2 - L1, L1 + 1); + test_hbitmap_next_dirty_area_check(data, L2 * 2, L2); + test_hbitmap_next_dirty_area_check_limited(data, L2 * 2 + 1, INT64_MAX, 5); + test_hbitmap_next_dirty_area_check_limited(data, L2 * 2 + 1, 10, 5); + test_hbitmap_next_dirty_area_check_limited(data, L2 * 2 + 1, 2, 5); + + hbitmap_set(data->hb, 0, L3); + test_hbitmap_next_dirty_area_check(data, 0, INT64_MAX); +} + +static void test_hbitmap_next_dirty_area_0(TestHBitmapData *data, + const void *unused) +{ + test_hbitmap_next_dirty_area_do(data, 0); +} + +static void test_hbitmap_next_dirty_area_1(TestHBitmapData *data, + const void *unused) +{ + test_hbitmap_next_dirty_area_do(data, 1); +} + +static void test_hbitmap_next_dirty_area_4(TestHBitmapData *data, + const void *unused) +{ + test_hbitmap_next_dirty_area_do(data, 4); +} + +static void test_hbitmap_next_dirty_area_after_truncate(TestHBitmapData *data, + const void *unused) +{ + hbitmap_test_init(data, L1, 0); + hbitmap_test_truncate_impl(data, L1 * 2); + hbitmap_set(data->hb, L1 + 1, 1); + test_hbitmap_next_dirty_area_check(data, 0, INT64_MAX); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + hbitmap_test_add("/hbitmap/size/0", test_hbitmap_zero); + hbitmap_test_add("/hbitmap/size/unaligned", test_hbitmap_unaligned); + hbitmap_test_add("/hbitmap/iter/empty", test_hbitmap_iter_empty); + hbitmap_test_add("/hbitmap/iter/partial", test_hbitmap_iter_partial); + hbitmap_test_add("/hbitmap/iter/granularity", test_hbitmap_iter_granularity); + hbitmap_test_add("/hbitmap/get/all", test_hbitmap_get_all); + hbitmap_test_add("/hbitmap/get/some", test_hbitmap_get_some); + hbitmap_test_add("/hbitmap/set/all", test_hbitmap_set_all); + hbitmap_test_add("/hbitmap/set/one", test_hbitmap_set_one); + hbitmap_test_add("/hbitmap/set/two-elem", test_hbitmap_set_two_elem); + hbitmap_test_add("/hbitmap/set/general", test_hbitmap_set); + hbitmap_test_add("/hbitmap/set/twice", test_hbitmap_set_twice); + hbitmap_test_add("/hbitmap/set/overlap", test_hbitmap_set_overlap); + hbitmap_test_add("/hbitmap/reset/empty", test_hbitmap_reset_empty); + hbitmap_test_add("/hbitmap/reset/general", test_hbitmap_reset); + hbitmap_test_add("/hbitmap/reset/all", test_hbitmap_reset_all); + hbitmap_test_add("/hbitmap/granularity", test_hbitmap_granularity); + + hbitmap_test_add("/hbitmap/truncate/nop", test_hbitmap_truncate_nop); + hbitmap_test_add("/hbitmap/truncate/grow/negligible", + test_hbitmap_truncate_grow_negligible); + hbitmap_test_add("/hbitmap/truncate/shrink/negligible", + test_hbitmap_truncate_shrink_negligible); + hbitmap_test_add("/hbitmap/truncate/grow/tiny", + test_hbitmap_truncate_grow_tiny); + hbitmap_test_add("/hbitmap/truncate/shrink/tiny", + test_hbitmap_truncate_shrink_tiny); + hbitmap_test_add("/hbitmap/truncate/grow/small", + test_hbitmap_truncate_grow_small); + hbitmap_test_add("/hbitmap/truncate/shrink/small", + test_hbitmap_truncate_shrink_small); + hbitmap_test_add("/hbitmap/truncate/grow/medium", + test_hbitmap_truncate_grow_medium); + hbitmap_test_add("/hbitmap/truncate/shrink/medium", + test_hbitmap_truncate_shrink_medium); + hbitmap_test_add("/hbitmap/truncate/grow/large", + test_hbitmap_truncate_grow_large); + hbitmap_test_add("/hbitmap/truncate/shrink/large", + test_hbitmap_truncate_shrink_large); + + hbitmap_test_add("/hbitmap/serialize/align", + test_hbitmap_serialize_align); + hbitmap_test_add("/hbitmap/serialize/basic", + test_hbitmap_serialize_basic); + hbitmap_test_add("/hbitmap/serialize/part", + test_hbitmap_serialize_part); + hbitmap_test_add("/hbitmap/serialize/zeroes", + test_hbitmap_serialize_zeroes); + + hbitmap_test_add("/hbitmap/iter/iter_and_reset", + test_hbitmap_iter_and_reset); + + hbitmap_test_add("/hbitmap/next_zero/next_x_0", + test_hbitmap_next_x_0); + hbitmap_test_add("/hbitmap/next_zero/next_x_4", + test_hbitmap_next_x_4); + hbitmap_test_add("/hbitmap/next_zero/next_x_after_truncate", + test_hbitmap_next_x_after_truncate); + + hbitmap_test_add("/hbitmap/next_dirty_area/next_dirty_area_0", + test_hbitmap_next_dirty_area_0); + hbitmap_test_add("/hbitmap/next_dirty_area/next_dirty_area_1", + test_hbitmap_next_dirty_area_1); + hbitmap_test_add("/hbitmap/next_dirty_area/next_dirty_area_4", + test_hbitmap_next_dirty_area_4); + hbitmap_test_add("/hbitmap/next_dirty_area/next_dirty_area_after_truncate", + test_hbitmap_next_dirty_area_after_truncate); + + g_test_run(); + + return 0; +} diff --git a/tests/unit/test-image-locking.c b/tests/unit/test-image-locking.c new file mode 100644 index 0000000000..ba057bd66c --- /dev/null +++ b/tests/unit/test-image-locking.c @@ -0,0 +1,158 @@ +/* + * Image locking tests + * + * Copyright (c) 2018 Red Hat Inc. + * + * Author: Fam Zheng + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "block/block.h" +#include "sysemu/block-backend.h" +#include "qapi/error.h" +#include "qapi/qmp/qdict.h" +#include "qemu/main-loop.h" + +static BlockBackend *open_image(const char *path, + uint64_t perm, uint64_t shared_perm, + Error **errp) +{ + Error *local_err = NULL; + BlockBackend *blk; + QDict *options = qdict_new(); + + qdict_put_str(options, "driver", "raw"); + blk = blk_new_open(path, NULL, options, BDRV_O_RDWR, &local_err); + if (blk) { + g_assert_null(local_err); + if (blk_set_perm(blk, perm, shared_perm, errp)) { + blk_unref(blk); + blk = NULL; + } + } else { + error_propagate(errp, local_err); + } + return blk; +} + +static void check_locked_bytes(int fd, uint64_t perm_locks, + uint64_t shared_perm_locks) +{ + int i; + + if (!perm_locks && !shared_perm_locks) { + g_assert(!qemu_lock_fd_test(fd, 0, 0, true)); + return; + } + for (i = 0; (1ULL << i) <= BLK_PERM_ALL; i++) { + uint64_t bit = (1ULL << i); + bool perm_expected = !!(bit & perm_locks); + bool shared_perm_expected = !!(bit & shared_perm_locks); + g_assert_cmpint(perm_expected, ==, + !!qemu_lock_fd_test(fd, 100 + i, 1, true)); + g_assert_cmpint(shared_perm_expected, ==, + !!qemu_lock_fd_test(fd, 200 + i, 1, true)); + } +} + +static void test_image_locking_basic(void) +{ + BlockBackend *blk1, *blk2, *blk3; + char img_path[] = "/tmp/qtest.XXXXXX"; + uint64_t perm, shared_perm; + + int fd = mkstemp(img_path); + assert(fd >= 0); + + perm = BLK_PERM_WRITE | BLK_PERM_CONSISTENT_READ; + shared_perm = BLK_PERM_ALL; + blk1 = open_image(img_path, perm, shared_perm, &error_abort); + g_assert(blk1); + + check_locked_bytes(fd, perm, ~shared_perm); + + /* compatible perm between blk1 and blk2 */ + blk2 = open_image(img_path, perm | BLK_PERM_RESIZE, shared_perm, NULL); + g_assert(blk2); + check_locked_bytes(fd, perm | BLK_PERM_RESIZE, ~shared_perm); + + /* incompatible perm with already open blk1 and blk2 */ + blk3 = open_image(img_path, perm, BLK_PERM_WRITE_UNCHANGED, NULL); + g_assert_null(blk3); + + blk_unref(blk2); + + /* Check that extra bytes in blk2 are correctly unlocked */ + check_locked_bytes(fd, perm, ~shared_perm); + + blk_unref(blk1); + + /* Image is unused, no lock there */ + check_locked_bytes(fd, 0, 0); + blk3 = open_image(img_path, perm, BLK_PERM_WRITE_UNCHANGED, &error_abort); + g_assert(blk3); + blk_unref(blk3); + close(fd); + unlink(img_path); +} + +static void test_set_perm_abort(void) +{ + BlockBackend *blk1, *blk2; + char img_path[] = "/tmp/qtest.XXXXXX"; + uint64_t perm, shared_perm; + int r; + int fd = mkstemp(img_path); + assert(fd >= 0); + + perm = BLK_PERM_WRITE | BLK_PERM_CONSISTENT_READ; + shared_perm = BLK_PERM_ALL; + blk1 = open_image(img_path, perm, shared_perm, &error_abort); + g_assert(blk1); + + blk2 = open_image(img_path, perm, shared_perm, &error_abort); + g_assert(blk2); + + check_locked_bytes(fd, perm, ~shared_perm); + + /* A failed blk_set_perm mustn't change perm status (locked bytes) */ + r = blk_set_perm(blk2, perm | BLK_PERM_RESIZE, BLK_PERM_WRITE_UNCHANGED, + NULL); + g_assert_cmpint(r, !=, 0); + check_locked_bytes(fd, perm, ~shared_perm); + blk_unref(blk1); + blk_unref(blk2); +} + +int main(int argc, char **argv) +{ + bdrv_init(); + qemu_init_main_loop(&error_abort); + + g_test_init(&argc, &argv, NULL); + + if (qemu_has_ofd_lock()) { + g_test_add_func("/image-locking/basic", test_image_locking_basic); + g_test_add_func("/image-locking/set-perm-abort", test_set_perm_abort); + } + + return g_test_run(); +} diff --git a/tests/unit/test-int128.c b/tests/unit/test-int128.c new file mode 100644 index 0000000000..b86a3c76e6 --- /dev/null +++ b/tests/unit/test-int128.c @@ -0,0 +1,223 @@ +/* + * Test Int128 arithmetic + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "qemu/int128.h" + +/* clang doesn't support __noclone__ but it does have a mechanism for + * telling us this. We assume that if we don't have __has_attribute() + * then this is GCC and that GCC always supports __noclone__. + */ +#if defined(__has_attribute) +#if !__has_attribute(__noclone__) +#define ATTRIBUTE_NOCLONE +#endif +#endif +#ifndef ATTRIBUTE_NOCLONE +#define ATTRIBUTE_NOCLONE __attribute__((__noclone__)) +#endif + +static uint32_t tests[8] = { + 0x00000000, 0x00000001, 0x7FFFFFFE, 0x7FFFFFFF, + 0x80000000, 0x80000001, 0xFFFFFFFE, 0xFFFFFFFF, +}; + +#define LOW 3ULL +#define HIGH (1ULL << 63) +#define MIDDLE (-1ULL & ~LOW & ~HIGH) + +static uint64_t expand16(unsigned x) +{ + return (x & LOW) | ((x & 4) ? MIDDLE : 0) | (x & 0x8000 ? HIGH : 0); +} + +static Int128 expand(uint32_t x) +{ + uint64_t l, h; + l = expand16(x & 65535); + h = expand16(x >> 16); + return (Int128) int128_make128(l, h); +}; + +static void test_and(void) +{ + int i, j; + + for (i = 0; i < ARRAY_SIZE(tests); ++i) { + for (j = 0; j < ARRAY_SIZE(tests); ++j) { + Int128 a = expand(tests[i]); + Int128 b = expand(tests[j]); + Int128 r = expand(tests[i] & tests[j]); + Int128 s = int128_and(a, b); + g_assert_cmpuint(int128_getlo(r), ==, int128_getlo(s)); + g_assert_cmpuint(int128_gethi(r), ==, int128_gethi(s)); + } + } +} + +static void test_add(void) +{ + int i, j; + + for (i = 0; i < ARRAY_SIZE(tests); ++i) { + for (j = 0; j < ARRAY_SIZE(tests); ++j) { + Int128 a = expand(tests[i]); + Int128 b = expand(tests[j]); + Int128 r = expand(tests[i] + tests[j]); + Int128 s = int128_add(a, b); + g_assert_cmpuint(int128_getlo(r), ==, int128_getlo(s)); + g_assert_cmpuint(int128_gethi(r), ==, int128_gethi(s)); + } + } +} + +static void test_sub(void) +{ + int i, j; + + for (i = 0; i < ARRAY_SIZE(tests); ++i) { + for (j = 0; j < ARRAY_SIZE(tests); ++j) { + Int128 a = expand(tests[i]); + Int128 b = expand(tests[j]); + Int128 r = expand(tests[i] - tests[j]); + Int128 s = int128_sub(a, b); + g_assert_cmpuint(int128_getlo(r), ==, int128_getlo(s)); + g_assert_cmpuint(int128_gethi(r), ==, int128_gethi(s)); + } + } +} + +static void test_neg(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(tests); ++i) { + Int128 a = expand(tests[i]); + Int128 r = expand(-tests[i]); + Int128 s = int128_neg(a); + g_assert_cmpuint(int128_getlo(r), ==, int128_getlo(s)); + g_assert_cmpuint(int128_gethi(r), ==, int128_gethi(s)); + } +} + +static void test_nz(void) +{ + int i, j; + + for (i = 0; i < ARRAY_SIZE(tests); ++i) { + for (j = 0; j < ARRAY_SIZE(tests); ++j) { + Int128 a = expand(tests[i]); + g_assert_cmpuint(int128_nz(a), ==, tests[i] != 0); + } + } +} + +static void test_le(void) +{ + int i, j; + + for (i = 0; i < ARRAY_SIZE(tests); ++i) { + for (j = 0; j < ARRAY_SIZE(tests); ++j) { + /* Signed comparison */ + int32_t a = (int32_t) tests[i]; + int32_t b = (int32_t) tests[j]; + g_assert_cmpuint(int128_le(expand(a), expand(b)), ==, a <= b); + } + } +} + +static void test_lt(void) +{ + int i, j; + + for (i = 0; i < ARRAY_SIZE(tests); ++i) { + for (j = 0; j < ARRAY_SIZE(tests); ++j) { + /* Signed comparison */ + int32_t a = (int32_t) tests[i]; + int32_t b = (int32_t) tests[j]; + g_assert_cmpuint(int128_lt(expand(a), expand(b)), ==, a < b); + } + } +} + +static void test_ge(void) +{ + int i, j; + + for (i = 0; i < ARRAY_SIZE(tests); ++i) { + for (j = 0; j < ARRAY_SIZE(tests); ++j) { + /* Signed comparison */ + int32_t a = (int32_t) tests[i]; + int32_t b = (int32_t) tests[j]; + g_assert_cmpuint(int128_ge(expand(a), expand(b)), ==, a >= b); + } + } +} + +static void test_gt(void) +{ + int i, j; + + for (i = 0; i < ARRAY_SIZE(tests); ++i) { + for (j = 0; j < ARRAY_SIZE(tests); ++j) { + /* Signed comparison */ + int32_t a = (int32_t) tests[i]; + int32_t b = (int32_t) tests[j]; + g_assert_cmpuint(int128_gt(expand(a), expand(b)), ==, a > b); + } + } +} + +/* Make sure to test undefined behavior at runtime! */ + +static void __attribute__((__noinline__)) ATTRIBUTE_NOCLONE +test_rshift_one(uint32_t x, int n, uint64_t h, uint64_t l) +{ + Int128 a = expand(x); + Int128 r = int128_rshift(a, n); + g_assert_cmpuint(int128_getlo(r), ==, l); + g_assert_cmpuint(int128_gethi(r), ==, h); +} + +static void test_rshift(void) +{ + test_rshift_one(0x00010000U, 64, 0x0000000000000000ULL, 0x0000000000000001ULL); + test_rshift_one(0x80010000U, 64, 0xFFFFFFFFFFFFFFFFULL, 0x8000000000000001ULL); + test_rshift_one(0x7FFE0000U, 64, 0x0000000000000000ULL, 0x7FFFFFFFFFFFFFFEULL); + test_rshift_one(0xFFFE0000U, 64, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFEULL); + test_rshift_one(0x00010000U, 60, 0x0000000000000000ULL, 0x0000000000000010ULL); + test_rshift_one(0x80010000U, 60, 0xFFFFFFFFFFFFFFF8ULL, 0x0000000000000010ULL); + test_rshift_one(0x00018000U, 60, 0x0000000000000000ULL, 0x0000000000000018ULL); + test_rshift_one(0x80018000U, 60, 0xFFFFFFFFFFFFFFF8ULL, 0x0000000000000018ULL); + test_rshift_one(0x7FFE0000U, 60, 0x0000000000000007ULL, 0xFFFFFFFFFFFFFFE0ULL); + test_rshift_one(0xFFFE0000U, 60, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFE0ULL); + test_rshift_one(0x7FFE8000U, 60, 0x0000000000000007ULL, 0xFFFFFFFFFFFFFFE8ULL); + test_rshift_one(0xFFFE8000U, 60, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFE8ULL); + test_rshift_one(0x00018000U, 0, 0x0000000000000001ULL, 0x8000000000000000ULL); + test_rshift_one(0x80018000U, 0, 0x8000000000000001ULL, 0x8000000000000000ULL); + test_rshift_one(0x7FFE0000U, 0, 0x7FFFFFFFFFFFFFFEULL, 0x0000000000000000ULL); + test_rshift_one(0xFFFE0000U, 0, 0xFFFFFFFFFFFFFFFEULL, 0x0000000000000000ULL); + test_rshift_one(0x7FFE8000U, 0, 0x7FFFFFFFFFFFFFFEULL, 0x8000000000000000ULL); + test_rshift_one(0xFFFE8000U, 0, 0xFFFFFFFFFFFFFFFEULL, 0x8000000000000000ULL); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + g_test_add_func("/int128/int128_and", test_and); + g_test_add_func("/int128/int128_add", test_add); + g_test_add_func("/int128/int128_sub", test_sub); + g_test_add_func("/int128/int128_neg", test_neg); + g_test_add_func("/int128/int128_nz", test_nz); + g_test_add_func("/int128/int128_le", test_le); + g_test_add_func("/int128/int128_lt", test_lt); + g_test_add_func("/int128/int128_ge", test_ge); + g_test_add_func("/int128/int128_gt", test_gt); + g_test_add_func("/int128/int128_rshift", test_rshift); + return g_test_run(); +} diff --git a/tests/unit/test-io-channel-buffer.c b/tests/unit/test-io-channel-buffer.c new file mode 100644 index 0000000000..9c6724dea4 --- /dev/null +++ b/tests/unit/test-io-channel-buffer.c @@ -0,0 +1,52 @@ +/* + * QEMU I/O channel buffer test + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + * + */ + +#include "qemu/osdep.h" +#include "io/channel-buffer.h" +#include "qemu/module.h" +#include "io-channel-helpers.h" + + +static void test_io_channel_buf(void) +{ + QIOChannelBuffer *buf; + QIOChannelTest *test; + + buf = qio_channel_buffer_new(0); + + test = qio_channel_test_new(); + qio_channel_test_run_writer(test, QIO_CHANNEL(buf)); + buf->offset = 0; + qio_channel_test_run_reader(test, QIO_CHANNEL(buf)); + qio_channel_test_validate(test); + + object_unref(OBJECT(buf)); +} + + +int main(int argc, char **argv) +{ + module_call_init(MODULE_INIT_QOM); + + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/io/channel/buf", test_io_channel_buf); + return g_test_run(); +} diff --git a/tests/unit/test-io-channel-command.c b/tests/unit/test-io-channel-command.c new file mode 100644 index 0000000000..99056e07c0 --- /dev/null +++ b/tests/unit/test-io-channel-command.c @@ -0,0 +1,130 @@ +/* + * QEMU I/O channel command test + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + * + */ + +#include "qemu/osdep.h" +#include "io/channel-command.h" +#include "io-channel-helpers.h" +#include "qapi/error.h" +#include "qemu/module.h" + +#ifndef WIN32 +static void test_io_channel_command_fifo(bool async) +{ +#define TEST_FIFO "tests/test-io-channel-command.fifo" + QIOChannel *src, *dst; + QIOChannelTest *test; + const char *srcfifo = "PIPE:" TEST_FIFO ",wronly"; + const char *dstfifo = "PIPE:" TEST_FIFO ",rdonly"; + const char *srcargv[] = { + "/bin/socat", "-", srcfifo, NULL, + }; + const char *dstargv[] = { + "/bin/socat", dstfifo, "-", NULL, + }; + + unlink(TEST_FIFO); + if (access("/bin/socat", X_OK) < 0) { + return; /* Pretend success if socat is not present */ + } + if (mkfifo(TEST_FIFO, 0600) < 0) { + abort(); + } + src = QIO_CHANNEL(qio_channel_command_new_spawn(srcargv, + O_WRONLY, + &error_abort)); + dst = QIO_CHANNEL(qio_channel_command_new_spawn(dstargv, + O_RDONLY, + &error_abort)); + + test = qio_channel_test_new(); + qio_channel_test_run_threads(test, async, src, dst); + qio_channel_test_validate(test); + + object_unref(OBJECT(src)); + object_unref(OBJECT(dst)); + + unlink(TEST_FIFO); +} + + +static void test_io_channel_command_fifo_async(void) +{ + test_io_channel_command_fifo(true); +} + +static void test_io_channel_command_fifo_sync(void) +{ + test_io_channel_command_fifo(false); +} + + +static void test_io_channel_command_echo(bool async) +{ + QIOChannel *ioc; + QIOChannelTest *test; + const char *socatargv[] = { + "/bin/socat", "-", "-", NULL, + }; + + if (access("/bin/socat", X_OK) < 0) { + return; /* Pretend success if socat is not present */ + } + + ioc = QIO_CHANNEL(qio_channel_command_new_spawn(socatargv, + O_RDWR, + &error_abort)); + test = qio_channel_test_new(); + qio_channel_test_run_threads(test, async, ioc, ioc); + qio_channel_test_validate(test); + + object_unref(OBJECT(ioc)); +} + + +static void test_io_channel_command_echo_async(void) +{ + test_io_channel_command_echo(true); +} + +static void test_io_channel_command_echo_sync(void) +{ + test_io_channel_command_echo(false); +} +#endif + +int main(int argc, char **argv) +{ + module_call_init(MODULE_INIT_QOM); + + g_test_init(&argc, &argv, NULL); + +#ifndef WIN32 + g_test_add_func("/io/channel/command/fifo/sync", + test_io_channel_command_fifo_sync); + g_test_add_func("/io/channel/command/fifo/async", + test_io_channel_command_fifo_async); + g_test_add_func("/io/channel/command/echo/sync", + test_io_channel_command_echo_sync); + g_test_add_func("/io/channel/command/echo/async", + test_io_channel_command_echo_async); +#endif + + return g_test_run(); +} diff --git a/tests/unit/test-io-channel-file.c b/tests/unit/test-io-channel-file.c new file mode 100644 index 0000000000..29038e67b6 --- /dev/null +++ b/tests/unit/test-io-channel-file.c @@ -0,0 +1,155 @@ +/* + * QEMU I/O channel file test + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + * + */ + +#include "qemu/osdep.h" +#include "io/channel-file.h" +#include "io/channel-util.h" +#include "io-channel-helpers.h" +#include "qapi/error.h" +#include "qemu/module.h" + +#define TEST_FILE "tests/test-io-channel-file.txt" +#define TEST_MASK 0600 + +/* + * On Windows the stat() function in the C library checks only + * the FAT-style READONLY attribute and does not look at the ACL at all. + */ +#ifdef _WIN32 +#define TEST_MASK_EXPECT 0700 +#else +#define TEST_MASK_EXPECT 0777 +#endif + +static void test_io_channel_file_helper(int flags) +{ + QIOChannel *src, *dst; + QIOChannelTest *test; + struct stat st; + mode_t mask; + int ret; + + unlink(TEST_FILE); + src = QIO_CHANNEL(qio_channel_file_new_path( + TEST_FILE, + flags, TEST_MASK, + &error_abort)); + dst = QIO_CHANNEL(qio_channel_file_new_path( + TEST_FILE, + O_RDONLY | O_BINARY, 0, + &error_abort)); + + test = qio_channel_test_new(); + qio_channel_test_run_writer(test, src); + qio_channel_test_run_reader(test, dst); + qio_channel_test_validate(test); + + /* Check that the requested mode took effect. */ + mask = umask(0); + umask(mask); + ret = stat(TEST_FILE, &st); + g_assert_cmpint(ret, >, -1); + g_assert_cmpuint(TEST_MASK & ~mask, ==, st.st_mode & TEST_MASK_EXPECT); + + unlink(TEST_FILE); + object_unref(OBJECT(src)); + object_unref(OBJECT(dst)); +} + +static void test_io_channel_file(void) +{ + test_io_channel_file_helper(O_WRONLY | O_CREAT | O_TRUNC | O_BINARY); +} + +static void test_io_channel_file_rdwr(void) +{ + test_io_channel_file_helper(O_RDWR | O_CREAT | O_TRUNC | O_BINARY); +} + +static void test_io_channel_fd(void) +{ + QIOChannel *ioc; + int fd = -1; + + fd = open(TEST_FILE, O_CREAT | O_TRUNC | O_WRONLY, 0600); + g_assert_cmpint(fd, >, -1); + + ioc = qio_channel_new_fd(fd, &error_abort); + + g_assert_cmpstr(object_get_typename(OBJECT(ioc)), + ==, + TYPE_QIO_CHANNEL_FILE); + + unlink(TEST_FILE); + object_unref(OBJECT(ioc)); +} + + +#ifndef _WIN32 +static void test_io_channel_pipe(bool async) +{ + QIOChannel *src, *dst; + QIOChannelTest *test; + int fd[2]; + + if (pipe(fd) < 0) { + perror("pipe"); + abort(); + } + + src = QIO_CHANNEL(qio_channel_file_new_fd(fd[1])); + dst = QIO_CHANNEL(qio_channel_file_new_fd(fd[0])); + + test = qio_channel_test_new(); + qio_channel_test_run_threads(test, async, src, dst); + qio_channel_test_validate(test); + + object_unref(OBJECT(src)); + object_unref(OBJECT(dst)); +} + + +static void test_io_channel_pipe_async(void) +{ + test_io_channel_pipe(true); +} + +static void test_io_channel_pipe_sync(void) +{ + test_io_channel_pipe(false); +} +#endif /* ! _WIN32 */ + + +int main(int argc, char **argv) +{ + module_call_init(MODULE_INIT_QOM); + + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/io/channel/file", test_io_channel_file); + g_test_add_func("/io/channel/file/rdwr", test_io_channel_file_rdwr); + g_test_add_func("/io/channel/file/fd", test_io_channel_fd); +#ifndef _WIN32 + g_test_add_func("/io/channel/pipe/sync", test_io_channel_pipe_sync); + g_test_add_func("/io/channel/pipe/async", test_io_channel_pipe_async); +#endif + return g_test_run(); +} diff --git a/tests/unit/test-io-channel-socket.c b/tests/unit/test-io-channel-socket.c new file mode 100644 index 0000000000..c49eec1f03 --- /dev/null +++ b/tests/unit/test-io-channel-socket.c @@ -0,0 +1,603 @@ +/* + * QEMU I/O channel sockets test + * + * Copyright (c) 2015-2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + * + */ + +#include "qemu/osdep.h" +#include "io/channel-socket.h" +#include "io/channel-util.h" +#include "io-channel-helpers.h" +#include "socket-helpers.h" +#include "qapi/error.h" +#include "qemu/module.h" +#include "qemu/main-loop.h" + + +static void test_io_channel_set_socket_bufs(QIOChannel *src, + QIOChannel *dst) +{ + int buflen = 64 * 1024; + + /* + * Make the socket buffers small so that we see + * the effects of partial reads/writes + */ + setsockopt(((QIOChannelSocket *)src)->fd, + SOL_SOCKET, SO_SNDBUF, + (char *)&buflen, + sizeof(buflen)); + + setsockopt(((QIOChannelSocket *)dst)->fd, + SOL_SOCKET, SO_SNDBUF, + (char *)&buflen, + sizeof(buflen)); +} + + +static void test_io_channel_setup_sync(SocketAddress *listen_addr, + SocketAddress *connect_addr, + QIOChannel **srv, + QIOChannel **src, + QIOChannel **dst) +{ + QIOChannelSocket *lioc; + + lioc = qio_channel_socket_new(); + qio_channel_socket_listen_sync(lioc, listen_addr, 1, &error_abort); + + if (listen_addr->type == SOCKET_ADDRESS_TYPE_INET) { + SocketAddress *laddr = qio_channel_socket_get_local_address( + lioc, &error_abort); + + g_free(connect_addr->u.inet.port); + connect_addr->u.inet.port = g_strdup(laddr->u.inet.port); + + qapi_free_SocketAddress(laddr); + } + + *src = QIO_CHANNEL(qio_channel_socket_new()); + qio_channel_socket_connect_sync( + QIO_CHANNEL_SOCKET(*src), connect_addr, &error_abort); + qio_channel_set_delay(*src, false); + + qio_channel_wait(QIO_CHANNEL(lioc), G_IO_IN); + *dst = QIO_CHANNEL(qio_channel_socket_accept(lioc, &error_abort)); + g_assert(*dst); + + test_io_channel_set_socket_bufs(*src, *dst); + + *srv = QIO_CHANNEL(lioc); +} + + +struct TestIOChannelData { + bool err; + GMainLoop *loop; +}; + + +static void test_io_channel_complete(QIOTask *task, + gpointer opaque) +{ + struct TestIOChannelData *data = opaque; + data->err = qio_task_propagate_error(task, NULL); + g_main_loop_quit(data->loop); +} + + +static void test_io_channel_setup_async(SocketAddress *listen_addr, + SocketAddress *connect_addr, + QIOChannel **srv, + QIOChannel **src, + QIOChannel **dst) +{ + QIOChannelSocket *lioc; + struct TestIOChannelData data; + + data.loop = g_main_loop_new(g_main_context_default(), + TRUE); + + lioc = qio_channel_socket_new(); + qio_channel_socket_listen_async( + lioc, listen_addr, 1, + test_io_channel_complete, &data, NULL, NULL); + + g_main_loop_run(data.loop); + g_main_context_iteration(g_main_context_default(), FALSE); + + g_assert(!data.err); + + if (listen_addr->type == SOCKET_ADDRESS_TYPE_INET) { + SocketAddress *laddr = qio_channel_socket_get_local_address( + lioc, &error_abort); + + g_free(connect_addr->u.inet.port); + connect_addr->u.inet.port = g_strdup(laddr->u.inet.port); + + qapi_free_SocketAddress(laddr); + } + + *src = QIO_CHANNEL(qio_channel_socket_new()); + + qio_channel_socket_connect_async( + QIO_CHANNEL_SOCKET(*src), connect_addr, + test_io_channel_complete, &data, NULL, NULL); + + g_main_loop_run(data.loop); + g_main_context_iteration(g_main_context_default(), FALSE); + + g_assert(!data.err); + + qio_channel_wait(QIO_CHANNEL(lioc), G_IO_IN); + *dst = QIO_CHANNEL(qio_channel_socket_accept(lioc, &error_abort)); + g_assert(*dst); + + qio_channel_set_delay(*src, false); + test_io_channel_set_socket_bufs(*src, *dst); + + *srv = QIO_CHANNEL(lioc); + + g_main_loop_unref(data.loop); +} + + +static void test_io_channel_socket_path_exists(SocketAddress *addr, + bool expectExists) +{ + if (addr->type != SOCKET_ADDRESS_TYPE_UNIX) { + return; + } + + g_assert(g_file_test(addr->u.q_unix.path, + G_FILE_TEST_EXISTS) == expectExists); +} + + +static void test_io_channel(bool async, + SocketAddress *listen_addr, + SocketAddress *connect_addr, + bool passFD) +{ + QIOChannel *src, *dst, *srv; + QIOChannelTest *test; + if (async) { + test_io_channel_setup_async(listen_addr, connect_addr, + &srv, &src, &dst); + + g_assert(!passFD || + qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_FD_PASS)); + g_assert(!passFD || + qio_channel_has_feature(dst, QIO_CHANNEL_FEATURE_FD_PASS)); + g_assert(qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_SHUTDOWN)); + g_assert(qio_channel_has_feature(dst, QIO_CHANNEL_FEATURE_SHUTDOWN)); + + test_io_channel_socket_path_exists(listen_addr, true); + + test = qio_channel_test_new(); + qio_channel_test_run_threads(test, true, src, dst); + qio_channel_test_validate(test); + + test_io_channel_socket_path_exists(listen_addr, true); + + /* unref without close, to ensure finalize() cleans up */ + + object_unref(OBJECT(src)); + object_unref(OBJECT(dst)); + test_io_channel_socket_path_exists(listen_addr, true); + + object_unref(OBJECT(srv)); + test_io_channel_socket_path_exists(listen_addr, false); + + test_io_channel_setup_async(listen_addr, connect_addr, + &srv, &src, &dst); + + g_assert(!passFD || + qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_FD_PASS)); + g_assert(!passFD || + qio_channel_has_feature(dst, QIO_CHANNEL_FEATURE_FD_PASS)); + g_assert(qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_SHUTDOWN)); + g_assert(qio_channel_has_feature(dst, QIO_CHANNEL_FEATURE_SHUTDOWN)); + + test = qio_channel_test_new(); + qio_channel_test_run_threads(test, false, src, dst); + qio_channel_test_validate(test); + + /* close before unref, to ensure finalize copes with already closed */ + + qio_channel_close(src, &error_abort); + qio_channel_close(dst, &error_abort); + test_io_channel_socket_path_exists(listen_addr, true); + + object_unref(OBJECT(src)); + object_unref(OBJECT(dst)); + test_io_channel_socket_path_exists(listen_addr, true); + + qio_channel_close(srv, &error_abort); + test_io_channel_socket_path_exists(listen_addr, false); + + object_unref(OBJECT(srv)); + test_io_channel_socket_path_exists(listen_addr, false); + } else { + test_io_channel_setup_sync(listen_addr, connect_addr, + &srv, &src, &dst); + + g_assert(!passFD || + qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_FD_PASS)); + g_assert(!passFD || + qio_channel_has_feature(dst, QIO_CHANNEL_FEATURE_FD_PASS)); + g_assert(qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_SHUTDOWN)); + g_assert(qio_channel_has_feature(dst, QIO_CHANNEL_FEATURE_SHUTDOWN)); + + test_io_channel_socket_path_exists(listen_addr, true); + + test = qio_channel_test_new(); + qio_channel_test_run_threads(test, true, src, dst); + qio_channel_test_validate(test); + + test_io_channel_socket_path_exists(listen_addr, true); + + /* unref without close, to ensure finalize() cleans up */ + + object_unref(OBJECT(src)); + object_unref(OBJECT(dst)); + test_io_channel_socket_path_exists(listen_addr, true); + + object_unref(OBJECT(srv)); + test_io_channel_socket_path_exists(listen_addr, false); + + test_io_channel_setup_sync(listen_addr, connect_addr, + &srv, &src, &dst); + + g_assert(!passFD || + qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_FD_PASS)); + g_assert(!passFD || + qio_channel_has_feature(dst, QIO_CHANNEL_FEATURE_FD_PASS)); + g_assert(qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_SHUTDOWN)); + g_assert(qio_channel_has_feature(dst, QIO_CHANNEL_FEATURE_SHUTDOWN)); + + test = qio_channel_test_new(); + qio_channel_test_run_threads(test, false, src, dst); + qio_channel_test_validate(test); + + test_io_channel_socket_path_exists(listen_addr, true); + + /* close before unref, to ensure finalize copes with already closed */ + + qio_channel_close(src, &error_abort); + qio_channel_close(dst, &error_abort); + test_io_channel_socket_path_exists(listen_addr, true); + + object_unref(OBJECT(src)); + object_unref(OBJECT(dst)); + test_io_channel_socket_path_exists(listen_addr, true); + + qio_channel_close(srv, &error_abort); + test_io_channel_socket_path_exists(listen_addr, false); + + object_unref(OBJECT(srv)); + test_io_channel_socket_path_exists(listen_addr, false); + } +} + + +static void test_io_channel_ipv4(bool async) +{ + SocketAddress *listen_addr = g_new0(SocketAddress, 1); + SocketAddress *connect_addr = g_new0(SocketAddress, 1); + + listen_addr->type = SOCKET_ADDRESS_TYPE_INET; + listen_addr->u.inet = (InetSocketAddress) { + .host = g_strdup("127.0.0.1"), + .port = NULL, /* Auto-select */ + }; + + connect_addr->type = SOCKET_ADDRESS_TYPE_INET; + connect_addr->u.inet = (InetSocketAddress) { + .host = g_strdup("127.0.0.1"), + .port = NULL, /* Filled in later */ + }; + + test_io_channel(async, listen_addr, connect_addr, false); + + qapi_free_SocketAddress(listen_addr); + qapi_free_SocketAddress(connect_addr); +} + + +static void test_io_channel_ipv4_sync(void) +{ + return test_io_channel_ipv4(false); +} + + +static void test_io_channel_ipv4_async(void) +{ + return test_io_channel_ipv4(true); +} + + +static void test_io_channel_ipv6(bool async) +{ + SocketAddress *listen_addr = g_new0(SocketAddress, 1); + SocketAddress *connect_addr = g_new0(SocketAddress, 1); + + listen_addr->type = SOCKET_ADDRESS_TYPE_INET; + listen_addr->u.inet = (InetSocketAddress) { + .host = g_strdup("::1"), + .port = NULL, /* Auto-select */ + }; + + connect_addr->type = SOCKET_ADDRESS_TYPE_INET; + connect_addr->u.inet = (InetSocketAddress) { + .host = g_strdup("::1"), + .port = NULL, /* Filled in later */ + }; + + test_io_channel(async, listen_addr, connect_addr, false); + + qapi_free_SocketAddress(listen_addr); + qapi_free_SocketAddress(connect_addr); +} + + +static void test_io_channel_ipv6_sync(void) +{ + return test_io_channel_ipv6(false); +} + + +static void test_io_channel_ipv6_async(void) +{ + return test_io_channel_ipv6(true); +} + + +#ifndef _WIN32 +static void test_io_channel_unix(bool async) +{ + SocketAddress *listen_addr = g_new0(SocketAddress, 1); + SocketAddress *connect_addr = g_new0(SocketAddress, 1); + +#define TEST_SOCKET "test-io-channel-socket.sock" + listen_addr->type = SOCKET_ADDRESS_TYPE_UNIX; + listen_addr->u.q_unix.path = g_strdup(TEST_SOCKET); + + connect_addr->type = SOCKET_ADDRESS_TYPE_UNIX; + connect_addr->u.q_unix.path = g_strdup(TEST_SOCKET); + + test_io_channel(async, listen_addr, connect_addr, true); + + qapi_free_SocketAddress(listen_addr); + qapi_free_SocketAddress(connect_addr); +} + + +static void test_io_channel_unix_sync(void) +{ + return test_io_channel_unix(false); +} + + +static void test_io_channel_unix_async(void) +{ + return test_io_channel_unix(true); +} + +static void test_io_channel_unix_fd_pass(void) +{ + SocketAddress *listen_addr = g_new0(SocketAddress, 1); + SocketAddress *connect_addr = g_new0(SocketAddress, 1); + QIOChannel *src, *dst, *srv; + int testfd; + int fdsend[3]; + int *fdrecv = NULL; + size_t nfdrecv = 0; + size_t i; + char bufsend[12], bufrecv[12]; + struct iovec iosend[1], iorecv[1]; + +#define TEST_SOCKET "test-io-channel-socket.sock" +#define TEST_FILE "test-io-channel-socket.txt" + + testfd = open(TEST_FILE, O_RDWR|O_TRUNC|O_CREAT, 0700); + g_assert(testfd != -1); + fdsend[0] = testfd; + fdsend[1] = testfd; + fdsend[2] = testfd; + + listen_addr->type = SOCKET_ADDRESS_TYPE_UNIX; + listen_addr->u.q_unix.path = g_strdup(TEST_SOCKET); + + connect_addr->type = SOCKET_ADDRESS_TYPE_UNIX; + connect_addr->u.q_unix.path = g_strdup(TEST_SOCKET); + + test_io_channel_setup_sync(listen_addr, connect_addr, &srv, &src, &dst); + + memcpy(bufsend, "Hello World", G_N_ELEMENTS(bufsend)); + + iosend[0].iov_base = bufsend; + iosend[0].iov_len = G_N_ELEMENTS(bufsend); + + iorecv[0].iov_base = bufrecv; + iorecv[0].iov_len = G_N_ELEMENTS(bufrecv); + + g_assert(qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_FD_PASS)); + g_assert(qio_channel_has_feature(dst, QIO_CHANNEL_FEATURE_FD_PASS)); + + qio_channel_writev_full(src, + iosend, + G_N_ELEMENTS(iosend), + fdsend, + G_N_ELEMENTS(fdsend), + &error_abort); + + qio_channel_readv_full(dst, + iorecv, + G_N_ELEMENTS(iorecv), + &fdrecv, + &nfdrecv, + &error_abort); + + g_assert(nfdrecv == G_N_ELEMENTS(fdsend)); + /* Each recvd FD should be different from sent FD */ + for (i = 0; i < nfdrecv; i++) { + g_assert_cmpint(fdrecv[i], !=, testfd); + } + /* Each recvd FD should be different from each other */ + g_assert_cmpint(fdrecv[0], !=, fdrecv[1]); + g_assert_cmpint(fdrecv[0], !=, fdrecv[2]); + g_assert_cmpint(fdrecv[1], !=, fdrecv[2]); + + /* Check the I/O buf we sent at the same time matches */ + g_assert(memcmp(bufsend, bufrecv, G_N_ELEMENTS(bufsend)) == 0); + + /* Write some data into the FD we received */ + g_assert(write(fdrecv[0], bufsend, G_N_ELEMENTS(bufsend)) == + G_N_ELEMENTS(bufsend)); + + /* Read data from the original FD and make sure it matches */ + memset(bufrecv, 0, G_N_ELEMENTS(bufrecv)); + g_assert(lseek(testfd, 0, SEEK_SET) == 0); + g_assert(read(testfd, bufrecv, G_N_ELEMENTS(bufrecv)) == + G_N_ELEMENTS(bufrecv)); + g_assert(memcmp(bufsend, bufrecv, G_N_ELEMENTS(bufsend)) == 0); + + object_unref(OBJECT(src)); + object_unref(OBJECT(dst)); + object_unref(OBJECT(srv)); + qapi_free_SocketAddress(listen_addr); + qapi_free_SocketAddress(connect_addr); + unlink(TEST_SOCKET); + unlink(TEST_FILE); + close(testfd); + for (i = 0; i < nfdrecv; i++) { + close(fdrecv[i]); + } + g_free(fdrecv); +} + +static void test_io_channel_unix_listen_cleanup(void) +{ + QIOChannelSocket *ioc; + struct sockaddr_un un; + int sock; + +#define TEST_SOCKET "test-io-channel-socket.sock" + + ioc = qio_channel_socket_new(); + + /* Manually bind ioc without calling the qio api to avoid setting + * the LISTEN feature */ + sock = qemu_socket(PF_UNIX, SOCK_STREAM, 0); + memset(&un, 0, sizeof(un)); + un.sun_family = AF_UNIX; + snprintf(un.sun_path, sizeof(un.sun_path), "%s", TEST_SOCKET); + unlink(TEST_SOCKET); + bind(sock, (struct sockaddr *)&un, sizeof(un)); + ioc->fd = sock; + ioc->localAddrLen = sizeof(ioc->localAddr); + getsockname(sock, (struct sockaddr *)&ioc->localAddr, + &ioc->localAddrLen); + + g_assert(g_file_test(TEST_SOCKET, G_FILE_TEST_EXISTS)); + object_unref(OBJECT(ioc)); + g_assert(g_file_test(TEST_SOCKET, G_FILE_TEST_EXISTS)); + + unlink(TEST_SOCKET); +} + +#endif /* _WIN32 */ + + +static void test_io_channel_ipv4_fd(void) +{ + QIOChannel *ioc; + int fd = -1; + struct sockaddr_in sa = { + .sin_family = AF_INET, + .sin_addr = { + .s_addr = htonl(INADDR_LOOPBACK), + } + /* Leave port unset for auto-assign */ + }; + socklen_t salen = sizeof(sa); + + fd = socket(AF_INET, SOCK_STREAM, 0); + g_assert_cmpint(fd, >, -1); + + g_assert_cmpint(bind(fd, (struct sockaddr *)&sa, salen), ==, 0); + + ioc = qio_channel_new_fd(fd, &error_abort); + + g_assert_cmpstr(object_get_typename(OBJECT(ioc)), + ==, + TYPE_QIO_CHANNEL_SOCKET); + + object_unref(OBJECT(ioc)); +} + + +int main(int argc, char **argv) +{ + bool has_ipv4, has_ipv6; + + module_call_init(MODULE_INIT_QOM); + qemu_init_main_loop(&error_abort); + socket_init(); + + g_test_init(&argc, &argv, NULL); + + /* We're creating actual IPv4/6 sockets, so we should + * check if the host running tests actually supports + * each protocol to avoid breaking tests on machines + * with either IPv4 or IPv6 disabled. + */ + if (socket_check_protocol_support(&has_ipv4, &has_ipv6) < 0) { + g_printerr("socket_check_protocol_support() failed\n"); + goto end; + } + + if (has_ipv4) { + g_test_add_func("/io/channel/socket/ipv4-sync", + test_io_channel_ipv4_sync); + g_test_add_func("/io/channel/socket/ipv4-async", + test_io_channel_ipv4_async); + g_test_add_func("/io/channel/socket/ipv4-fd", + test_io_channel_ipv4_fd); + } + if (has_ipv6) { + g_test_add_func("/io/channel/socket/ipv6-sync", + test_io_channel_ipv6_sync); + g_test_add_func("/io/channel/socket/ipv6-async", + test_io_channel_ipv6_async); + } + +#ifndef _WIN32 + g_test_add_func("/io/channel/socket/unix-sync", + test_io_channel_unix_sync); + g_test_add_func("/io/channel/socket/unix-async", + test_io_channel_unix_async); + g_test_add_func("/io/channel/socket/unix-fd-pass", + test_io_channel_unix_fd_pass); + g_test_add_func("/io/channel/socket/unix-listen-cleanup", + test_io_channel_unix_listen_cleanup); +#endif /* _WIN32 */ + +end: + return g_test_run(); +} diff --git a/tests/unit/test-io-channel-tls.c b/tests/unit/test-io-channel-tls.c new file mode 100644 index 0000000000..ad7554c534 --- /dev/null +++ b/tests/unit/test-io-channel-tls.c @@ -0,0 +1,346 @@ +/* + * QEMU I/O channel TLS test + * + * Copyright (C) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + * + * Author: Daniel P. Berrange + */ + + +#include "qemu/osdep.h" + +#include "crypto-tls-x509-helpers.h" +#include "io/channel-tls.h" +#include "io/channel-socket.h" +#include "io-channel-helpers.h" +#include "crypto/init.h" +#include "crypto/tlscredsx509.h" +#include "qapi/error.h" +#include "qemu/module.h" +#include "authz/list.h" +#include "qom/object_interfaces.h" + +#ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT + +#define WORKDIR "tests/test-io-channel-tls-work/" +#define KEYFILE WORKDIR "key-ctx.pem" + +struct QIOChannelTLSTestData { + const char *servercacrt; + const char *clientcacrt; + const char *servercrt; + const char *clientcrt; + bool expectServerFail; + bool expectClientFail; + const char *hostname; + const char *const *wildcards; +}; + +struct QIOChannelTLSHandshakeData { + bool finished; + bool failed; +}; + +static void test_tls_handshake_done(QIOTask *task, + gpointer opaque) +{ + struct QIOChannelTLSHandshakeData *data = opaque; + + data->finished = true; + data->failed = qio_task_propagate_error(task, NULL); +} + + +static QCryptoTLSCreds *test_tls_creds_create(QCryptoTLSCredsEndpoint endpoint, + const char *certdir) +{ + Object *parent = object_get_objects_root(); + Object *creds = object_new_with_props( + TYPE_QCRYPTO_TLS_CREDS_X509, + parent, + (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ? + "testtlscredsserver" : "testtlscredsclient"), + &error_abort, + "endpoint", (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ? + "server" : "client"), + "dir", certdir, + "verify-peer", "yes", + "priority", "NORMAL", + /* We skip initial sanity checks here because we + * want to make sure that problems are being + * detected at the TLS session validation stage, + * and the test-crypto-tlscreds test already + * validate the sanity check code. + */ + "sanity-check", "no", + NULL + ); + + return QCRYPTO_TLS_CREDS(creds); +} + + +/* + * This tests validation checking of peer certificates + * + * This is replicating the checks that are done for an + * active TLS session after handshake completes. To + * simulate that we create our TLS contexts, skipping + * sanity checks. When then get a socketpair, and + * initiate a TLS session across them. Finally do + * do actual cert validation tests + */ +static void test_io_channel_tls(const void *opaque) +{ + struct QIOChannelTLSTestData *data = + (struct QIOChannelTLSTestData *)opaque; + QCryptoTLSCreds *clientCreds; + QCryptoTLSCreds *serverCreds; + QIOChannelTLS *clientChanTLS; + QIOChannelTLS *serverChanTLS; + QIOChannelSocket *clientChanSock; + QIOChannelSocket *serverChanSock; + QAuthZList *auth; + const char * const *wildcards; + int channel[2]; + struct QIOChannelTLSHandshakeData clientHandshake = { false, false }; + struct QIOChannelTLSHandshakeData serverHandshake = { false, false }; + QIOChannelTest *test; + GMainContext *mainloop; + + /* We'll use this for our fake client-server connection */ + g_assert(socketpair(AF_UNIX, SOCK_STREAM, 0, channel) == 0); + +#define CLIENT_CERT_DIR "tests/test-io-channel-tls-client/" +#define SERVER_CERT_DIR "tests/test-io-channel-tls-server/" + mkdir(CLIENT_CERT_DIR, 0700); + mkdir(SERVER_CERT_DIR, 0700); + + unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT); + unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT); + unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY); + + unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT); + unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT); + unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY); + + g_assert(link(data->servercacrt, + SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT) == 0); + g_assert(link(data->servercrt, + SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT) == 0); + g_assert(link(KEYFILE, + SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY) == 0); + + g_assert(link(data->clientcacrt, + CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT) == 0); + g_assert(link(data->clientcrt, + CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT) == 0); + g_assert(link(KEYFILE, + CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY) == 0); + + clientCreds = test_tls_creds_create( + QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, + CLIENT_CERT_DIR); + g_assert(clientCreds != NULL); + + serverCreds = test_tls_creds_create( + QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, + SERVER_CERT_DIR); + g_assert(serverCreds != NULL); + + auth = qauthz_list_new("channeltlsacl", + QAUTHZ_LIST_POLICY_DENY, + &error_abort); + wildcards = data->wildcards; + while (wildcards && *wildcards) { + qauthz_list_append_rule(auth, *wildcards, + QAUTHZ_LIST_POLICY_ALLOW, + QAUTHZ_LIST_FORMAT_GLOB, + &error_abort); + wildcards++; + } + + clientChanSock = qio_channel_socket_new_fd( + channel[0], &error_abort); + g_assert(clientChanSock != NULL); + serverChanSock = qio_channel_socket_new_fd( + channel[1], &error_abort); + g_assert(serverChanSock != NULL); + + /* + * We have an evil loop to do the handshake in a single + * thread, so we need these non-blocking to avoid deadlock + * of ourselves + */ + qio_channel_set_blocking(QIO_CHANNEL(clientChanSock), false, NULL); + qio_channel_set_blocking(QIO_CHANNEL(serverChanSock), false, NULL); + + /* Now the real part of the test, setup the sessions */ + clientChanTLS = qio_channel_tls_new_client( + QIO_CHANNEL(clientChanSock), clientCreds, + data->hostname, &error_abort); + g_assert(clientChanTLS != NULL); + + serverChanTLS = qio_channel_tls_new_server( + QIO_CHANNEL(serverChanSock), serverCreds, + "channeltlsacl", &error_abort); + g_assert(serverChanTLS != NULL); + + qio_channel_tls_handshake(clientChanTLS, + test_tls_handshake_done, + &clientHandshake, + NULL, + NULL); + qio_channel_tls_handshake(serverChanTLS, + test_tls_handshake_done, + &serverHandshake, + NULL, + NULL); + + /* + * Finally we loop around & around doing handshake on each + * session until we get an error, or the handshake completes. + * This relies on the socketpair being nonblocking to avoid + * deadlocking ourselves upon handshake + */ + mainloop = g_main_context_default(); + do { + g_main_context_iteration(mainloop, TRUE); + } while (!clientHandshake.finished || + !serverHandshake.finished); + + g_assert(clientHandshake.failed == data->expectClientFail); + g_assert(serverHandshake.failed == data->expectServerFail); + + test = qio_channel_test_new(); + qio_channel_test_run_threads(test, false, + QIO_CHANNEL(clientChanTLS), + QIO_CHANNEL(serverChanTLS)); + qio_channel_test_validate(test); + + test = qio_channel_test_new(); + qio_channel_test_run_threads(test, true, + QIO_CHANNEL(clientChanTLS), + QIO_CHANNEL(serverChanTLS)); + qio_channel_test_validate(test); + + unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT); + unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT); + unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY); + + unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT); + unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT); + unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY); + + rmdir(CLIENT_CERT_DIR); + rmdir(SERVER_CERT_DIR); + + object_unparent(OBJECT(serverCreds)); + object_unparent(OBJECT(clientCreds)); + + object_unref(OBJECT(serverChanTLS)); + object_unref(OBJECT(clientChanTLS)); + + object_unref(OBJECT(serverChanSock)); + object_unref(OBJECT(clientChanSock)); + + object_unparent(OBJECT(auth)); + + close(channel[0]); + close(channel[1]); +} + + +int main(int argc, char **argv) +{ + int ret; + + g_assert(qcrypto_init(NULL) == 0); + + module_call_init(MODULE_INIT_QOM); + g_test_init(&argc, &argv, NULL); + g_setenv("GNUTLS_FORCE_FIPS_MODE", "2", 1); + + mkdir(WORKDIR, 0700); + + test_tls_init(KEYFILE); + +# define TEST_CHANNEL(name, caCrt, \ + serverCrt, clientCrt, \ + expectServerFail, expectClientFail, \ + hostname, wildcards) \ + struct QIOChannelTLSTestData name = { \ + caCrt, caCrt, serverCrt, clientCrt, \ + expectServerFail, expectClientFail, \ + hostname, wildcards \ + }; \ + g_test_add_data_func("/qio/channel/tls/" # name, \ + &name, test_io_channel_tls); + + /* A perfect CA, perfect client & perfect server */ + + /* Basic:CA:critical */ + TLS_ROOT_REQ(cacertreq, + "UK", "qemu CA", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(servercertreq, cacertreq, + "UK", "qemu.org", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); + TLS_CERT_REQ(clientcertreq, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL, + 0, 0); + + const char *const wildcards[] = { + "C=UK,CN=qemu*", + NULL, + }; + TEST_CHANNEL(basic, cacertreq.filename, servercertreq.filename, + clientcertreq.filename, false, false, + "qemu.org", wildcards); + + ret = g_test_run(); + + test_tls_discard_cert(&clientcertreq); + test_tls_discard_cert(&servercertreq); + test_tls_discard_cert(&cacertreq); + + test_tls_cleanup(KEYFILE); + rmdir(WORKDIR); + + return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; +} + +#else /* ! QCRYPTO_HAVE_TLS_TEST_SUPPORT */ + +int +main(void) +{ + return EXIT_SUCCESS; +} + +#endif /* ! QCRYPTO_HAVE_TLS_TEST_SUPPORT */ diff --git a/tests/unit/test-io-task.c b/tests/unit/test-io-task.c new file mode 100644 index 0000000000..953a50ae66 --- /dev/null +++ b/tests/unit/test-io-task.c @@ -0,0 +1,268 @@ +/* + * QEMU I/O task tests + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + * + */ + +#include "qemu/osdep.h" + +#include "qom/object.h" +#include "io/task.h" +#include "qapi/error.h" +#include "qemu/module.h" + +#define TYPE_DUMMY "qemu:dummy" + +typedef struct DummyObject DummyObject; +typedef struct DummyObjectClass DummyObjectClass; + +struct DummyObject { + Object parent; +}; + +struct DummyObjectClass { + ObjectClass parent; +}; + +static const TypeInfo dummy_info = { + .parent = TYPE_OBJECT, + .name = TYPE_DUMMY, + .instance_size = sizeof(DummyObject), + .class_size = sizeof(DummyObjectClass), +}; + +struct TestTaskData { + Object *source; + Error *err; + bool freed; +}; + + +static void task_callback(QIOTask *task, + gpointer opaque) +{ + struct TestTaskData *data = opaque; + + data->source = qio_task_get_source(task); + qio_task_propagate_error(task, &data->err); +} + + +static void test_task_complete(void) +{ + QIOTask *task; + Object *obj = object_new(TYPE_DUMMY); + Object *src; + struct TestTaskData data = { NULL, NULL, false }; + + task = qio_task_new(obj, task_callback, &data, NULL); + src = qio_task_get_source(task); + + qio_task_complete(task); + + g_assert(obj == src); + + object_unref(obj); + + g_assert(data.source == obj); + g_assert(data.err == NULL); + g_assert(data.freed == false); +} + + +static void task_data_free(gpointer opaque) +{ + struct TestTaskData *data = opaque; + + data->freed = true; +} + + +static void test_task_data_free(void) +{ + QIOTask *task; + Object *obj = object_new(TYPE_DUMMY); + struct TestTaskData data = { NULL, NULL, false }; + + task = qio_task_new(obj, task_callback, &data, task_data_free); + + qio_task_complete(task); + + object_unref(obj); + + g_assert(data.source == obj); + g_assert(data.err == NULL); + g_assert(data.freed == true); +} + + +static void test_task_failure(void) +{ + QIOTask *task; + Object *obj = object_new(TYPE_DUMMY); + struct TestTaskData data = { NULL, NULL, false }; + Error *err = NULL; + + task = qio_task_new(obj, task_callback, &data, NULL); + + error_setg(&err, "Some error"); + + qio_task_set_error(task, err); + qio_task_complete(task); + + object_unref(obj); + + g_assert(data.source == obj); + g_assert(data.err == err); + g_assert(data.freed == false); + error_free(data.err); +} + + +struct TestThreadWorkerData { + Object *source; + Error *err; + bool fail; + GThread *worker; + GThread *complete; + GMainLoop *loop; +}; + +static void test_task_thread_worker(QIOTask *task, + gpointer opaque) +{ + struct TestThreadWorkerData *data = opaque; + + data->worker = g_thread_self(); + + if (data->fail) { + Error *err = NULL; + error_setg(&err, "Testing fail"); + qio_task_set_error(task, err); + } +} + + +static void test_task_thread_callback(QIOTask *task, + gpointer opaque) +{ + struct TestThreadWorkerData *data = opaque; + + data->source = qio_task_get_source(task); + qio_task_propagate_error(task, &data->err); + + data->complete = g_thread_self(); + + g_main_loop_quit(data->loop); +} + + +static void test_task_thread_complete(void) +{ + QIOTask *task; + Object *obj = object_new(TYPE_DUMMY); + struct TestThreadWorkerData data = { 0 }; + GThread *self; + + data.loop = g_main_loop_new(g_main_context_default(), + TRUE); + + task = qio_task_new(obj, + test_task_thread_callback, + &data, + NULL); + + qio_task_run_in_thread(task, + test_task_thread_worker, + &data, + NULL, + NULL); + + g_main_loop_run(data.loop); + + g_main_loop_unref(data.loop); + object_unref(obj); + + g_assert(data.source == obj); + g_assert(data.err == NULL); + + self = g_thread_self(); + + /* Make sure the test_task_thread_worker actually got + * run in a different thread */ + g_assert(data.worker != self); + + /* And that the test_task_thread_callback got rnu in + * the main loop thread (ie this one) */ + g_assert(data.complete == self); +} + + +static void test_task_thread_failure(void) +{ + QIOTask *task; + Object *obj = object_new(TYPE_DUMMY); + struct TestThreadWorkerData data = { 0 }; + GThread *self; + + data.loop = g_main_loop_new(g_main_context_default(), + TRUE); + data.fail = true; + + task = qio_task_new(obj, + test_task_thread_callback, + &data, + NULL); + + qio_task_run_in_thread(task, + test_task_thread_worker, + &data, + NULL, + NULL); + + g_main_loop_run(data.loop); + + g_main_loop_unref(data.loop); + object_unref(obj); + + g_assert(data.source == obj); + error_free_or_abort(&data.err); + + self = g_thread_self(); + + /* Make sure the test_task_thread_worker actually got + * run in a different thread */ + g_assert(data.worker != self); + + /* And that the test_task_thread_callback got rnu in + * the main loop thread (ie this one) */ + g_assert(data.complete == self); +} + + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + module_call_init(MODULE_INIT_QOM); + type_register_static(&dummy_info); + g_test_add_func("/crypto/task/complete", test_task_complete); + g_test_add_func("/crypto/task/datafree", test_task_data_free); + g_test_add_func("/crypto/task/failure", test_task_failure); + g_test_add_func("/crypto/task/thread_complete", test_task_thread_complete); + g_test_add_func("/crypto/task/thread_failure", test_task_thread_failure); + return g_test_run(); +} diff --git a/tests/unit/test-iov.c b/tests/unit/test-iov.c new file mode 100644 index 0000000000..9c415e2f1f --- /dev/null +++ b/tests/unit/test-iov.c @@ -0,0 +1,581 @@ +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "qemu/iov.h" +#include "qemu/sockets.h" + +/* create a randomly-sized iovec with random vectors */ +static void iov_random(struct iovec **iovp, unsigned *iov_cntp) +{ + unsigned niov = g_test_rand_int_range(3,8); + struct iovec *iov = g_malloc(niov * sizeof(*iov)); + unsigned i; + for (i = 0; i < niov; ++i) { + iov[i].iov_len = g_test_rand_int_range(5,20); + iov[i].iov_base = g_malloc(iov[i].iov_len); + } + *iovp = iov; + *iov_cntp = niov; +} + +static void iov_free(struct iovec *iov, unsigned niov) +{ + unsigned i; + for (i = 0; i < niov; ++i) { + g_free(iov[i].iov_base); + } + g_free(iov); +} + +static bool iov_equals(const struct iovec *a, const struct iovec *b, + unsigned niov) +{ + return memcmp(a, b, sizeof(a[0]) * niov) == 0; +} + +static void test_iov_bytes(struct iovec *iov, unsigned niov, + size_t offset, size_t bytes) +{ + unsigned i; + size_t j, o; + unsigned char *b; + o = 0; + + /* we walk over all elements, */ + for (i = 0; i < niov; ++i) { + b = iov[i].iov_base; + /* over each char of each element, */ + for (j = 0; j < iov[i].iov_len; ++j) { + /* counting each of them and + * verifying that the ones within [offset,offset+bytes) + * range are equal to the position number (o) */ + if (o >= offset && o < offset + bytes) { + g_assert(b[j] == (o & 255)); + } else { + g_assert(b[j] == 0xff); + } + ++o; + } + } +} + +static void test_to_from_buf_1(void) +{ + unsigned niov; + struct iovec *iov; + size_t sz; + unsigned char *ibuf, *obuf; + unsigned i, j, n; + + iov_random(&iov, &niov); + + sz = iov_size(iov, niov); + + ibuf = g_malloc(sz + 8) + 4; + memcpy(ibuf-4, "aaaa", 4); memcpy(ibuf + sz, "bbbb", 4); + obuf = g_malloc(sz + 8) + 4; + memcpy(obuf-4, "xxxx", 4); memcpy(obuf + sz, "yyyy", 4); + + /* fill in ibuf with 0123456... */ + for (i = 0; i < sz; ++i) { + ibuf[i] = i & 255; + } + + for (i = 0; i <= sz; ++i) { + + /* Test from/to buf for offset(i) in [0..sz] up to the end of buffer. + * For last iteration with offset == sz, the procedure should + * skip whole vector and process exactly 0 bytes */ + + /* first set bytes [i..sz) to some "random" value */ + n = iov_memset(iov, niov, 0, 0xff, sz); + g_assert(n == sz); + + /* next copy bytes [i..sz) from ibuf to iovec */ + n = iov_from_buf(iov, niov, i, ibuf + i, sz - i); + g_assert(n == sz - i); + + /* clear part of obuf */ + memset(obuf + i, 0, sz - i); + /* and set this part of obuf to values from iovec */ + n = iov_to_buf(iov, niov, i, obuf + i, sz - i); + g_assert(n == sz - i); + + /* now compare resulting buffers */ + g_assert(memcmp(ibuf, obuf, sz) == 0); + + /* test just one char */ + n = iov_to_buf(iov, niov, i, obuf + i, 1); + g_assert(n == (i < sz)); + if (n) { + g_assert(obuf[i] == (i & 255)); + } + + for (j = i; j <= sz; ++j) { + /* now test num of bytes cap up to byte no. j, + * with j in [i..sz]. */ + + /* clear iovec */ + n = iov_memset(iov, niov, 0, 0xff, sz); + g_assert(n == sz); + + /* copy bytes [i..j) from ibuf to iovec */ + n = iov_from_buf(iov, niov, i, ibuf + i, j - i); + g_assert(n == j - i); + + /* clear part of obuf */ + memset(obuf + i, 0, j - i); + + /* copy bytes [i..j) from iovec to obuf */ + n = iov_to_buf(iov, niov, i, obuf + i, j - i); + g_assert(n == j - i); + + /* verify result */ + g_assert(memcmp(ibuf, obuf, sz) == 0); + + /* now actually check if the iovec contains the right data */ + test_iov_bytes(iov, niov, i, j - i); + } + } + g_assert(!memcmp(ibuf-4, "aaaa", 4) && !memcmp(ibuf+sz, "bbbb", 4)); + g_free(ibuf-4); + g_assert(!memcmp(obuf-4, "xxxx", 4) && !memcmp(obuf+sz, "yyyy", 4)); + g_free(obuf-4); + iov_free(iov, niov); +} + +static void test_to_from_buf(void) +{ + int x; + for (x = 0; x < 4; ++x) { + test_to_from_buf_1(); + } +} + +static void test_io(void) +{ +#ifndef _WIN32 +/* socketpair(PF_UNIX) which does not exist on windows */ + + int sv[2]; + int r; + unsigned i, j, k, s, t; + fd_set fds; + unsigned niov; + struct iovec *iov, *siov; + unsigned char *buf; + size_t sz; + + iov_random(&iov, &niov); + sz = iov_size(iov, niov); + buf = g_malloc(sz); + for (i = 0; i < sz; ++i) { + buf[i] = i & 255; + } + iov_from_buf(iov, niov, 0, buf, sz); + + siov = g_memdup(iov, sizeof(*iov) * niov); + + if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) < 0) { + perror("socketpair"); + exit(1); + } + + FD_ZERO(&fds); + + t = 0; + if (fork() == 0) { + /* writer */ + + close(sv[0]); + FD_SET(sv[1], &fds); + fcntl(sv[1], F_SETFL, O_RDWR|O_NONBLOCK); + r = g_test_rand_int_range(sz / 2, sz); + setsockopt(sv[1], SOL_SOCKET, SO_SNDBUF, &r, sizeof(r)); + + for (i = 0; i <= sz; ++i) { + for (j = i; j <= sz; ++j) { + k = i; + do { + s = g_test_rand_int_range(0, j - k + 1); + r = iov_send(sv[1], iov, niov, k, s); + g_assert(memcmp(iov, siov, sizeof(*iov)*niov) == 0); + if (r >= 0) { + k += r; + t += r; + usleep(g_test_rand_int_range(0, 30)); + } else if (errno == EAGAIN) { + select(sv[1]+1, NULL, &fds, NULL, NULL); + continue; + } else { + perror("send"); + exit(1); + } + } while(k < j); + } + } + iov_free(iov, niov); + g_free(buf); + g_free(siov); + exit(0); + + } else { + /* reader & verifier */ + + close(sv[1]); + FD_SET(sv[0], &fds); + fcntl(sv[0], F_SETFL, O_RDWR|O_NONBLOCK); + r = g_test_rand_int_range(sz / 2, sz); + setsockopt(sv[0], SOL_SOCKET, SO_RCVBUF, &r, sizeof(r)); + usleep(500000); + + for (i = 0; i <= sz; ++i) { + for (j = i; j <= sz; ++j) { + k = i; + iov_memset(iov, niov, 0, 0xff, sz); + do { + s = g_test_rand_int_range(0, j - k + 1); + r = iov_recv(sv[0], iov, niov, k, s); + g_assert(memcmp(iov, siov, sizeof(*iov)*niov) == 0); + if (r > 0) { + k += r; + t += r; + } else if (!r) { + if (s) { + break; + } + } else if (errno == EAGAIN) { + select(sv[0]+1, &fds, NULL, NULL, NULL); + continue; + } else { + perror("recv"); + exit(1); + } + } while(k < j); + test_iov_bytes(iov, niov, i, j - i); + } + } + + iov_free(iov, niov); + g_free(buf); + g_free(siov); + } +#endif +} + +static void test_discard_front(void) +{ + struct iovec *iov; + struct iovec *iov_tmp; + unsigned int iov_cnt; + unsigned int iov_cnt_tmp; + void *old_base; + size_t size; + size_t ret; + + /* Discard zero bytes */ + iov_random(&iov, &iov_cnt); + iov_tmp = iov; + iov_cnt_tmp = iov_cnt; + ret = iov_discard_front(&iov_tmp, &iov_cnt_tmp, 0); + g_assert(ret == 0); + g_assert(iov_tmp == iov); + g_assert(iov_cnt_tmp == iov_cnt); + iov_free(iov, iov_cnt); + + /* Discard more bytes than vector size */ + iov_random(&iov, &iov_cnt); + iov_tmp = iov; + iov_cnt_tmp = iov_cnt; + size = iov_size(iov, iov_cnt); + ret = iov_discard_front(&iov_tmp, &iov_cnt_tmp, size + 1); + g_assert(ret == size); + g_assert(iov_cnt_tmp == 0); + iov_free(iov, iov_cnt); + + /* Discard entire vector */ + iov_random(&iov, &iov_cnt); + iov_tmp = iov; + iov_cnt_tmp = iov_cnt; + size = iov_size(iov, iov_cnt); + ret = iov_discard_front(&iov_tmp, &iov_cnt_tmp, size); + g_assert(ret == size); + g_assert(iov_cnt_tmp == 0); + iov_free(iov, iov_cnt); + + /* Discard within first element */ + iov_random(&iov, &iov_cnt); + iov_tmp = iov; + iov_cnt_tmp = iov_cnt; + old_base = iov->iov_base; + size = g_test_rand_int_range(1, iov->iov_len); + ret = iov_discard_front(&iov_tmp, &iov_cnt_tmp, size); + g_assert(ret == size); + g_assert(iov_tmp == iov); + g_assert(iov_cnt_tmp == iov_cnt); + g_assert(iov_tmp->iov_base == old_base + size); + iov_tmp->iov_base = old_base; /* undo before g_free() */ + iov_free(iov, iov_cnt); + + /* Discard entire first element */ + iov_random(&iov, &iov_cnt); + iov_tmp = iov; + iov_cnt_tmp = iov_cnt; + ret = iov_discard_front(&iov_tmp, &iov_cnt_tmp, iov->iov_len); + g_assert(ret == iov->iov_len); + g_assert(iov_tmp == iov + 1); + g_assert(iov_cnt_tmp == iov_cnt - 1); + iov_free(iov, iov_cnt); + + /* Discard within second element */ + iov_random(&iov, &iov_cnt); + iov_tmp = iov; + iov_cnt_tmp = iov_cnt; + old_base = iov[1].iov_base; + size = iov->iov_len + g_test_rand_int_range(1, iov[1].iov_len); + ret = iov_discard_front(&iov_tmp, &iov_cnt_tmp, size); + g_assert(ret == size); + g_assert(iov_tmp == iov + 1); + g_assert(iov_cnt_tmp == iov_cnt - 1); + g_assert(iov_tmp->iov_base == old_base + (size - iov->iov_len)); + iov_tmp->iov_base = old_base; /* undo before g_free() */ + iov_free(iov, iov_cnt); +} + +static void test_discard_front_undo(void) +{ + IOVDiscardUndo undo; + struct iovec *iov; + struct iovec *iov_tmp; + struct iovec *iov_orig; + unsigned int iov_cnt; + unsigned int iov_cnt_tmp; + size_t size; + + /* Discard zero bytes */ + iov_random(&iov, &iov_cnt); + iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt); + iov_tmp = iov; + iov_cnt_tmp = iov_cnt; + iov_discard_front_undoable(&iov_tmp, &iov_cnt_tmp, 0, &undo); + iov_discard_undo(&undo); + assert(iov_equals(iov, iov_orig, iov_cnt)); + g_free(iov_orig); + iov_free(iov, iov_cnt); + + /* Discard more bytes than vector size */ + iov_random(&iov, &iov_cnt); + iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt); + iov_tmp = iov; + iov_cnt_tmp = iov_cnt; + size = iov_size(iov, iov_cnt); + iov_discard_front_undoable(&iov_tmp, &iov_cnt_tmp, size + 1, &undo); + iov_discard_undo(&undo); + assert(iov_equals(iov, iov_orig, iov_cnt)); + g_free(iov_orig); + iov_free(iov, iov_cnt); + + /* Discard entire vector */ + iov_random(&iov, &iov_cnt); + iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt); + iov_tmp = iov; + iov_cnt_tmp = iov_cnt; + size = iov_size(iov, iov_cnt); + iov_discard_front_undoable(&iov_tmp, &iov_cnt_tmp, size, &undo); + iov_discard_undo(&undo); + assert(iov_equals(iov, iov_orig, iov_cnt)); + g_free(iov_orig); + iov_free(iov, iov_cnt); + + /* Discard within first element */ + iov_random(&iov, &iov_cnt); + iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt); + iov_tmp = iov; + iov_cnt_tmp = iov_cnt; + size = g_test_rand_int_range(1, iov->iov_len); + iov_discard_front_undoable(&iov_tmp, &iov_cnt_tmp, size, &undo); + iov_discard_undo(&undo); + assert(iov_equals(iov, iov_orig, iov_cnt)); + g_free(iov_orig); + iov_free(iov, iov_cnt); + + /* Discard entire first element */ + iov_random(&iov, &iov_cnt); + iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt); + iov_tmp = iov; + iov_cnt_tmp = iov_cnt; + iov_discard_front_undoable(&iov_tmp, &iov_cnt_tmp, iov->iov_len, &undo); + iov_discard_undo(&undo); + assert(iov_equals(iov, iov_orig, iov_cnt)); + g_free(iov_orig); + iov_free(iov, iov_cnt); + + /* Discard within second element */ + iov_random(&iov, &iov_cnt); + iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt); + iov_tmp = iov; + iov_cnt_tmp = iov_cnt; + size = iov->iov_len + g_test_rand_int_range(1, iov[1].iov_len); + iov_discard_front_undoable(&iov_tmp, &iov_cnt_tmp, size, &undo); + iov_discard_undo(&undo); + assert(iov_equals(iov, iov_orig, iov_cnt)); + g_free(iov_orig); + iov_free(iov, iov_cnt); +} + +static void test_discard_back(void) +{ + struct iovec *iov; + unsigned int iov_cnt; + unsigned int iov_cnt_tmp; + void *old_base; + size_t size; + size_t ret; + + /* Discard zero bytes */ + iov_random(&iov, &iov_cnt); + iov_cnt_tmp = iov_cnt; + ret = iov_discard_back(iov, &iov_cnt_tmp, 0); + g_assert(ret == 0); + g_assert(iov_cnt_tmp == iov_cnt); + iov_free(iov, iov_cnt); + + /* Discard more bytes than vector size */ + iov_random(&iov, &iov_cnt); + iov_cnt_tmp = iov_cnt; + size = iov_size(iov, iov_cnt); + ret = iov_discard_back(iov, &iov_cnt_tmp, size + 1); + g_assert(ret == size); + g_assert(iov_cnt_tmp == 0); + iov_free(iov, iov_cnt); + + /* Discard entire vector */ + iov_random(&iov, &iov_cnt); + iov_cnt_tmp = iov_cnt; + size = iov_size(iov, iov_cnt); + ret = iov_discard_back(iov, &iov_cnt_tmp, size); + g_assert(ret == size); + g_assert(iov_cnt_tmp == 0); + iov_free(iov, iov_cnt); + + /* Discard within last element */ + iov_random(&iov, &iov_cnt); + iov_cnt_tmp = iov_cnt; + old_base = iov[iov_cnt - 1].iov_base; + size = g_test_rand_int_range(1, iov[iov_cnt - 1].iov_len); + ret = iov_discard_back(iov, &iov_cnt_tmp, size); + g_assert(ret == size); + g_assert(iov_cnt_tmp == iov_cnt); + g_assert(iov[iov_cnt - 1].iov_base == old_base); + iov_free(iov, iov_cnt); + + /* Discard entire last element */ + iov_random(&iov, &iov_cnt); + iov_cnt_tmp = iov_cnt; + old_base = iov[iov_cnt - 1].iov_base; + size = iov[iov_cnt - 1].iov_len; + ret = iov_discard_back(iov, &iov_cnt_tmp, size); + g_assert(ret == size); + g_assert(iov_cnt_tmp == iov_cnt - 1); + iov_free(iov, iov_cnt); + + /* Discard within second-to-last element */ + iov_random(&iov, &iov_cnt); + iov_cnt_tmp = iov_cnt; + old_base = iov[iov_cnt - 2].iov_base; + size = iov[iov_cnt - 1].iov_len + + g_test_rand_int_range(1, iov[iov_cnt - 2].iov_len); + ret = iov_discard_back(iov, &iov_cnt_tmp, size); + g_assert(ret == size); + g_assert(iov_cnt_tmp == iov_cnt - 1); + g_assert(iov[iov_cnt - 2].iov_base == old_base); + iov_free(iov, iov_cnt); +} + +static void test_discard_back_undo(void) +{ + IOVDiscardUndo undo; + struct iovec *iov; + struct iovec *iov_orig; + unsigned int iov_cnt; + unsigned int iov_cnt_tmp; + size_t size; + + /* Discard zero bytes */ + iov_random(&iov, &iov_cnt); + iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt); + iov_cnt_tmp = iov_cnt; + iov_discard_back_undoable(iov, &iov_cnt_tmp, 0, &undo); + iov_discard_undo(&undo); + assert(iov_equals(iov, iov_orig, iov_cnt)); + g_free(iov_orig); + iov_free(iov, iov_cnt); + + /* Discard more bytes than vector size */ + iov_random(&iov, &iov_cnt); + iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt); + iov_cnt_tmp = iov_cnt; + size = iov_size(iov, iov_cnt); + iov_discard_back_undoable(iov, &iov_cnt_tmp, size + 1, &undo); + iov_discard_undo(&undo); + assert(iov_equals(iov, iov_orig, iov_cnt)); + g_free(iov_orig); + iov_free(iov, iov_cnt); + + /* Discard entire vector */ + iov_random(&iov, &iov_cnt); + iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt); + iov_cnt_tmp = iov_cnt; + size = iov_size(iov, iov_cnt); + iov_discard_back_undoable(iov, &iov_cnt_tmp, size, &undo); + iov_discard_undo(&undo); + assert(iov_equals(iov, iov_orig, iov_cnt)); + g_free(iov_orig); + iov_free(iov, iov_cnt); + + /* Discard within last element */ + iov_random(&iov, &iov_cnt); + iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt); + iov_cnt_tmp = iov_cnt; + size = g_test_rand_int_range(1, iov[iov_cnt - 1].iov_len); + iov_discard_back_undoable(iov, &iov_cnt_tmp, size, &undo); + iov_discard_undo(&undo); + assert(iov_equals(iov, iov_orig, iov_cnt)); + g_free(iov_orig); + iov_free(iov, iov_cnt); + + /* Discard entire last element */ + iov_random(&iov, &iov_cnt); + iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt); + iov_cnt_tmp = iov_cnt; + size = iov[iov_cnt - 1].iov_len; + iov_discard_back_undoable(iov, &iov_cnt_tmp, size, &undo); + iov_discard_undo(&undo); + assert(iov_equals(iov, iov_orig, iov_cnt)); + g_free(iov_orig); + iov_free(iov, iov_cnt); + + /* Discard within second-to-last element */ + iov_random(&iov, &iov_cnt); + iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt); + iov_cnt_tmp = iov_cnt; + size = iov[iov_cnt - 1].iov_len + + g_test_rand_int_range(1, iov[iov_cnt - 2].iov_len); + iov_discard_back_undoable(iov, &iov_cnt_tmp, size, &undo); + iov_discard_undo(&undo); + assert(iov_equals(iov, iov_orig, iov_cnt)); + g_free(iov_orig); + iov_free(iov, iov_cnt); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + g_test_rand_int(); + g_test_add_func("/basic/iov/from-to-buf", test_to_from_buf); + g_test_add_func("/basic/iov/io", test_io); + g_test_add_func("/basic/iov/discard-front", test_discard_front); + g_test_add_func("/basic/iov/discard-back", test_discard_back); + g_test_add_func("/basic/iov/discard-front-undo", test_discard_front_undo); + g_test_add_func("/basic/iov/discard-back-undo", test_discard_back_undo); + return g_test_run(); +} diff --git a/tests/unit/test-keyval.c b/tests/unit/test-keyval.c new file mode 100644 index 0000000000..ee927fe4e4 --- /dev/null +++ b/tests/unit/test-keyval.c @@ -0,0 +1,750 @@ +/* + * Unit tests for parsing of KEY=VALUE,... strings + * + * Copyright (C) 2017 Red Hat Inc. + * + * Authors: + * Markus Armbruster , + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "qapi/error.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qmp/qlist.h" +#include "qapi/qmp/qstring.h" +#include "qapi/qobject-input-visitor.h" +#include "test-qapi-visit.h" +#include "qemu/cutils.h" +#include "qemu/option.h" + +static void test_keyval_parse(void) +{ + Error *err = NULL; + QDict *qdict, *sub_qdict; + char long_key[129]; + char *params; + bool help; + + /* Nothing */ + qdict = keyval_parse("", NULL, NULL, &error_abort); + g_assert_cmpuint(qdict_size(qdict), ==, 0); + qobject_unref(qdict); + + /* Empty key (qemu_opts_parse() accepts this) */ + qdict = keyval_parse("=val", NULL, NULL, &err); + error_free_or_abort(&err); + g_assert(!qdict); + + /* Empty key fragment */ + qdict = keyval_parse(".", NULL, NULL, &err); + error_free_or_abort(&err); + g_assert(!qdict); + qdict = keyval_parse("key.", NULL, NULL, &err); + error_free_or_abort(&err); + g_assert(!qdict); + + /* Invalid non-empty key (qemu_opts_parse() doesn't care) */ + qdict = keyval_parse("7up=val", NULL, NULL, &err); + error_free_or_abort(&err); + g_assert(!qdict); + + /* Overlong key */ + memset(long_key, 'a', 127); + long_key[127] = 'z'; + long_key[128] = 0; + params = g_strdup_printf("k.%s=v", long_key); + qdict = keyval_parse(params + 2, NULL, NULL, &err); + error_free_or_abort(&err); + g_assert(!qdict); + + /* Overlong key fragment */ + qdict = keyval_parse(params, NULL, NULL, &err); + error_free_or_abort(&err); + g_assert(!qdict); + g_free(params); + + /* Long key (qemu_opts_parse() accepts and truncates silently) */ + params = g_strdup_printf("k.%s=v", long_key + 1); + qdict = keyval_parse(params + 2, NULL, NULL, &error_abort); + g_assert_cmpuint(qdict_size(qdict), ==, 1); + g_assert_cmpstr(qdict_get_try_str(qdict, long_key + 1), ==, "v"); + qobject_unref(qdict); + + /* Long key fragment */ + qdict = keyval_parse(params, NULL, NULL, &error_abort); + g_assert_cmpuint(qdict_size(qdict), ==, 1); + sub_qdict = qdict_get_qdict(qdict, "k"); + g_assert(sub_qdict); + g_assert_cmpuint(qdict_size(sub_qdict), ==, 1); + g_assert_cmpstr(qdict_get_try_str(sub_qdict, long_key + 1), ==, "v"); + qobject_unref(qdict); + g_free(params); + + /* Crap after valid key */ + qdict = keyval_parse("key[0]=val", NULL, NULL, &err); + error_free_or_abort(&err); + g_assert(!qdict); + + /* Multiple keys, last one wins */ + qdict = keyval_parse("a=1,b=2,,x,a=3", NULL, NULL, &error_abort); + g_assert_cmpuint(qdict_size(qdict), ==, 2); + g_assert_cmpstr(qdict_get_try_str(qdict, "a"), ==, "3"); + g_assert_cmpstr(qdict_get_try_str(qdict, "b"), ==, "2,x"); + qobject_unref(qdict); + + /* Even when it doesn't in qemu_opts_parse() */ + qdict = keyval_parse("id=foo,id=bar", NULL, NULL, &error_abort); + g_assert_cmpuint(qdict_size(qdict), ==, 1); + g_assert_cmpstr(qdict_get_try_str(qdict, "id"), ==, "bar"); + qobject_unref(qdict); + + /* Dotted keys */ + qdict = keyval_parse("a.b.c=1,a.b.c=2,d=3", NULL, NULL, &error_abort); + g_assert_cmpuint(qdict_size(qdict), ==, 2); + sub_qdict = qdict_get_qdict(qdict, "a"); + g_assert(sub_qdict); + g_assert_cmpuint(qdict_size(sub_qdict), ==, 1); + sub_qdict = qdict_get_qdict(sub_qdict, "b"); + g_assert(sub_qdict); + g_assert_cmpuint(qdict_size(sub_qdict), ==, 1); + g_assert_cmpstr(qdict_get_try_str(sub_qdict, "c"), ==, "2"); + g_assert_cmpstr(qdict_get_try_str(qdict, "d"), ==, "3"); + qobject_unref(qdict); + + /* Inconsistent dotted keys */ + qdict = keyval_parse("a.b=1,a=2", NULL, NULL, &err); + error_free_or_abort(&err); + g_assert(!qdict); + qdict = keyval_parse("a.b=1,a.b.c=2", NULL, NULL, &err); + error_free_or_abort(&err); + g_assert(!qdict); + + /* Trailing comma is ignored */ + qdict = keyval_parse("x=y,", NULL, NULL, &error_abort); + g_assert_cmpuint(qdict_size(qdict), ==, 1); + g_assert_cmpstr(qdict_get_try_str(qdict, "x"), ==, "y"); + qobject_unref(qdict); + + /* Except when it isn't */ + qdict = keyval_parse(",", NULL, NULL, &err); + error_free_or_abort(&err); + g_assert(!qdict); + + /* Value containing ,id= not misinterpreted as qemu_opts_parse() does */ + qdict = keyval_parse("x=,,id=bar", NULL, NULL, &error_abort); + g_assert_cmpuint(qdict_size(qdict), ==, 1); + g_assert_cmpstr(qdict_get_try_str(qdict, "x"), ==, ",id=bar"); + qobject_unref(qdict); + + /* Anti-social ID is left to caller (qemu_opts_parse() rejects it) */ + qdict = keyval_parse("id=666", NULL, NULL, &error_abort); + g_assert_cmpuint(qdict_size(qdict), ==, 1); + g_assert_cmpstr(qdict_get_try_str(qdict, "id"), ==, "666"); + qobject_unref(qdict); + + /* Implied value not supported (unlike qemu_opts_parse()) */ + qdict = keyval_parse("an,noaus,noaus=", NULL, NULL, &err); + error_free_or_abort(&err); + g_assert(!qdict); + + /* Implied value, key "no" (qemu_opts_parse(): negated empty key) */ + qdict = keyval_parse("no", NULL, NULL, &err); + error_free_or_abort(&err); + g_assert(!qdict); + + /* Implied key */ + qdict = keyval_parse("an,aus=off,noaus=", "implied", NULL, &error_abort); + g_assert_cmpuint(qdict_size(qdict), ==, 3); + g_assert_cmpstr(qdict_get_try_str(qdict, "implied"), ==, "an"); + g_assert_cmpstr(qdict_get_try_str(qdict, "aus"), ==, "off"); + g_assert_cmpstr(qdict_get_try_str(qdict, "noaus"), ==, ""); + qobject_unref(qdict); + + /* Implied dotted key */ + qdict = keyval_parse("val", "eins.zwei", NULL, &error_abort); + g_assert_cmpuint(qdict_size(qdict), ==, 1); + sub_qdict = qdict_get_qdict(qdict, "eins"); + g_assert(sub_qdict); + g_assert_cmpuint(qdict_size(sub_qdict), ==, 1); + g_assert_cmpstr(qdict_get_try_str(sub_qdict, "zwei"), ==, "val"); + qobject_unref(qdict); + + /* Implied key with empty value (qemu_opts_parse() accepts this) */ + qdict = keyval_parse(",", "implied", NULL, &err); + error_free_or_abort(&err); + g_assert(!qdict); + + /* Likewise (qemu_opts_parse(): implied key with comma value) */ + qdict = keyval_parse(",,,a=1", "implied", NULL, &err); + error_free_or_abort(&err); + g_assert(!qdict); + + /* Implied key's value can't have comma (qemu_opts_parse(): it can) */ + qdict = keyval_parse("val,,ue", "implied", NULL, &err); + error_free_or_abort(&err); + g_assert(!qdict); + + /* Empty key is not an implied key */ + qdict = keyval_parse("=val", "implied", NULL, &err); + error_free_or_abort(&err); + g_assert(!qdict); + + /* "help" by itself, without implied key */ + qdict = keyval_parse("help", NULL, &help, &error_abort); + g_assert_cmpuint(qdict_size(qdict), ==, 0); + g_assert(help); + qobject_unref(qdict); + + /* "help" by itself, with implied key */ + qdict = keyval_parse("help", "implied", &help, &error_abort); + g_assert_cmpuint(qdict_size(qdict), ==, 0); + g_assert(help); + qobject_unref(qdict); + + /* "help" when no help is available, without implied key */ + qdict = keyval_parse("help", NULL, NULL, &err); + error_free_or_abort(&err); + g_assert(!qdict); + + /* "help" when no help is available, with implied key */ + qdict = keyval_parse("help", "implied", NULL, &err); + error_free_or_abort(&err); + g_assert(!qdict); + + /* Key "help" */ + qdict = keyval_parse("help=on", NULL, &help, &error_abort); + g_assert_cmpuint(qdict_size(qdict), ==, 1); + g_assert_cmpstr(qdict_get_try_str(qdict, "help"), ==, "on"); + g_assert(!help); + qobject_unref(qdict); + + /* "help" followed by crap, without implied key */ + qdict = keyval_parse("help.abc", NULL, &help, &err); + error_free_or_abort(&err); + g_assert(!qdict); + + /* "help" followed by crap, with implied key */ + qdict = keyval_parse("help.abc", "implied", &help, &err); + g_assert_cmpuint(qdict_size(qdict), ==, 1); + g_assert_cmpstr(qdict_get_try_str(qdict, "implied"), ==, "help.abc"); + g_assert(!help); + qobject_unref(qdict); + + /* "help" with other stuff, without implied key */ + qdict = keyval_parse("number=42,help,foo=bar", NULL, &help, &error_abort); + g_assert_cmpuint(qdict_size(qdict), ==, 2); + g_assert_cmpstr(qdict_get_try_str(qdict, "number"), ==, "42"); + g_assert_cmpstr(qdict_get_try_str(qdict, "foo"), ==, "bar"); + g_assert(help); + qobject_unref(qdict); + + /* "help" with other stuff, with implied key */ + qdict = keyval_parse("val,help,foo=bar", "implied", &help, &error_abort); + g_assert_cmpuint(qdict_size(qdict), ==, 2); + g_assert_cmpstr(qdict_get_try_str(qdict, "implied"), ==, "val"); + g_assert_cmpstr(qdict_get_try_str(qdict, "foo"), ==, "bar"); + g_assert(help); + qobject_unref(qdict); +} + +static void check_list012(QList *qlist) +{ + static const char *expected[] = { "null", "eins", "zwei" }; + int i; + QString *qstr; + + g_assert(qlist); + for (i = 0; i < ARRAY_SIZE(expected); i++) { + qstr = qobject_to(QString, qlist_pop(qlist)); + g_assert(qstr); + g_assert_cmpstr(qstring_get_str(qstr), ==, expected[i]); + qobject_unref(qstr); + } + g_assert(qlist_empty(qlist)); +} + +static void test_keyval_parse_list(void) +{ + Error *err = NULL; + QDict *qdict, *sub_qdict; + + /* Root can't be a list */ + qdict = keyval_parse("0=1", NULL, NULL, &err); + error_free_or_abort(&err); + g_assert(!qdict); + + /* List elements need not be in order */ + qdict = keyval_parse("list.0=null,list.2=zwei,list.1=eins", NULL, NULL, + &error_abort); + g_assert_cmpint(qdict_size(qdict), ==, 1); + check_list012(qdict_get_qlist(qdict, "list")); + qobject_unref(qdict); + + /* Multiple indexes, last one wins */ + qdict = keyval_parse("list.1=goner,list.0=null,list.01=eins,list.2=zwei", + NULL, NULL, &error_abort); + g_assert_cmpint(qdict_size(qdict), ==, 1); + check_list012(qdict_get_qlist(qdict, "list")); + qobject_unref(qdict); + + /* List at deeper nesting */ + qdict = keyval_parse("a.list.1=eins,a.list.00=null,a.list.2=zwei", NULL, + NULL, &error_abort); + g_assert_cmpint(qdict_size(qdict), ==, 1); + sub_qdict = qdict_get_qdict(qdict, "a"); + g_assert_cmpint(qdict_size(sub_qdict), ==, 1); + check_list012(qdict_get_qlist(sub_qdict, "list")); + qobject_unref(qdict); + + /* Inconsistent dotted keys: both list and dictionary */ + qdict = keyval_parse("a.b.c=1,a.b.0=2", NULL, NULL, &err); + error_free_or_abort(&err); + g_assert(!qdict); + qdict = keyval_parse("a.0.c=1,a.b.c=2", NULL, NULL, &err); + error_free_or_abort(&err); + g_assert(!qdict); + + /* Missing list indexes */ + qdict = keyval_parse("list.1=lonely", NULL, NULL, &err); + error_free_or_abort(&err); + g_assert(!qdict); + qdict = keyval_parse("list.0=null,list.2=eins,list.02=zwei", NULL, NULL, + &err); + error_free_or_abort(&err); + g_assert(!qdict); +} + +static void test_keyval_visit_bool(void) +{ + Error *err = NULL; + Visitor *v; + QDict *qdict; + bool b; + + qdict = keyval_parse("bool1=on,bool2=off", NULL, NULL, &error_abort); + v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); + qobject_unref(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_type_bool(v, "bool1", &b, &error_abort); + g_assert(b); + visit_type_bool(v, "bool2", &b, &error_abort); + g_assert(!b); + visit_check_struct(v, &error_abort); + visit_end_struct(v, NULL); + visit_free(v); + + qdict = keyval_parse("bool1=offer", NULL, NULL, &error_abort); + v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); + qobject_unref(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_type_bool(v, "bool1", &b, &err); + error_free_or_abort(&err); + visit_end_struct(v, NULL); + visit_free(v); +} + +static void test_keyval_visit_number(void) +{ + Error *err = NULL; + Visitor *v; + QDict *qdict; + uint64_t u; + + /* Lower limit zero */ + qdict = keyval_parse("number1=0", NULL, NULL, &error_abort); + v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); + qobject_unref(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_type_uint64(v, "number1", &u, &error_abort); + g_assert_cmpuint(u, ==, 0); + visit_check_struct(v, &error_abort); + visit_end_struct(v, NULL); + visit_free(v); + + /* Upper limit 2^64-1 */ + qdict = keyval_parse("number1=18446744073709551615,number2=-1", NULL, + NULL, &error_abort); + v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); + qobject_unref(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_type_uint64(v, "number1", &u, &error_abort); + g_assert_cmphex(u, ==, UINT64_MAX); + visit_type_uint64(v, "number2", &u, &error_abort); + g_assert_cmphex(u, ==, UINT64_MAX); + visit_check_struct(v, &error_abort); + visit_end_struct(v, NULL); + visit_free(v); + + /* Above upper limit */ + qdict = keyval_parse("number1=18446744073709551616", NULL, NULL, + &error_abort); + v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); + qobject_unref(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_type_uint64(v, "number1", &u, &err); + error_free_or_abort(&err); + visit_end_struct(v, NULL); + visit_free(v); + + /* Below lower limit */ + qdict = keyval_parse("number1=-18446744073709551616", NULL, NULL, + &error_abort); + v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); + qobject_unref(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_type_uint64(v, "number1", &u, &err); + error_free_or_abort(&err); + visit_end_struct(v, NULL); + visit_free(v); + + /* Hex and octal */ + qdict = keyval_parse("number1=0x2a,number2=052", NULL, NULL, &error_abort); + v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); + qobject_unref(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_type_uint64(v, "number1", &u, &error_abort); + g_assert_cmpuint(u, ==, 42); + visit_type_uint64(v, "number2", &u, &error_abort); + g_assert_cmpuint(u, ==, 42); + visit_check_struct(v, &error_abort); + visit_end_struct(v, NULL); + visit_free(v); + + /* Trailing crap */ + qdict = keyval_parse("number1=3.14,number2=08", NULL, NULL, &error_abort); + v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); + qobject_unref(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_type_uint64(v, "number1", &u, &err); + error_free_or_abort(&err); + visit_type_uint64(v, "number2", &u, &err); + error_free_or_abort(&err); + visit_end_struct(v, NULL); + visit_free(v); +} + +static void test_keyval_visit_size(void) +{ + Error *err = NULL; + Visitor *v; + QDict *qdict; + uint64_t sz; + + /* Lower limit zero */ + qdict = keyval_parse("sz1=0", NULL, NULL, &error_abort); + v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); + qobject_unref(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_type_size(v, "sz1", &sz, &error_abort); + g_assert_cmpuint(sz, ==, 0); + visit_check_struct(v, &error_abort); + visit_end_struct(v, NULL); + visit_free(v); + + /* Note: precision is 53 bits since we're parsing with strtod() */ + + /* Around limit of precision: 2^53-1, 2^53, 2^53+1 */ + qdict = keyval_parse("sz1=9007199254740991," + "sz2=9007199254740992," + "sz3=9007199254740993", + NULL, NULL, &error_abort); + v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); + qobject_unref(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_type_size(v, "sz1", &sz, &error_abort); + g_assert_cmphex(sz, ==, 0x1fffffffffffff); + visit_type_size(v, "sz2", &sz, &error_abort); + g_assert_cmphex(sz, ==, 0x20000000000000); + visit_type_size(v, "sz3", &sz, &error_abort); + g_assert_cmphex(sz, ==, 0x20000000000000); + visit_check_struct(v, &error_abort); + visit_end_struct(v, NULL); + visit_free(v); + + /* Close to signed upper limit 0x7ffffffffffffc00 (53 msbs set) */ + qdict = keyval_parse("sz1=9223372036854774784," /* 7ffffffffffffc00 */ + "sz2=9223372036854775295", /* 7ffffffffffffdff */ + NULL, NULL, &error_abort); + v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); + qobject_unref(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_type_size(v, "sz1", &sz, &error_abort); + g_assert_cmphex(sz, ==, 0x7ffffffffffffc00); + visit_type_size(v, "sz2", &sz, &error_abort); + g_assert_cmphex(sz, ==, 0x7ffffffffffffc00); + visit_check_struct(v, &error_abort); + visit_end_struct(v, NULL); + visit_free(v); + + /* Close to actual upper limit 0xfffffffffffff800 (53 msbs set) */ + qdict = keyval_parse("sz1=18446744073709549568," /* fffffffffffff800 */ + "sz2=18446744073709550591", /* fffffffffffffbff */ + NULL, NULL, &error_abort); + v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); + qobject_unref(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_type_size(v, "sz1", &sz, &error_abort); + g_assert_cmphex(sz, ==, 0xfffffffffffff800); + visit_type_size(v, "sz2", &sz, &error_abort); + g_assert_cmphex(sz, ==, 0xfffffffffffff800); + visit_check_struct(v, &error_abort); + visit_end_struct(v, NULL); + visit_free(v); + + /* Beyond limits */ + qdict = keyval_parse("sz1=-1," + "sz2=18446744073709550592", /* fffffffffffffc00 */ + NULL, NULL, &error_abort); + v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); + qobject_unref(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_type_size(v, "sz1", &sz, &err); + error_free_or_abort(&err); + visit_type_size(v, "sz2", &sz, &err); + error_free_or_abort(&err); + visit_end_struct(v, NULL); + visit_free(v); + + /* Suffixes */ + qdict = keyval_parse("sz1=8b,sz2=1.5k,sz3=2M,sz4=0.1G,sz5=16777215T", + NULL, NULL, &error_abort); + v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); + qobject_unref(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_type_size(v, "sz1", &sz, &error_abort); + g_assert_cmpuint(sz, ==, 8); + visit_type_size(v, "sz2", &sz, &error_abort); + g_assert_cmpuint(sz, ==, 1536); + visit_type_size(v, "sz3", &sz, &error_abort); + g_assert_cmphex(sz, ==, 2 * MiB); + visit_type_size(v, "sz4", &sz, &error_abort); + g_assert_cmphex(sz, ==, GiB / 10); + visit_type_size(v, "sz5", &sz, &error_abort); + g_assert_cmphex(sz, ==, 16777215ULL * TiB); + visit_check_struct(v, &error_abort); + visit_end_struct(v, NULL); + visit_free(v); + + /* Beyond limit with suffix */ + qdict = keyval_parse("sz1=16777216T", NULL, NULL, &error_abort); + v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); + qobject_unref(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_type_size(v, "sz1", &sz, &err); + error_free_or_abort(&err); + visit_end_struct(v, NULL); + visit_free(v); + + /* Trailing crap */ + qdict = keyval_parse("sz1=0Z,sz2=16Gi", NULL, NULL, &error_abort); + v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); + qobject_unref(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_type_size(v, "sz1", &sz, &err); + error_free_or_abort(&err); + visit_type_size(v, "sz2", &sz, &err); + error_free_or_abort(&err); + visit_end_struct(v, NULL); + visit_free(v); +} + +static void test_keyval_visit_dict(void) +{ + Error *err = NULL; + Visitor *v; + QDict *qdict; + int64_t i; + + qdict = keyval_parse("a.b.c=1,a.b.c=2,d=3", NULL, NULL, &error_abort); + v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); + qobject_unref(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_start_struct(v, "a", NULL, 0, &error_abort); + visit_start_struct(v, "b", NULL, 0, &error_abort); + visit_type_int(v, "c", &i, &error_abort); + g_assert_cmpint(i, ==, 2); + visit_check_struct(v, &error_abort); + visit_end_struct(v, NULL); + visit_check_struct(v, &error_abort); + visit_end_struct(v, NULL); + visit_type_int(v, "d", &i, &error_abort); + g_assert_cmpint(i, ==, 3); + visit_check_struct(v, &error_abort); + visit_end_struct(v, NULL); + visit_free(v); + + qdict = keyval_parse("a.b=", NULL, NULL, &error_abort); + v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); + qobject_unref(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_start_struct(v, "a", NULL, 0, &error_abort); + visit_type_int(v, "c", &i, &err); /* a.c missing */ + error_free_or_abort(&err); + visit_check_struct(v, &err); + error_free_or_abort(&err); /* a.b unexpected */ + visit_end_struct(v, NULL); + visit_check_struct(v, &error_abort); + visit_end_struct(v, NULL); + visit_free(v); +} + +static void test_keyval_visit_list(void) +{ + Error *err = NULL; + Visitor *v; + QDict *qdict; + char *s; + + qdict = keyval_parse("a.0=,a.1=I,a.2.0=II", NULL, NULL, &error_abort); + /* TODO empty list */ + v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); + qobject_unref(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_start_list(v, "a", NULL, 0, &error_abort); + visit_type_str(v, NULL, &s, &error_abort); + g_assert_cmpstr(s, ==, ""); + g_free(s); + visit_type_str(v, NULL, &s, &error_abort); + g_assert_cmpstr(s, ==, "I"); + g_free(s); + visit_start_list(v, NULL, NULL, 0, &error_abort); + visit_type_str(v, NULL, &s, &error_abort); + g_assert_cmpstr(s, ==, "II"); + g_free(s); + visit_check_list(v, &error_abort); + visit_end_list(v, NULL); + visit_check_list(v, &error_abort); + visit_end_list(v, NULL); + visit_check_struct(v, &error_abort); + visit_end_struct(v, NULL); + visit_free(v); + + qdict = keyval_parse("a.0=,b.0.0=head", NULL, NULL, &error_abort); + v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); + qobject_unref(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_start_list(v, "a", NULL, 0, &error_abort); + visit_check_list(v, &err); /* a[0] unexpected */ + error_free_or_abort(&err); + visit_end_list(v, NULL); + visit_start_list(v, "b", NULL, 0, &error_abort); + visit_start_list(v, NULL, NULL, 0, &error_abort); + visit_type_str(v, NULL, &s, &error_abort); + g_assert_cmpstr(s, ==, "head"); + g_free(s); + visit_type_str(v, NULL, &s, &err); /* b[0][1] missing */ + error_free_or_abort(&err); + visit_end_list(v, NULL); + visit_end_list(v, NULL); + visit_check_struct(v, &error_abort); + visit_end_struct(v, NULL); + visit_free(v); +} + +static void test_keyval_visit_optional(void) +{ + Visitor *v; + QDict *qdict; + bool present; + int64_t i; + + qdict = keyval_parse("a.b=1", NULL, NULL, &error_abort); + v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); + qobject_unref(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_optional(v, "b", &present); + g_assert(!present); /* b missing */ + visit_optional(v, "a", &present); + g_assert(present); /* a present */ + visit_start_struct(v, "a", NULL, 0, &error_abort); + visit_optional(v, "b", &present); + g_assert(present); /* a.b present */ + visit_type_int(v, "b", &i, &error_abort); + g_assert_cmpint(i, ==, 1); + visit_optional(v, "a", &present); + g_assert(!present); /* a.a missing */ + visit_check_struct(v, &error_abort); + visit_end_struct(v, NULL); + visit_check_struct(v, &error_abort); + visit_end_struct(v, NULL); + visit_free(v); +} + +static void test_keyval_visit_alternate(void) +{ + Error *err = NULL; + Visitor *v; + QDict *qdict; + AltStrObj *aso; + AltNumEnum *ane; + AltEnumBool *aeb; + + /* + * Can't do scalar alternate variants other than string. You get + * the string variant if there is one, else an error. + * TODO make it work for unambiguous cases like AltEnumBool below + */ + qdict = keyval_parse("a=1,b=2,c=on", NULL, NULL, &error_abort); + v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); + qobject_unref(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_type_AltStrObj(v, "a", &aso, &error_abort); + g_assert_cmpint(aso->type, ==, QTYPE_QSTRING); + g_assert_cmpstr(aso->u.s, ==, "1"); + qapi_free_AltStrObj(aso); + visit_type_AltNumEnum(v, "b", &ane, &err); + error_free_or_abort(&err); + visit_type_AltEnumBool(v, "c", &aeb, &err); + error_free_or_abort(&err); + visit_end_struct(v, NULL); + visit_free(v); +} + +static void test_keyval_visit_any(void) +{ + Visitor *v; + QDict *qdict; + QObject *any; + QList *qlist; + QString *qstr; + + qdict = keyval_parse("a.0=null,a.1=1", NULL, NULL, &error_abort); + v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); + qobject_unref(qdict); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_type_any(v, "a", &any, &error_abort); + qlist = qobject_to(QList, any); + g_assert(qlist); + qstr = qobject_to(QString, qlist_pop(qlist)); + g_assert_cmpstr(qstring_get_str(qstr), ==, "null"); + qobject_unref(qstr); + qstr = qobject_to(QString, qlist_pop(qlist)); + g_assert_cmpstr(qstring_get_str(qstr), ==, "1"); + g_assert(qlist_empty(qlist)); + qobject_unref(qstr); + qobject_unref(any); + visit_check_struct(v, &error_abort); + visit_end_struct(v, NULL); + visit_free(v); +} + +int main(int argc, char *argv[]) +{ + g_test_init(&argc, &argv, NULL); + g_test_add_func("/keyval/keyval_parse", test_keyval_parse); + g_test_add_func("/keyval/keyval_parse/list", test_keyval_parse_list); + g_test_add_func("/keyval/visit/bool", test_keyval_visit_bool); + g_test_add_func("/keyval/visit/number", test_keyval_visit_number); + g_test_add_func("/keyval/visit/size", test_keyval_visit_size); + g_test_add_func("/keyval/visit/dict", test_keyval_visit_dict); + g_test_add_func("/keyval/visit/list", test_keyval_visit_list); + g_test_add_func("/keyval/visit/optional", test_keyval_visit_optional); + g_test_add_func("/keyval/visit/alternate", test_keyval_visit_alternate); + g_test_add_func("/keyval/visit/any", test_keyval_visit_any); + g_test_run(); + return 0; +} diff --git a/tests/unit/test-logging.c b/tests/unit/test-logging.c new file mode 100644 index 0000000000..ccb819f193 --- /dev/null +++ b/tests/unit/test-logging.c @@ -0,0 +1,218 @@ +/* + * logging unit-tests + * + * Copyright (C) 2016 Linaro Ltd. + * + * Author: Alex Bennée + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include + +#include "qemu-common.h" +#include "qapi/error.h" +#include "qemu/log.h" + +static void test_parse_range(void) +{ + Error *err = NULL; + + qemu_set_dfilter_ranges("0x1000+0x100", &error_abort); + + g_assert_false(qemu_log_in_addr_range(0xfff)); + g_assert(qemu_log_in_addr_range(0x1000)); + g_assert(qemu_log_in_addr_range(0x1001)); + g_assert(qemu_log_in_addr_range(0x10ff)); + g_assert_false(qemu_log_in_addr_range(0x1100)); + + qemu_set_dfilter_ranges("0x1000-0x100", &error_abort); + + g_assert_false(qemu_log_in_addr_range(0x1001)); + g_assert(qemu_log_in_addr_range(0x1000)); + g_assert(qemu_log_in_addr_range(0x0f01)); + g_assert_false(qemu_log_in_addr_range(0x0f00)); + + qemu_set_dfilter_ranges("0x1000..0x1100", &error_abort); + + g_assert_false(qemu_log_in_addr_range(0xfff)); + g_assert(qemu_log_in_addr_range(0x1000)); + g_assert(qemu_log_in_addr_range(0x1100)); + g_assert_false(qemu_log_in_addr_range(0x1101)); + + qemu_set_dfilter_ranges("0x1000..0x1000", &error_abort); + + g_assert_false(qemu_log_in_addr_range(0xfff)); + g_assert(qemu_log_in_addr_range(0x1000)); + g_assert_false(qemu_log_in_addr_range(0x1001)); + + qemu_set_dfilter_ranges("0x1000+0x100,0x2100-0x100,0x3000..0x3100", + &error_abort); + g_assert(qemu_log_in_addr_range(0x1050)); + g_assert(qemu_log_in_addr_range(0x2050)); + g_assert(qemu_log_in_addr_range(0x3050)); + + qemu_set_dfilter_ranges("0xffffffffffffffff-1", &error_abort); + g_assert(qemu_log_in_addr_range(UINT64_MAX)); + g_assert_false(qemu_log_in_addr_range(UINT64_MAX - 1)); + + qemu_set_dfilter_ranges("0..0xffffffffffffffff", &error_abort); + g_assert(qemu_log_in_addr_range(0)); + g_assert(qemu_log_in_addr_range(UINT64_MAX)); + + qemu_set_dfilter_ranges("2..1", &err); + error_free_or_abort(&err); + + qemu_set_dfilter_ranges("0x1000+onehundred", &err); + error_free_or_abort(&err); + + qemu_set_dfilter_ranges("0x1000+0", &err); + error_free_or_abort(&err); +} + +static void set_log_path_tmp(char const *dir, char const *tpl, Error **errp) +{ + gchar *file_path = g_build_filename(dir, tpl, NULL); + + qemu_set_log_filename(file_path, errp); + g_free(file_path); +} + +static void test_parse_path(gconstpointer data) +{ + gchar const *tmp_path = data; + Error *err = NULL; + + set_log_path_tmp(tmp_path, "qemu.log", &error_abort); + set_log_path_tmp(tmp_path, "qemu-%d.log", &error_abort); + set_log_path_tmp(tmp_path, "qemu.log.%d", &error_abort); + + set_log_path_tmp(tmp_path, "qemu-%d%d.log", &err); + error_free_or_abort(&err); +} + +static void test_logfile_write(gconstpointer data) +{ + QemuLogFile *logfile; + QemuLogFile *logfile2; + gchar const *dir = data; + g_autofree gchar *file_path = NULL; + g_autofree gchar *file_path1 = NULL; + FILE *orig_fd; + + /* + * Before starting test, set log flags, to ensure the file gets + * opened below with the call to qemu_set_log_filename(). + * In cases where a logging backend other than log is used, + * this is needed. + */ + qemu_set_log(CPU_LOG_TB_OUT_ASM); + file_path = g_build_filename(dir, "qemu_test_log_write0.log", NULL); + file_path1 = g_build_filename(dir, "qemu_test_log_write1.log", NULL); + + /* + * Test that even if an open file handle is changed, + * our handle remains valid due to RCU. + */ + qemu_set_log_filename(file_path, &error_abort); + rcu_read_lock(); + logfile = qatomic_rcu_read(&qemu_logfile); + orig_fd = logfile->fd; + g_assert(logfile && logfile->fd); + fprintf(logfile->fd, "%s 1st write to file\n", __func__); + fflush(logfile->fd); + + /* Change the logfile and ensure that the handle is still valid. */ + qemu_set_log_filename(file_path1, &error_abort); + logfile2 = qatomic_rcu_read(&qemu_logfile); + g_assert(logfile->fd == orig_fd); + g_assert(logfile2->fd != logfile->fd); + fprintf(logfile->fd, "%s 2nd write to file\n", __func__); + fflush(logfile->fd); + rcu_read_unlock(); +} + +static void test_logfile_lock(gconstpointer data) +{ + FILE *logfile; + gchar const *dir = data; + g_autofree gchar *file_path = NULL; + + file_path = g_build_filename(dir, "qemu_test_logfile_lock0.log", NULL); + + /* + * Test the use of the logfile lock, such + * that even if an open file handle is closed, + * our handle remains valid for use due to RCU. + */ + qemu_set_log_filename(file_path, &error_abort); + logfile = qemu_log_lock(); + g_assert(logfile); + fprintf(logfile, "%s 1st write to file\n", __func__); + fflush(logfile); + + /* + * Initiate a close file and make sure our handle remains + * valid since we still have the logfile lock. + */ + qemu_log_close(); + fprintf(logfile, "%s 2nd write to file\n", __func__); + fflush(logfile); + qemu_log_unlock(logfile); +} + +/* Remove a directory and all its entries (non-recursive). */ +static void rmdir_full(gchar const *root) +{ + GDir *root_gdir = g_dir_open(root, 0, NULL); + gchar const *entry_name; + + g_assert_nonnull(root_gdir); + while ((entry_name = g_dir_read_name(root_gdir)) != NULL) { + gchar *entry_path = g_build_filename(root, entry_name, NULL); + g_assert(g_remove(entry_path) == 0); + g_free(entry_path); + } + g_dir_close(root_gdir); + g_assert(g_rmdir(root) == 0); +} + +int main(int argc, char **argv) +{ + g_autofree gchar *tmp_path = g_dir_make_tmp("qemu-test-logging.XXXXXX", NULL); + int rc; + + g_test_init(&argc, &argv, NULL); + g_assert_nonnull(tmp_path); + + g_test_add_func("/logging/parse_range", test_parse_range); + g_test_add_data_func("/logging/parse_path", tmp_path, test_parse_path); + g_test_add_data_func("/logging/logfile_write_path", + tmp_path, test_logfile_write); + g_test_add_data_func("/logging/logfile_lock_path", + tmp_path, test_logfile_lock); + + rc = g_test_run(); + qemu_log_close(); + drain_call_rcu(); + + rmdir_full(tmp_path); + return rc; +} diff --git a/tests/unit/test-mul64.c b/tests/unit/test-mul64.c new file mode 100644 index 0000000000..9be775d084 --- /dev/null +++ b/tests/unit/test-mul64.c @@ -0,0 +1,68 @@ +/* + * Test 64x64 -> 128 multiply subroutines + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "qemu/host-utils.h" + + +typedef struct { + uint64_t a, b; + uint64_t rh, rl; +} Test; + +static const Test test_u_data[] = { + { 1, 1, 0, 1 }, + { 10000, 10000, 0, 100000000 }, + { 0xffffffffffffffffULL, 2, 1, 0xfffffffffffffffeULL }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, + 0xfffffffffffffffeULL, 0x0000000000000001ULL }, + { 0x1122334455667788ull, 0x8877665544332211ull, + 0x092228fb777ae38full, 0x0a3e963337c60008ull }, +}; + +static const Test test_s_data[] = { + { 1, 1, 0, 1 }, + { 1, -1, -1, -1 }, + { -10, -10, 0, 100 }, + { 10000, 10000, 0, 100000000 }, + { -1, 2, -1, -2 }, + { 0x1122334455667788ULL, 0x1122334455667788ULL, + 0x01258f60bbc2975cULL, 0x1eace4a3c82fb840ULL }, +}; + +static void test_u(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(test_u_data); ++i) { + uint64_t rl, rh; + mulu64(&rl, &rh, test_u_data[i].a, test_u_data[i].b); + g_assert_cmpuint(rl, ==, test_u_data[i].rl); + g_assert_cmpuint(rh, ==, test_u_data[i].rh); + } +} + +static void test_s(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(test_s_data); ++i) { + uint64_t rl, rh; + muls64(&rl, &rh, test_s_data[i].a, test_s_data[i].b); + g_assert_cmpuint(rl, ==, test_s_data[i].rl); + g_assert_cmpint(rh, ==, test_s_data[i].rh); + } +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + g_test_add_func("/host-utils/mulu64", test_u); + g_test_add_func("/host-utils/muls64", test_s); + return g_test_run(); +} diff --git a/tests/unit/test-opts-visitor.c b/tests/unit/test-opts-visitor.c new file mode 100644 index 0000000000..23e897061c --- /dev/null +++ b/tests/unit/test-opts-visitor.c @@ -0,0 +1,371 @@ +/* + * Options Visitor unit-tests. + * + * Copyright (C) 2013 Red Hat, Inc. + * + * Authors: + * Laszlo Ersek (based on test-string-output-visitor) + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" + +#include "qemu/config-file.h" /* qemu_add_opts() */ +#include "qemu/option.h" /* qemu_opts_parse() */ +#include "qapi/error.h" +#include "qapi/opts-visitor.h" /* opts_visitor_new() */ +#include "test-qapi-visit.h" /* visit_type_UserDefOptions() */ + +static QemuOptsList userdef_opts = { + .name = "userdef", + .head = QTAILQ_HEAD_INITIALIZER(userdef_opts.head), + .desc = { { 0 } } /* validated with OptsVisitor */ +}; + +/* fixture (= glib test case context) and test case manipulation */ + +typedef struct OptsVisitorFixture { + UserDefOptions *userdef; + Error *err; +} OptsVisitorFixture; + + +static void +setup_fixture(OptsVisitorFixture *f, gconstpointer test_data) +{ + const char *opts_string = test_data; + QemuOpts *opts; + Visitor *v; + + opts = qemu_opts_parse(qemu_find_opts("userdef"), opts_string, false, + NULL); + g_assert(opts != NULL); + + v = opts_visitor_new(opts); + visit_type_UserDefOptions(v, NULL, &f->userdef, &f->err); + visit_free(v); + qemu_opts_del(opts); +} + + +static void +teardown_fixture(OptsVisitorFixture *f, gconstpointer test_data) +{ + qapi_free_UserDefOptions(f->userdef); + error_free(f->err); +} + + +static void +add_test(const char *testpath, + void (*test_func)(OptsVisitorFixture *f, gconstpointer test_data), + gconstpointer test_data) +{ + g_test_add(testpath, OptsVisitorFixture, test_data, setup_fixture, + test_func, teardown_fixture); +} + +/* test output evaluation */ + +static void +expect_ok(OptsVisitorFixture *f, gconstpointer test_data) +{ + g_assert(f->err == NULL); + g_assert(f->userdef != NULL); +} + + +static void +expect_fail(OptsVisitorFixture *f, gconstpointer test_data) +{ + g_assert(f->err != NULL); + + /* The error message is printed when this test utility is invoked directly + * (ie. without gtester) and the --verbose flag is passed: + * + * tests/test-opts-visitor --verbose + */ + g_test_message("'%s': %s", (const char *)test_data, + error_get_pretty(f->err)); +} + + +static void +test_value(OptsVisitorFixture *f, gconstpointer test_data) +{ + uint64_t magic, bitval; + intList *i64; + uint64List *u64; + uint16List *u16; + + expect_ok(f, test_data); + + magic = 0; + for (i64 = f->userdef->i64; i64 != NULL; i64 = i64->next) { + g_assert(-16 <= i64->value && i64->value < 64-16); + bitval = 1ull << (i64->value + 16); + g_assert((magic & bitval) == 0); + magic |= bitval; + } + g_assert(magic == 0xDEADBEEF); + + magic = 0; + for (u64 = f->userdef->u64; u64 != NULL; u64 = u64->next) { + g_assert(u64->value < 64); + bitval = 1ull << u64->value; + g_assert((magic & bitval) == 0); + magic |= bitval; + } + g_assert(magic == 0xBADC0FFEE0DDF00DULL); + + magic = 0; + for (u16 = f->userdef->u16; u16 != NULL; u16 = u16->next) { + g_assert(u16->value < 64); + bitval = 1ull << u16->value; + g_assert((magic & bitval) == 0); + magic |= bitval; + } + g_assert(magic == 0xD15EA5E); +} + + +static void +expect_i64_min(OptsVisitorFixture *f, gconstpointer test_data) +{ + expect_ok(f, test_data); + g_assert(f->userdef->has_i64); + g_assert(f->userdef->i64->next == NULL); + g_assert(f->userdef->i64->value == INT64_MIN); +} + + +static void +expect_i64_max(OptsVisitorFixture *f, gconstpointer test_data) +{ + expect_ok(f, test_data); + g_assert(f->userdef->has_i64); + g_assert(f->userdef->i64->next == NULL); + g_assert(f->userdef->i64->value == INT64_MAX); +} + + +static void +expect_zero(OptsVisitorFixture *f, gconstpointer test_data) +{ + expect_ok(f, test_data); + g_assert(f->userdef->has_u64); + g_assert(f->userdef->u64->next == NULL); + g_assert(f->userdef->u64->value == 0); +} + + +static void +expect_u64_max(OptsVisitorFixture *f, gconstpointer test_data) +{ + expect_ok(f, test_data); + g_assert(f->userdef->has_u64); + g_assert(f->userdef->u64->next == NULL); + g_assert(f->userdef->u64->value == UINT64_MAX); +} + +/* test cases */ + +static void +test_opts_range_unvisited(void) +{ + Error *err = NULL; + intList *list = NULL; + intList *tail; + QemuOpts *opts; + Visitor *v; + + opts = qemu_opts_parse(qemu_find_opts("userdef"), "ilist=0-2", false, + &error_abort); + + v = opts_visitor_new(opts); + + visit_start_struct(v, NULL, NULL, 0, &error_abort); + + /* Would be simpler if the visitor genuinely supported virtual walks */ + visit_start_list(v, "ilist", (GenericList **)&list, sizeof(*list), + &error_abort); + tail = list; + visit_type_int(v, NULL, &tail->value, &error_abort); + g_assert_cmpint(tail->value, ==, 0); + tail = (intList *)visit_next_list(v, (GenericList *)tail, sizeof(*list)); + g_assert(tail); + visit_type_int(v, NULL, &tail->value, &error_abort); + g_assert_cmpint(tail->value, ==, 1); + tail = (intList *)visit_next_list(v, (GenericList *)tail, sizeof(*list)); + g_assert(tail); + visit_check_list(v, &error_abort); /* unvisited tail ignored until... */ + visit_end_list(v, (void **)&list); + + visit_check_struct(v, &err); /* ...here */ + error_free_or_abort(&err); + visit_end_struct(v, NULL); + + qapi_free_intList(list); + visit_free(v); + qemu_opts_del(opts); +} + +static void +test_opts_range_beyond(void) +{ + Error *err = NULL; + intList *list = NULL; + intList *tail; + QemuOpts *opts; + Visitor *v; + int64_t val; + + opts = qemu_opts_parse(qemu_find_opts("userdef"), "ilist=0", false, + &error_abort); + + v = opts_visitor_new(opts); + + visit_start_struct(v, NULL, NULL, 0, &error_abort); + + /* Would be simpler if the visitor genuinely supported virtual walks */ + visit_start_list(v, "ilist", (GenericList **)&list, sizeof(*list), + &error_abort); + tail = list; + visit_type_int(v, NULL, &tail->value, &error_abort); + g_assert_cmpint(tail->value, ==, 0); + tail = (intList *)visit_next_list(v, (GenericList *)tail, sizeof(*tail)); + g_assert(!tail); + visit_type_int(v, NULL, &val, &err); + error_free_or_abort(&err); + visit_end_list(v, (void **)&list); + + visit_check_struct(v, &error_abort); + visit_end_struct(v, NULL); + + qapi_free_intList(list); + visit_free(v); + qemu_opts_del(opts); +} + +static void +test_opts_dict_unvisited(void) +{ + Error *err = NULL; + QemuOpts *opts; + Visitor *v; + UserDefOptions *userdef; + + opts = qemu_opts_parse(qemu_find_opts("userdef"), "i64x=0,bogus=1", false, + &error_abort); + + v = opts_visitor_new(opts); + visit_type_UserDefOptions(v, NULL, &userdef, &err); + error_free_or_abort(&err); + visit_free(v); + qemu_opts_del(opts); + g_assert(!userdef); +} + +int +main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + qemu_add_opts(&userdef_opts); + + /* Three hexadecimal magic numbers, "dead beef", "bad coffee, odd food" and + * "disease", from + * , were + * converted to binary and dissected into bit ranges. Each magic number is + * going to be recomposed using the lists called "i64", "u64" and "u16", + * respectively. + * + * (Note that these types pertain to the individual bit shift counts, not + * the magic numbers themselves; the intent is to exercise opts_type_int() + * and opts_type_uint64().) + * + * The "i64" shift counts have been decreased by 16 (decimal) in order to + * test negative values as well. Finally, the full list of QemuOpt elements + * has been permuted with "shuf". + * + * Both "i64" and "u64" have some (distinct) single-element ranges + * represented as both "a" and "a-a". "u16" is a special case of "i64" (see + * visit_type_uint16()), so it wouldn't add a separate test in this regard. + */ + + add_test("/visitor/opts/flatten/value", &test_value, + "i64=-1-0,u64=12-16,u64=2-3,i64=-11--9,u64=57,u16=9,i64=5-5," + "u16=1-4,u16=20,u64=63-63,i64=-16--13,u64=50-52,i64=14-15,u16=11," + "i64=7,u16=18,i64=2-3,u16=6,u64=54-55,u64=0,u64=18-20,u64=33-43," + "i64=9-12,u16=26-27,u64=59-61,u16=13-16,u64=29-31,u64=22-23," + "u16=24,i64=-7--3"); + + add_test("/visitor/opts/i64/val1/errno", &expect_fail, + "i64=0x8000000000000000"); + add_test("/visitor/opts/i64/val1/empty", &expect_fail, "i64="); + add_test("/visitor/opts/i64/val1/trailing", &expect_fail, "i64=5z"); + add_test("/visitor/opts/i64/nonlist", &expect_fail, "i64x=5-6"); + add_test("/visitor/opts/i64/val2/errno", &expect_fail, + "i64=0x7fffffffffffffff-0x8000000000000000"); + add_test("/visitor/opts/i64/val2/empty", &expect_fail, "i64=5-"); + add_test("/visitor/opts/i64/val2/trailing", &expect_fail, "i64=5-6z"); + add_test("/visitor/opts/i64/range/empty", &expect_fail, "i64=6-5"); + add_test("/visitor/opts/i64/range/minval", &expect_i64_min, + "i64=-0x8000000000000000--0x8000000000000000"); + add_test("/visitor/opts/i64/range/maxval", &expect_i64_max, + "i64=0x7fffffffffffffff-0x7fffffffffffffff"); + + add_test("/visitor/opts/u64/val1/errno", &expect_fail, "u64=-1"); + add_test("/visitor/opts/u64/val1/empty", &expect_fail, "u64="); + add_test("/visitor/opts/u64/val1/trailing", &expect_fail, "u64=5z"); + add_test("/visitor/opts/u64/nonlist", &expect_fail, "u64x=5-6"); + add_test("/visitor/opts/u64/val2/errno", &expect_fail, + "u64=0xffffffffffffffff-0x10000000000000000"); + add_test("/visitor/opts/u64/val2/empty", &expect_fail, "u64=5-"); + add_test("/visitor/opts/u64/val2/trailing", &expect_fail, "u64=5-6z"); + add_test("/visitor/opts/u64/range/empty", &expect_fail, "u64=6-5"); + add_test("/visitor/opts/u64/range/minval", &expect_zero, "u64=0-0"); + add_test("/visitor/opts/u64/range/maxval", &expect_u64_max, + "u64=0xffffffffffffffff-0xffffffffffffffff"); + + /* Test maximum range sizes. The macro value is open-coded here + * *intentionally*; the test case must use concrete values by design. If + * OPTS_VISITOR_RANGE_MAX is changed, the following values need to be + * recalculated as well. The assert and this comment should help with it. + */ + g_assert(OPTS_VISITOR_RANGE_MAX == 65536); + + /* The unsigned case is simple, a u64-u64 difference can always be + * represented as a u64. + */ + add_test("/visitor/opts/u64/range/max", &expect_ok, "u64=0-65535"); + add_test("/visitor/opts/u64/range/2big", &expect_fail, "u64=0-65536"); + + /* The same cannot be said about an i64-i64 difference. */ + add_test("/visitor/opts/i64/range/max/pos/a", &expect_ok, + "i64=0x7fffffffffff0000-0x7fffffffffffffff"); + add_test("/visitor/opts/i64/range/max/pos/b", &expect_ok, + "i64=0x7ffffffffffeffff-0x7ffffffffffffffe"); + add_test("/visitor/opts/i64/range/2big/pos", &expect_fail, + "i64=0x7ffffffffffeffff-0x7fffffffffffffff"); + add_test("/visitor/opts/i64/range/max/neg/a", &expect_ok, + "i64=-0x8000000000000000--0x7fffffffffff0001"); + add_test("/visitor/opts/i64/range/max/neg/b", &expect_ok, + "i64=-0x7fffffffffffffff--0x7fffffffffff0000"); + add_test("/visitor/opts/i64/range/2big/neg", &expect_fail, + "i64=-0x8000000000000000--0x7fffffffffff0000"); + add_test("/visitor/opts/i64/range/2big/full", &expect_fail, + "i64=-0x8000000000000000-0x7fffffffffffffff"); + + g_test_add_func("/visitor/opts/range/unvisited", + test_opts_range_unvisited); + g_test_add_func("/visitor/opts/range/beyond", + test_opts_range_beyond); + + g_test_add_func("/visitor/opts/dict/unvisited", test_opts_dict_unvisited); + + g_test_run(); + return 0; +} diff --git a/tests/unit/test-qapi-util.c b/tests/unit/test-qapi-util.c new file mode 100644 index 0000000000..847f305cff --- /dev/null +++ b/tests/unit/test-qapi-util.c @@ -0,0 +1,78 @@ +/* + * Unit tests for QAPI utility functions + * + * Copyright (C) 2017 Red Hat Inc. + * + * Authors: + * Markus Armbruster , + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" + +static void test_qapi_enum_parse(void) +{ + Error *err = NULL; + int ret; + + ret = qapi_enum_parse(&QType_lookup, NULL, QTYPE_NONE, &error_abort); + g_assert_cmpint(ret, ==, QTYPE_NONE); + + ret = qapi_enum_parse(&QType_lookup, "junk", -1, NULL); + g_assert_cmpint(ret, ==, -1); + + ret = qapi_enum_parse(&QType_lookup, "junk", -1, &err); + error_free_or_abort(&err); + + ret = qapi_enum_parse(&QType_lookup, "none", -1, &error_abort); + g_assert_cmpint(ret, ==, QTYPE_NONE); + + ret = qapi_enum_parse(&QType_lookup, QType_str(QTYPE__MAX - 1), + QTYPE__MAX - 1, &error_abort); + g_assert_cmpint(ret, ==, QTYPE__MAX - 1); +} + +static void test_parse_qapi_name(void) +{ + int ret; + + /* Must start with a letter */ + ret = parse_qapi_name("a", true); + g_assert(ret == 1); + ret = parse_qapi_name("a$", false); + g_assert(ret == 1); + ret = parse_qapi_name("", false); + g_assert(ret == -1); + ret = parse_qapi_name("1", false); + g_assert(ret == -1); + + /* Only letters, digits, hyphen, underscore */ + ret = parse_qapi_name("A-Za-z0-9_", true); + g_assert(ret == 10); + ret = parse_qapi_name("A-Za-z0-9_$", false); + g_assert(ret == 10); + ret = parse_qapi_name("A-Za-z0-9_$", true); + g_assert(ret == -1); + + /* __RFQDN_ */ + ret = parse_qapi_name("__com.redhat_supports", true); + g_assert(ret == 21); + ret = parse_qapi_name("_com.example_", false); + g_assert(ret == -1); + ret = parse_qapi_name("__com.example", false); + g_assert(ret == -1); + ret = parse_qapi_name("__com.example_", false); + g_assert(ret == -1); +} + +int main(int argc, char *argv[]) +{ + g_test_init(&argc, &argv, NULL); + g_test_add_func("/qapi/util/qapi_enum_parse", test_qapi_enum_parse); + g_test_add_func("/qapi/util/parse_qapi_name", test_parse_qapi_name); + g_test_run(); + return 0; +} diff --git a/tests/unit/test-qdev-global-props.c b/tests/unit/test-qdev-global-props.c new file mode 100644 index 0000000000..c8862cac5f --- /dev/null +++ b/tests/unit/test-qdev-global-props.c @@ -0,0 +1,319 @@ +/* + * Test code for qdev global-properties handling + * + * Copyright (c) 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" + +#include "hw/qdev-properties.h" +#include "qom/object.h" +#include "qapi/error.h" +#include "qapi/visitor.h" + + +#define TYPE_STATIC_PROPS "static_prop_type" +typedef struct MyType MyType; +DECLARE_INSTANCE_CHECKER(MyType, STATIC_TYPE, + TYPE_STATIC_PROPS) + +#define TYPE_SUBCLASS "static_prop_subtype" + +#define PROP_DEFAULT 100 + +struct MyType { + DeviceState parent_obj; + + uint32_t prop1; + uint32_t prop2; +}; + +static Property static_props[] = { + DEFINE_PROP_UINT32("prop1", MyType, prop1, PROP_DEFAULT), + DEFINE_PROP_UINT32("prop2", MyType, prop2, PROP_DEFAULT), + DEFINE_PROP_END_OF_LIST() +}; + +static void static_prop_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = NULL; + device_class_set_props(dc, static_props); +} + +static const TypeInfo static_prop_type = { + .name = TYPE_STATIC_PROPS, + .parent = TYPE_DEVICE, + .instance_size = sizeof(MyType), + .class_init = static_prop_class_init, +}; + +static const TypeInfo subclass_type = { + .name = TYPE_SUBCLASS, + .parent = TYPE_STATIC_PROPS, +}; + +/* Test simple static property setting to default value */ +static void test_static_prop_subprocess(void) +{ + MyType *mt; + + mt = STATIC_TYPE(object_new(TYPE_STATIC_PROPS)); + qdev_realize(DEVICE(mt), NULL, &error_fatal); + + g_assert_cmpuint(mt->prop1, ==, PROP_DEFAULT); +} + +static void test_static_prop(void) +{ + g_test_trap_subprocess("/qdev/properties/static/default/subprocess", 0, 0); + g_test_trap_assert_passed(); + g_test_trap_assert_stderr(""); + g_test_trap_assert_stdout(""); +} + +static void register_global_properties(GlobalProperty *props) +{ + int i; + + for (i = 0; props[i].driver != NULL; i++) { + qdev_prop_register_global(props + i); + } +} + + +/* Test setting of static property using global properties */ +static void test_static_globalprop_subprocess(void) +{ + MyType *mt; + static GlobalProperty props[] = { + { TYPE_STATIC_PROPS, "prop1", "200" }, + {} + }; + + register_global_properties(props); + + mt = STATIC_TYPE(object_new(TYPE_STATIC_PROPS)); + qdev_realize(DEVICE(mt), NULL, &error_fatal); + + g_assert_cmpuint(mt->prop1, ==, 200); + g_assert_cmpuint(mt->prop2, ==, PROP_DEFAULT); +} + +static void test_static_globalprop(void) +{ + g_test_trap_subprocess("/qdev/properties/static/global/subprocess", 0, 0); + g_test_trap_assert_passed(); + g_test_trap_assert_stderr(""); + g_test_trap_assert_stdout(""); +} + +#define TYPE_DYNAMIC_PROPS "dynamic-prop-type" +DECLARE_INSTANCE_CHECKER(MyType, DYNAMIC_TYPE, + TYPE_DYNAMIC_PROPS) + +#define TYPE_UNUSED_HOTPLUG "hotplug-type" +#define TYPE_UNUSED_NOHOTPLUG "nohotplug-type" + +static void prop1_accessor(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + MyType *mt = DYNAMIC_TYPE(obj); + + visit_type_uint32(v, name, &mt->prop1, errp); +} + +static void prop2_accessor(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + MyType *mt = DYNAMIC_TYPE(obj); + + visit_type_uint32(v, name, &mt->prop2, errp); +} + +static void dynamic_instance_init(Object *obj) +{ + object_property_add(obj, "prop1", "uint32", prop1_accessor, prop1_accessor, + NULL, NULL); + object_property_add(obj, "prop2", "uint32", prop2_accessor, prop2_accessor, + NULL, NULL); +} + +static void dynamic_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = NULL; +} + + +static const TypeInfo dynamic_prop_type = { + .name = TYPE_DYNAMIC_PROPS, + .parent = TYPE_DEVICE, + .instance_size = sizeof(MyType), + .instance_init = dynamic_instance_init, + .class_init = dynamic_class_init, +}; + +static void hotplug_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = NULL; + dc->hotpluggable = true; +} + +static const TypeInfo hotplug_type = { + .name = TYPE_UNUSED_HOTPLUG, + .parent = TYPE_DEVICE, + .instance_size = sizeof(MyType), + .instance_init = dynamic_instance_init, + .class_init = hotplug_class_init, +}; + +static void nohotplug_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = NULL; + dc->hotpluggable = false; +} + +static const TypeInfo nohotplug_type = { + .name = TYPE_UNUSED_NOHOTPLUG, + .parent = TYPE_DEVICE, + .instance_size = sizeof(MyType), + .instance_init = dynamic_instance_init, + .class_init = nohotplug_class_init, +}; + +#define TYPE_NONDEVICE "nondevice-type" + +static const TypeInfo nondevice_type = { + .name = TYPE_NONDEVICE, + .parent = TYPE_OBJECT, +}; + +/* Test setting of dynamic properties using global properties */ +static void test_dynamic_globalprop_subprocess(void) +{ + MyType *mt; + static GlobalProperty props[] = { + { TYPE_DYNAMIC_PROPS, "prop1", "101", }, + { TYPE_DYNAMIC_PROPS, "prop2", "102", }, + { TYPE_DYNAMIC_PROPS"-bad", "prop3", "103", }, + { TYPE_UNUSED_HOTPLUG, "prop4", "104", }, + { TYPE_UNUSED_NOHOTPLUG, "prop5", "105", }, + { TYPE_NONDEVICE, "prop6", "106", }, + {} + }; + int global_error; + + register_global_properties(props); + + mt = DYNAMIC_TYPE(object_new(TYPE_DYNAMIC_PROPS)); + qdev_realize(DEVICE(mt), NULL, &error_fatal); + + g_assert_cmpuint(mt->prop1, ==, 101); + g_assert_cmpuint(mt->prop2, ==, 102); + global_error = qdev_prop_check_globals(); + g_assert_cmpuint(global_error, ==, 1); + g_assert(props[0].used); + g_assert(props[1].used); + g_assert(!props[2].used); + g_assert(!props[3].used); + g_assert(!props[4].used); + g_assert(!props[5].used); +} + +static void test_dynamic_globalprop(void) +{ + g_test_trap_subprocess("/qdev/properties/dynamic/global/subprocess", 0, 0); + g_test_trap_assert_passed(); + g_test_trap_assert_stderr_unmatched("*prop1*"); + g_test_trap_assert_stderr_unmatched("*prop2*"); + g_test_trap_assert_stderr( + "*warning: global dynamic-prop-type-bad.prop3 has invalid class name*"); + g_test_trap_assert_stderr_unmatched("*prop4*"); + g_test_trap_assert_stderr( + "*warning: global nohotplug-type.prop5=105 not used*"); + g_test_trap_assert_stderr( + "*warning: global nondevice-type.prop6 has invalid class name*"); + g_test_trap_assert_stdout(""); +} + +/* Test if global props affecting subclasses are applied in the right order */ +static void test_subclass_global_props(void) +{ + MyType *mt; + /* Global properties must be applied in the order they were registered */ + static GlobalProperty props[] = { + { TYPE_STATIC_PROPS, "prop1", "101" }, + { TYPE_SUBCLASS, "prop1", "102" }, + { TYPE_SUBCLASS, "prop2", "103" }, + { TYPE_STATIC_PROPS, "prop2", "104" }, + {} + }; + + register_global_properties(props); + + mt = STATIC_TYPE(object_new(TYPE_SUBCLASS)); + qdev_realize(DEVICE(mt), NULL, &error_fatal); + + g_assert_cmpuint(mt->prop1, ==, 102); + g_assert_cmpuint(mt->prop2, ==, 104); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + module_call_init(MODULE_INIT_QOM); + type_register_static(&static_prop_type); + type_register_static(&subclass_type); + type_register_static(&dynamic_prop_type); + type_register_static(&hotplug_type); + type_register_static(&nohotplug_type); + type_register_static(&nondevice_type); + + g_test_add_func("/qdev/properties/static/default/subprocess", + test_static_prop_subprocess); + g_test_add_func("/qdev/properties/static/default", + test_static_prop); + + g_test_add_func("/qdev/properties/static/global/subprocess", + test_static_globalprop_subprocess); + g_test_add_func("/qdev/properties/static/global", + test_static_globalprop); + + g_test_add_func("/qdev/properties/dynamic/global/subprocess", + test_dynamic_globalprop_subprocess); + g_test_add_func("/qdev/properties/dynamic/global", + test_dynamic_globalprop); + + g_test_add_func("/qdev/properties/global/subclass", + test_subclass_global_props); + + g_test_run(); + + return 0; +} diff --git a/tests/unit/test-qdist.c b/tests/unit/test-qdist.c new file mode 100644 index 0000000000..9541ce31eb --- /dev/null +++ b/tests/unit/test-qdist.c @@ -0,0 +1,389 @@ +/* + * Copyright (C) 2016, Emilio G. Cota + * + * License: GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#include "qemu/osdep.h" +#include "qemu/qdist.h" + +#include + +struct entry_desc { + double x; + unsigned long count; + + /* 0 prints a space, 1-8 prints from qdist_blocks[] */ + int fill_code; +}; + +/* See: https://en.wikipedia.org/wiki/Block_Elements */ +static const gunichar qdist_blocks[] = { + 0x2581, + 0x2582, + 0x2583, + 0x2584, + 0x2585, + 0x2586, + 0x2587, + 0x2588 +}; + +#define QDIST_NR_BLOCK_CODES ARRAY_SIZE(qdist_blocks) + +static char *pr_hist(const struct entry_desc *darr, size_t n) +{ + GString *s = g_string_new(""); + size_t i; + + for (i = 0; i < n; i++) { + int fill = darr[i].fill_code; + + if (fill) { + assert(fill <= QDIST_NR_BLOCK_CODES); + g_string_append_unichar(s, qdist_blocks[fill - 1]); + } else { + g_string_append_c(s, ' '); + } + } + return g_string_free(s, FALSE); +} + +static void +histogram_check(const struct qdist *dist, const struct entry_desc *darr, + size_t n, size_t n_bins) +{ + char *pr = qdist_pr_plain(dist, n_bins); + char *str = pr_hist(darr, n); + + g_assert_cmpstr(pr, ==, str); + g_free(pr); + g_free(str); +} + +static void histogram_check_single_full(const struct qdist *dist, size_t n_bins) +{ + struct entry_desc desc = { .fill_code = 8 }; + + histogram_check(dist, &desc, 1, n_bins); +} + +static void +entries_check(const struct qdist *dist, const struct entry_desc *darr, size_t n) +{ + size_t i; + + for (i = 0; i < n; i++) { + struct qdist_entry *e = &dist->entries[i]; + + g_assert_cmpuint(e->count, ==, darr[i].count); + } +} + +static void +entries_insert(struct qdist *dist, const struct entry_desc *darr, size_t n) +{ + size_t i; + + for (i = 0; i < n; i++) { + qdist_add(dist, darr[i].x, darr[i].count); + } +} + +static void do_test_bin(const struct entry_desc *a, size_t n_a, + const struct entry_desc *b, size_t n_b) +{ + struct qdist qda; + struct qdist qdb; + + qdist_init(&qda); + + entries_insert(&qda, a, n_a); + qdist_inc(&qda, a[0].x); + qdist_add(&qda, a[0].x, -1); + + g_assert_cmpuint(qdist_unique_entries(&qda), ==, n_a); + g_assert_cmpfloat(qdist_xmin(&qda), ==, a[0].x); + g_assert_cmpfloat(qdist_xmax(&qda), ==, a[n_a - 1].x); + histogram_check(&qda, a, n_a, 0); + histogram_check(&qda, a, n_a, n_a); + + qdist_bin__internal(&qdb, &qda, n_b); + g_assert_cmpuint(qdb.n, ==, n_b); + entries_check(&qdb, b, n_b); + g_assert_cmpuint(qdist_sample_count(&qda), ==, qdist_sample_count(&qdb)); + /* + * No histogram_check() for $qdb, since we'd rebin it and that is a bug. + * Instead, regenerate it from $qda. + */ + histogram_check(&qda, b, n_b, n_b); + + qdist_destroy(&qdb); + qdist_destroy(&qda); +} + +static void do_test_pr(uint32_t opt) +{ + static const struct entry_desc desc[] = { + [0] = { 1, 900, 8 }, + [1] = { 2, 1, 1 }, + [2] = { 3, 2, 1 } + }; + static const char border[] = "|"; + const char *llabel = NULL; + const char *rlabel = NULL; + struct qdist dist; + GString *s; + char *str; + char *pr; + size_t n; + + n = ARRAY_SIZE(desc); + qdist_init(&dist); + + entries_insert(&dist, desc, n); + histogram_check(&dist, desc, n, 0); + + s = g_string_new(""); + + if (opt & QDIST_PR_LABELS) { + unsigned int lopts = opt & (QDIST_PR_NODECIMAL | + QDIST_PR_PERCENT | + QDIST_PR_100X | + QDIST_PR_NOBINRANGE); + + if (lopts == 0) { + llabel = "[1.0,1.7)"; + rlabel = "[2.3,3.0]"; + } else if (lopts == QDIST_PR_NODECIMAL) { + llabel = "[1,2)"; + rlabel = "[2,3]"; + } else if (lopts == (QDIST_PR_PERCENT | QDIST_PR_NODECIMAL)) { + llabel = "[1,2)%"; + rlabel = "[2,3]%"; + } else if (lopts == QDIST_PR_100X) { + llabel = "[100.0,166.7)"; + rlabel = "[233.3,300.0]"; + } else if (lopts == (QDIST_PR_NOBINRANGE | QDIST_PR_NODECIMAL)) { + llabel = "1"; + rlabel = "3"; + } else { + g_assert_cmpstr("BUG", ==, "This is not meant to be exhaustive"); + } + } + + if (llabel) { + g_string_append(s, llabel); + } + if (opt & QDIST_PR_BORDER) { + g_string_append(s, border); + } + + str = pr_hist(desc, n); + g_string_append(s, str); + g_free(str); + + if (opt & QDIST_PR_BORDER) { + g_string_append(s, border); + } + if (rlabel) { + g_string_append(s, rlabel); + } + + str = g_string_free(s, FALSE); + pr = qdist_pr(&dist, n, opt); + g_assert_cmpstr(pr, ==, str); + g_free(pr); + g_free(str); + + qdist_destroy(&dist); +} + +static inline void do_test_pr_label(uint32_t opt) +{ + opt |= QDIST_PR_LABELS; + do_test_pr(opt); +} + +static void test_pr(void) +{ + do_test_pr(0); + + do_test_pr(QDIST_PR_BORDER); + + /* 100X should be ignored because we're not setting LABELS */ + do_test_pr(QDIST_PR_100X); + + do_test_pr_label(0); + do_test_pr_label(QDIST_PR_NODECIMAL); + do_test_pr_label(QDIST_PR_PERCENT | QDIST_PR_NODECIMAL); + do_test_pr_label(QDIST_PR_100X); + do_test_pr_label(QDIST_PR_NOBINRANGE | QDIST_PR_NODECIMAL); +} + +static void test_bin_shrink(void) +{ + static const struct entry_desc a[] = { + [0] = { 0.0, 42922, 7 }, + [1] = { 0.25, 47834, 8 }, + [2] = { 0.50, 26628, 0 }, + [3] = { 0.625, 597, 4 }, + [4] = { 0.75, 10298, 1 }, + [5] = { 0.875, 22, 2 }, + [6] = { 1.0, 2771, 1 } + }; + static const struct entry_desc b[] = { + [0] = { 0.0, 42922, 7 }, + [1] = { 0.25, 47834, 8 }, + [2] = { 0.50, 27225, 3 }, + [3] = { 0.75, 13091, 1 } + }; + + return do_test_bin(a, ARRAY_SIZE(a), b, ARRAY_SIZE(b)); +} + +static void test_bin_expand(void) +{ + static const struct entry_desc a[] = { + [0] = { 0.0, 11713, 5 }, + [1] = { 0.25, 20294, 0 }, + [2] = { 0.50, 17266, 8 }, + [3] = { 0.625, 1506, 0 }, + [4] = { 0.75, 10355, 6 }, + [5] = { 0.833, 2, 1 }, + [6] = { 0.875, 99, 4 }, + [7] = { 1.0, 4301, 2 } + }; + static const struct entry_desc b[] = { + [0] = { 0.0, 11713, 5 }, + [1] = { 0.0, 0, 0 }, + [2] = { 0.0, 20294, 8 }, + [3] = { 0.0, 0, 0 }, + [4] = { 0.0, 0, 0 }, + [5] = { 0.0, 17266, 6 }, + [6] = { 0.0, 1506, 1 }, + [7] = { 0.0, 10355, 4 }, + [8] = { 0.0, 101, 1 }, + [9] = { 0.0, 4301, 2 } + }; + + return do_test_bin(a, ARRAY_SIZE(a), b, ARRAY_SIZE(b)); +} + +static void test_bin_precision(void) +{ + static const struct entry_desc a[] = { + [0] = { 0, 213549, 8 }, + [1] = { 1, 70, 1 }, + }; + static const struct entry_desc b[] = { + [0] = { 0, 213549, 8 }, + [1] = { 0, 70, 1 }, + }; + + return do_test_bin(a, ARRAY_SIZE(a), b, ARRAY_SIZE(b)); +} + +static void test_bin_simple(void) +{ + static const struct entry_desc a[] = { + [0] = { 10, 101, 8 }, + [1] = { 11, 0, 0 }, + [2] = { 12, 2, 1 } + }; + static const struct entry_desc b[] = { + [0] = { 0, 101, 8 }, + [1] = { 0, 0, 0 }, + [2] = { 0, 0, 0 }, + [3] = { 0, 0, 0 }, + [4] = { 0, 2, 1 } + }; + + return do_test_bin(a, ARRAY_SIZE(a), b, ARRAY_SIZE(b)); +} + +static void test_single_full(void) +{ + struct qdist dist; + + qdist_init(&dist); + + qdist_add(&dist, 3, 102); + g_assert_cmpfloat(qdist_avg(&dist), ==, 3); + g_assert_cmpfloat(qdist_xmin(&dist), ==, 3); + g_assert_cmpfloat(qdist_xmax(&dist), ==, 3); + + histogram_check_single_full(&dist, 0); + histogram_check_single_full(&dist, 1); + histogram_check_single_full(&dist, 10); + + qdist_destroy(&dist); +} + +static void test_single_empty(void) +{ + struct qdist dist; + char *pr; + + qdist_init(&dist); + + qdist_add(&dist, 3, 0); + g_assert_cmpuint(qdist_sample_count(&dist), ==, 0); + g_assert(isnan(qdist_avg(&dist))); + g_assert_cmpfloat(qdist_xmin(&dist), ==, 3); + g_assert_cmpfloat(qdist_xmax(&dist), ==, 3); + + pr = qdist_pr_plain(&dist, 0); + g_assert_cmpstr(pr, ==, " "); + g_free(pr); + + pr = qdist_pr_plain(&dist, 1); + g_assert_cmpstr(pr, ==, " "); + g_free(pr); + + pr = qdist_pr_plain(&dist, 2); + g_assert_cmpstr(pr, ==, " "); + g_free(pr); + + qdist_destroy(&dist); +} + +static void test_none(void) +{ + struct qdist dist; + char *pr; + + qdist_init(&dist); + + g_assert(isnan(qdist_avg(&dist))); + g_assert(isnan(qdist_xmin(&dist))); + g_assert(isnan(qdist_xmax(&dist))); + + pr = qdist_pr_plain(&dist, 0); + g_assert_cmpstr(pr, ==, "(empty)"); + g_free(pr); + + pr = qdist_pr_plain(&dist, 2); + g_assert_cmpstr(pr, ==, "(empty)"); + g_free(pr); + + pr = qdist_pr(&dist, 0, QDIST_PR_BORDER); + g_assert_cmpstr(pr, ==, "(empty)"); + g_free(pr); + + qdist_destroy(&dist); +} + +int main(int argc, char *argv[]) +{ + g_test_init(&argc, &argv, NULL); + g_test_add_func("/qdist/none", test_none); + g_test_add_func("/qdist/single/empty", test_single_empty); + g_test_add_func("/qdist/single/full", test_single_full); + g_test_add_func("/qdist/binning/simple", test_bin_simple); + g_test_add_func("/qdist/binning/precision", test_bin_precision); + g_test_add_func("/qdist/binning/expand", test_bin_expand); + g_test_add_func("/qdist/binning/shrink", test_bin_shrink); + g_test_add_func("/qdist/pr", test_pr); + return g_test_run(); +} diff --git a/tests/unit/test-qemu-opts.c b/tests/unit/test-qemu-opts.c new file mode 100644 index 0000000000..8bbb17b1c7 --- /dev/null +++ b/tests/unit/test-qemu-opts.c @@ -0,0 +1,1046 @@ +/* + * QemuOpts unit-tests. + * + * Copyright (C) 2014 Leandro Dorileo + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "qemu/option.h" +#include "qemu/option_int.h" +#include "qapi/error.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qmp/qstring.h" +#include "qemu/config-file.h" + + +static QemuOptsList opts_list_01 = { + .name = "opts_list_01", + .head = QTAILQ_HEAD_INITIALIZER(opts_list_01.head), + .desc = { + { + .name = "str1", + .type = QEMU_OPT_STRING, + .help = "Help texts are preserved in qemu_opts_append", + .def_value_str = "default", + },{ + .name = "str2", + .type = QEMU_OPT_STRING, + },{ + .name = "str3", + .type = QEMU_OPT_STRING, + },{ + .name = "number1", + .type = QEMU_OPT_NUMBER, + .help = "Having help texts only for some options is okay", + },{ + .name = "number2", + .type = QEMU_OPT_NUMBER, + }, + { /* end of list */ } + }, +}; + +static QemuOptsList opts_list_02 = { + .name = "opts_list_02", + .head = QTAILQ_HEAD_INITIALIZER(opts_list_02.head), + .desc = { + { + .name = "str1", + .type = QEMU_OPT_STRING, + },{ + .name = "str2", + .type = QEMU_OPT_STRING, + },{ + .name = "bool1", + .type = QEMU_OPT_BOOL, + },{ + .name = "bool2", + .type = QEMU_OPT_BOOL, + },{ + .name = "size1", + .type = QEMU_OPT_SIZE, + },{ + .name = "size2", + .type = QEMU_OPT_SIZE, + },{ + .name = "size3", + .type = QEMU_OPT_SIZE, + }, + { /* end of list */ } + }, +}; + +static QemuOptsList opts_list_03 = { + .name = "opts_list_03", + .implied_opt_name = "implied", + .head = QTAILQ_HEAD_INITIALIZER(opts_list_03.head), + .desc = { + /* no elements => accept any params */ + { /* end of list */ } + }, +}; + +static QemuOptsList opts_list_04 = { + .name = "opts_list_04", + .head = QTAILQ_HEAD_INITIALIZER(opts_list_04.head), + .merge_lists = true, + .desc = { + { + .name = "str3", + .type = QEMU_OPT_STRING, + }, + { /* end of list */ } + }, +}; + +static void register_opts(void) +{ + qemu_add_opts(&opts_list_01); + qemu_add_opts(&opts_list_02); + qemu_add_opts(&opts_list_03); + qemu_add_opts(&opts_list_04); +} + +static void test_find_unknown_opts(void) +{ + QemuOptsList *list; + Error *err = NULL; + + /* should not return anything, we don't have an "unknown" option */ + list = qemu_find_opts_err("unknown", &err); + g_assert(list == NULL); + error_free_or_abort(&err); +} + +static void test_qemu_find_opts(void) +{ + QemuOptsList *list; + + /* we have an "opts_list_01" option, should return it */ + list = qemu_find_opts("opts_list_01"); + g_assert(list != NULL); + g_assert_cmpstr(list->name, ==, "opts_list_01"); +} + +static void test_qemu_opts_create(void) +{ + QemuOptsList *list; + QemuOpts *opts; + + list = qemu_find_opts("opts_list_01"); + g_assert(list != NULL); + g_assert(QTAILQ_EMPTY(&list->head)); + g_assert_cmpstr(list->name, ==, "opts_list_01"); + + /* should not find anything at this point */ + opts = qemu_opts_find(list, NULL); + g_assert(opts == NULL); + + /* create the opts */ + opts = qemu_opts_create(list, NULL, 0, &error_abort); + g_assert(opts != NULL); + g_assert(!QTAILQ_EMPTY(&list->head)); + + /* now we've create the opts, must find it */ + opts = qemu_opts_find(list, NULL); + g_assert(opts != NULL); + + qemu_opts_del(opts); + + /* should not find anything at this point */ + opts = qemu_opts_find(list, NULL); + g_assert(opts == NULL); +} + +static void test_qemu_opt_get(void) +{ + QemuOptsList *list; + QemuOpts *opts; + const char *opt = NULL; + + list = qemu_find_opts("opts_list_01"); + g_assert(list != NULL); + g_assert(QTAILQ_EMPTY(&list->head)); + g_assert_cmpstr(list->name, ==, "opts_list_01"); + + /* should not find anything at this point */ + opts = qemu_opts_find(list, NULL); + g_assert(opts == NULL); + + /* create the opts */ + opts = qemu_opts_create(list, NULL, 0, &error_abort); + g_assert(opts != NULL); + g_assert(!QTAILQ_EMPTY(&list->head)); + + /* haven't set anything to str2 yet */ + opt = qemu_opt_get(opts, "str2"); + g_assert(opt == NULL); + + qemu_opt_set(opts, "str2", "value", &error_abort); + + /* now we have set str2, should know about it */ + opt = qemu_opt_get(opts, "str2"); + g_assert_cmpstr(opt, ==, "value"); + + qemu_opt_set(opts, "str2", "value2", &error_abort); + + /* having reset the value, the returned should be the reset one */ + opt = qemu_opt_get(opts, "str2"); + g_assert_cmpstr(opt, ==, "value2"); + + qemu_opts_del(opts); + + /* should not find anything at this point */ + opts = qemu_opts_find(list, NULL); + g_assert(opts == NULL); +} + +static void test_qemu_opt_get_bool(void) +{ + QemuOptsList *list; + QemuOpts *opts; + bool opt; + + list = qemu_find_opts("opts_list_02"); + g_assert(list != NULL); + g_assert(QTAILQ_EMPTY(&list->head)); + g_assert_cmpstr(list->name, ==, "opts_list_02"); + + /* should not find anything at this point */ + opts = qemu_opts_find(list, NULL); + g_assert(opts == NULL); + + /* create the opts */ + opts = qemu_opts_create(list, NULL, 0, &error_abort); + g_assert(opts != NULL); + g_assert(!QTAILQ_EMPTY(&list->head)); + + /* haven't set anything to bool1 yet, so defval should be returned */ + opt = qemu_opt_get_bool(opts, "bool1", false); + g_assert(opt == false); + + qemu_opt_set_bool(opts, "bool1", true, &error_abort); + + /* now we have set bool1, should know about it */ + opt = qemu_opt_get_bool(opts, "bool1", false); + g_assert(opt == true); + + /* having reset the value, opt should be the reset one not defval */ + qemu_opt_set_bool(opts, "bool1", false, &error_abort); + + opt = qemu_opt_get_bool(opts, "bool1", true); + g_assert(opt == false); + + qemu_opts_del(opts); + + /* should not find anything at this point */ + opts = qemu_opts_find(list, NULL); + g_assert(opts == NULL); +} + +static void test_qemu_opt_get_number(void) +{ + QemuOptsList *list; + QemuOpts *opts; + uint64_t opt; + + list = qemu_find_opts("opts_list_01"); + g_assert(list != NULL); + g_assert(QTAILQ_EMPTY(&list->head)); + g_assert_cmpstr(list->name, ==, "opts_list_01"); + + /* should not find anything at this point */ + opts = qemu_opts_find(list, NULL); + g_assert(opts == NULL); + + /* create the opts */ + opts = qemu_opts_create(list, NULL, 0, &error_abort); + g_assert(opts != NULL); + g_assert(!QTAILQ_EMPTY(&list->head)); + + /* haven't set anything to number1 yet, so defval should be returned */ + opt = qemu_opt_get_number(opts, "number1", 5); + g_assert(opt == 5); + + qemu_opt_set_number(opts, "number1", 10, &error_abort); + + /* now we have set number1, should know about it */ + opt = qemu_opt_get_number(opts, "number1", 5); + g_assert(opt == 10); + + /* having reset it, the returned should be the reset one not defval */ + qemu_opt_set_number(opts, "number1", 15, &error_abort); + + opt = qemu_opt_get_number(opts, "number1", 5); + g_assert(opt == 15); + + qemu_opts_del(opts); + + /* should not find anything at this point */ + opts = qemu_opts_find(list, NULL); + g_assert(opts == NULL); +} + +static void test_qemu_opt_get_size(void) +{ + QemuOptsList *list; + QemuOpts *opts; + uint64_t opt; + QDict *dict; + + list = qemu_find_opts("opts_list_02"); + g_assert(list != NULL); + g_assert(QTAILQ_EMPTY(&list->head)); + g_assert_cmpstr(list->name, ==, "opts_list_02"); + + /* should not find anything at this point */ + opts = qemu_opts_find(list, NULL); + g_assert(opts == NULL); + + /* create the opts */ + opts = qemu_opts_create(list, NULL, 0, &error_abort); + g_assert(opts != NULL); + g_assert(!QTAILQ_EMPTY(&list->head)); + + /* haven't set anything to size1 yet, so defval should be returned */ + opt = qemu_opt_get_size(opts, "size1", 5); + g_assert(opt == 5); + + dict = qdict_new(); + g_assert(dict != NULL); + + qdict_put_str(dict, "size1", "10"); + + qemu_opts_absorb_qdict(opts, dict, &error_abort); + g_assert(error_abort == NULL); + + /* now we have set size1, should know about it */ + opt = qemu_opt_get_size(opts, "size1", 5); + g_assert(opt == 10); + + /* reset value */ + qdict_put_str(dict, "size1", "15"); + + qemu_opts_absorb_qdict(opts, dict, &error_abort); + g_assert(error_abort == NULL); + + /* test the reset value */ + opt = qemu_opt_get_size(opts, "size1", 5); + g_assert(opt == 15); + + qdict_del(dict, "size1"); + g_free(dict); + + qemu_opts_del(opts); + + /* should not find anything at this point */ + opts = qemu_opts_find(list, NULL); + g_assert(opts == NULL); +} + +static void test_qemu_opt_unset(void) +{ + QemuOpts *opts; + const char *value; + int ret; + + /* dynamically initialized (parsed) opts */ + opts = qemu_opts_parse(&opts_list_03, "key=value", false, NULL); + g_assert(opts != NULL); + + /* check default/parsed value */ + value = qemu_opt_get(opts, "key"); + g_assert_cmpstr(value, ==, "value"); + + /* reset it to value2 */ + qemu_opt_set(opts, "key", "value2", &error_abort); + + value = qemu_opt_get(opts, "key"); + g_assert_cmpstr(value, ==, "value2"); + + /* unset, valid only for "accept any" */ + ret = qemu_opt_unset(opts, "key"); + g_assert(ret == 0); + + /* after reset the value should be the parsed/default one */ + value = qemu_opt_get(opts, "key"); + g_assert_cmpstr(value, ==, "value"); + + qemu_opts_del(opts); +} + +static void test_qemu_opts_reset(void) +{ + QemuOptsList *list; + QemuOpts *opts; + uint64_t opt; + + list = qemu_find_opts("opts_list_01"); + g_assert(list != NULL); + g_assert(QTAILQ_EMPTY(&list->head)); + g_assert_cmpstr(list->name, ==, "opts_list_01"); + + /* should not find anything at this point */ + opts = qemu_opts_find(list, NULL); + g_assert(opts == NULL); + + /* create the opts */ + opts = qemu_opts_create(list, NULL, 0, &error_abort); + g_assert(opts != NULL); + g_assert(!QTAILQ_EMPTY(&list->head)); + + /* haven't set anything to number1 yet, so defval should be returned */ + opt = qemu_opt_get_number(opts, "number1", 5); + g_assert(opt == 5); + + qemu_opt_set_number(opts, "number1", 10, &error_abort); + + /* now we have set number1, should know about it */ + opt = qemu_opt_get_number(opts, "number1", 5); + g_assert(opt == 10); + + qemu_opts_reset(list); + + /* should not find anything at this point */ + opts = qemu_opts_find(list, NULL); + g_assert(opts == NULL); +} + +static void test_qemu_opts_set(void) +{ + QemuOptsList *list; + QemuOpts *opts; + const char *opt; + + list = qemu_find_opts("opts_list_04"); + g_assert(list != NULL); + g_assert(QTAILQ_EMPTY(&list->head)); + g_assert_cmpstr(list->name, ==, "opts_list_04"); + + /* should not find anything at this point */ + opts = qemu_opts_find(list, NULL); + g_assert(opts == NULL); + + /* implicitly create opts and set str3 value */ + qemu_opts_set(list, "str3", "value", &error_abort); + g_assert(!QTAILQ_EMPTY(&list->head)); + + /* get the just created opts */ + opts = qemu_opts_find(list, NULL); + g_assert(opts != NULL); + + /* check the str3 value */ + opt = qemu_opt_get(opts, "str3"); + g_assert_cmpstr(opt, ==, "value"); + + qemu_opts_del(opts); + + /* should not find anything at this point */ + opts = qemu_opts_find(list, NULL); + g_assert(opts == NULL); +} + +static int opts_count_iter(void *opaque, const char *name, const char *value, + Error **errp) +{ + (*(size_t *)opaque)++; + return 0; +} + +static size_t opts_count(QemuOpts *opts) +{ + size_t n = 0; + + qemu_opt_foreach(opts, opts_count_iter, &n, NULL); + return n; +} + +static void test_opts_parse(void) +{ + Error *err = NULL; + QemuOpts *opts; + + /* Nothing */ + opts = qemu_opts_parse(&opts_list_03, "", false, &error_abort); + g_assert_cmpuint(opts_count(opts), ==, 0); + + /* Empty key */ + opts = qemu_opts_parse(&opts_list_03, "=val", false, &error_abort); + g_assert_cmpuint(opts_count(opts), ==, 1); + g_assert_cmpstr(qemu_opt_get(opts, ""), ==, "val"); + + /* Multiple keys, last one wins */ + opts = qemu_opts_parse(&opts_list_03, "a=1,b=2,,x,a=3", + false, &error_abort); + g_assert_cmpuint(opts_count(opts), ==, 3); + g_assert_cmpstr(qemu_opt_get(opts, "a"), ==, "3"); + g_assert_cmpstr(qemu_opt_get(opts, "b"), ==, "2,x"); + + /* Except when it doesn't */ + opts = qemu_opts_parse(&opts_list_03, "id=foo,id=bar", + false, &error_abort); + g_assert_cmpuint(opts_count(opts), ==, 0); + g_assert_cmpstr(qemu_opts_id(opts), ==, "foo"); + + /* TODO Cover low-level access to repeated keys */ + + /* Trailing comma is ignored */ + opts = qemu_opts_parse(&opts_list_03, "x=y,", false, &error_abort); + g_assert_cmpuint(opts_count(opts), ==, 1); + g_assert_cmpstr(qemu_opt_get(opts, "x"), ==, "y"); + + /* Except when it isn't */ + opts = qemu_opts_parse(&opts_list_03, ",", false, &error_abort); + g_assert_cmpuint(opts_count(opts), ==, 1); + g_assert_cmpstr(qemu_opt_get(opts, ""), ==, "on"); + + /* Duplicate ID */ + opts = qemu_opts_parse(&opts_list_03, "x=y,id=foo", false, &err); + error_free_or_abort(&err); + g_assert(!opts); + /* TODO Cover .merge_lists = true */ + + /* Buggy ID recognition (fixed) */ + opts = qemu_opts_parse(&opts_list_03, "x=,,id=bar", false, &error_abort); + g_assert_cmpuint(opts_count(opts), ==, 1); + g_assert(!qemu_opts_id(opts)); + g_assert_cmpstr(qemu_opt_get(opts, "x"), ==, ",id=bar"); + + /* Anti-social ID */ + opts = qemu_opts_parse(&opts_list_01, "id=666", false, &err); + error_free_or_abort(&err); + g_assert(!opts); + + /* Implied value (qemu_opts_parse warns but accepts it) */ + opts = qemu_opts_parse(&opts_list_03, "an,noaus,noaus=", + false, &error_abort); + g_assert_cmpuint(opts_count(opts), ==, 3); + g_assert_cmpstr(qemu_opt_get(opts, "an"), ==, "on"); + g_assert_cmpstr(qemu_opt_get(opts, "aus"), ==, "off"); + g_assert_cmpstr(qemu_opt_get(opts, "noaus"), ==, ""); + + /* Implied value, negated empty key */ + opts = qemu_opts_parse(&opts_list_03, "no", false, &error_abort); + g_assert_cmpuint(opts_count(opts), ==, 1); + g_assert_cmpstr(qemu_opt_get(opts, ""), ==, "off"); + + /* Implied key */ + opts = qemu_opts_parse(&opts_list_03, "an,noaus,noaus=", true, + &error_abort); + g_assert_cmpuint(opts_count(opts), ==, 3); + g_assert_cmpstr(qemu_opt_get(opts, "implied"), ==, "an"); + g_assert_cmpstr(qemu_opt_get(opts, "aus"), ==, "off"); + g_assert_cmpstr(qemu_opt_get(opts, "noaus"), ==, ""); + + /* Implied key with empty value */ + opts = qemu_opts_parse(&opts_list_03, ",", true, &error_abort); + g_assert_cmpuint(opts_count(opts), ==, 1); + g_assert_cmpstr(qemu_opt_get(opts, "implied"), ==, ""); + + /* Implied key with comma value */ + opts = qemu_opts_parse(&opts_list_03, ",,,a=1", true, &error_abort); + g_assert_cmpuint(opts_count(opts), ==, 2); + g_assert_cmpstr(qemu_opt_get(opts, "implied"), ==, ","); + g_assert_cmpstr(qemu_opt_get(opts, "a"), ==, "1"); + + /* Empty key is not an implied key */ + opts = qemu_opts_parse(&opts_list_03, "=val", true, &error_abort); + g_assert_cmpuint(opts_count(opts), ==, 1); + g_assert_cmpstr(qemu_opt_get(opts, ""), ==, "val"); + + /* Unknown key */ + opts = qemu_opts_parse(&opts_list_01, "nonexistent=", false, &err); + error_free_or_abort(&err); + g_assert(!opts); + + qemu_opts_reset(&opts_list_01); + qemu_opts_reset(&opts_list_03); +} + +static void test_opts_parse_bool(void) +{ + Error *err = NULL; + QemuOpts *opts; + + opts = qemu_opts_parse(&opts_list_02, "bool1=on,bool2=off", + false, &error_abort); + g_assert_cmpuint(opts_count(opts), ==, 2); + g_assert(qemu_opt_get_bool(opts, "bool1", false)); + g_assert(!qemu_opt_get_bool(opts, "bool2", true)); + + opts = qemu_opts_parse(&opts_list_02, "bool1=offer", false, &err); + error_free_or_abort(&err); + g_assert(!opts); + + qemu_opts_reset(&opts_list_02); +} + +static void test_opts_parse_number(void) +{ + Error *err = NULL; + QemuOpts *opts; + + /* Lower limit zero */ + opts = qemu_opts_parse(&opts_list_01, "number1=0", false, &error_abort); + g_assert_cmpuint(opts_count(opts), ==, 1); + g_assert_cmpuint(qemu_opt_get_number(opts, "number1", 1), ==, 0); + + /* Upper limit 2^64-1 */ + opts = qemu_opts_parse(&opts_list_01, + "number1=18446744073709551615,number2=-1", + false, &error_abort); + g_assert_cmpuint(opts_count(opts), ==, 2); + g_assert_cmphex(qemu_opt_get_number(opts, "number1", 1), ==, UINT64_MAX); + g_assert_cmphex(qemu_opt_get_number(opts, "number2", 0), ==, UINT64_MAX); + + /* Above upper limit */ + opts = qemu_opts_parse(&opts_list_01, "number1=18446744073709551616", + false, &err); + error_free_or_abort(&err); + g_assert(!opts); + + /* Below lower limit */ + opts = qemu_opts_parse(&opts_list_01, "number1=-18446744073709551616", + false, &err); + error_free_or_abort(&err); + g_assert(!opts); + + /* Hex and octal */ + opts = qemu_opts_parse(&opts_list_01, "number1=0x2a,number2=052", + false, &error_abort); + g_assert_cmpuint(opts_count(opts), ==, 2); + g_assert_cmpuint(qemu_opt_get_number(opts, "number1", 1), ==, 42); + g_assert_cmpuint(qemu_opt_get_number(opts, "number2", 0), ==, 42); + + /* Invalid */ + opts = qemu_opts_parse(&opts_list_01, "number1=", false, &err); + error_free_or_abort(&err); + g_assert(!opts); + opts = qemu_opts_parse(&opts_list_01, "number1=eins", false, &err); + error_free_or_abort(&err); + g_assert(!opts); + + /* Leading whitespace */ + opts = qemu_opts_parse(&opts_list_01, "number1= \t42", + false, &error_abort); + g_assert_cmpuint(opts_count(opts), ==, 1); + g_assert_cmpuint(qemu_opt_get_number(opts, "number1", 1), ==, 42); + + /* Trailing crap */ + opts = qemu_opts_parse(&opts_list_01, "number1=3.14", false, &err); + error_free_or_abort(&err); + g_assert(!opts); + opts = qemu_opts_parse(&opts_list_01, "number1=08", false, &err); + error_free_or_abort(&err); + g_assert(!opts); + opts = qemu_opts_parse(&opts_list_01, "number1=0 ", false, &err); + error_free_or_abort(&err); + g_assert(!opts); + + qemu_opts_reset(&opts_list_01); +} + +static void test_opts_parse_size(void) +{ + Error *err = NULL; + QemuOpts *opts; + + /* Lower limit zero */ + opts = qemu_opts_parse(&opts_list_02, "size1=0", false, &error_abort); + g_assert_cmpuint(opts_count(opts), ==, 1); + g_assert_cmpuint(qemu_opt_get_size(opts, "size1", 1), ==, 0); + + /* Note: precision is 53 bits since we're parsing with strtod() */ + + /* Around limit of precision: 2^53-1, 2^53, 2^54 */ + opts = qemu_opts_parse(&opts_list_02, + "size1=9007199254740991," + "size2=9007199254740992," + "size3=9007199254740993", + false, &error_abort); + g_assert_cmpuint(opts_count(opts), ==, 3); + g_assert_cmphex(qemu_opt_get_size(opts, "size1", 1), + ==, 0x1fffffffffffff); + g_assert_cmphex(qemu_opt_get_size(opts, "size2", 1), + ==, 0x20000000000000); + g_assert_cmphex(qemu_opt_get_size(opts, "size3", 1), + ==, 0x20000000000000); + + /* Close to signed upper limit 0x7ffffffffffffc00 (53 msbs set) */ + opts = qemu_opts_parse(&opts_list_02, + "size1=9223372036854774784," /* 7ffffffffffffc00 */ + "size2=9223372036854775295", /* 7ffffffffffffdff */ + false, &error_abort); + g_assert_cmpuint(opts_count(opts), ==, 2); + g_assert_cmphex(qemu_opt_get_size(opts, "size1", 1), + ==, 0x7ffffffffffffc00); + g_assert_cmphex(qemu_opt_get_size(opts, "size2", 1), + ==, 0x7ffffffffffffc00); + + /* Close to actual upper limit 0xfffffffffffff800 (53 msbs set) */ + opts = qemu_opts_parse(&opts_list_02, + "size1=18446744073709549568," /* fffffffffffff800 */ + "size2=18446744073709550591", /* fffffffffffffbff */ + false, &error_abort); + g_assert_cmpuint(opts_count(opts), ==, 2); + g_assert_cmphex(qemu_opt_get_size(opts, "size1", 1), + ==, 0xfffffffffffff800); + g_assert_cmphex(qemu_opt_get_size(opts, "size2", 1), + ==, 0xfffffffffffff800); + + /* Beyond limits */ + opts = qemu_opts_parse(&opts_list_02, "size1=-1", false, &err); + error_free_or_abort(&err); + g_assert(!opts); + opts = qemu_opts_parse(&opts_list_02, + "size1=18446744073709550592", /* fffffffffffffc00 */ + false, &err); + error_free_or_abort(&err); + g_assert(!opts); + + /* Suffixes */ + opts = qemu_opts_parse(&opts_list_02, "size1=8b,size2=1.5k,size3=2M", + false, &error_abort); + g_assert_cmpuint(opts_count(opts), ==, 3); + g_assert_cmphex(qemu_opt_get_size(opts, "size1", 0), ==, 8); + g_assert_cmphex(qemu_opt_get_size(opts, "size2", 0), ==, 1536); + g_assert_cmphex(qemu_opt_get_size(opts, "size3", 0), ==, 2 * MiB); + opts = qemu_opts_parse(&opts_list_02, "size1=0.1G,size2=16777215T", + false, &error_abort); + g_assert_cmpuint(opts_count(opts), ==, 2); + g_assert_cmphex(qemu_opt_get_size(opts, "size1", 0), ==, GiB / 10); + g_assert_cmphex(qemu_opt_get_size(opts, "size2", 0), ==, 16777215ULL * TiB); + + /* Beyond limit with suffix */ + opts = qemu_opts_parse(&opts_list_02, "size1=16777216T", + false, &err); + error_free_or_abort(&err); + g_assert(!opts); + + /* Trailing crap */ + opts = qemu_opts_parse(&opts_list_02, "size1=16E", false, &err); + error_free_or_abort(&err); + g_assert(!opts); + opts = qemu_opts_parse(&opts_list_02, "size1=16Gi", false, &err); + error_free_or_abort(&err); + g_assert(!opts); + + qemu_opts_reset(&opts_list_02); +} + +static void test_has_help_option(void) +{ + static const struct { + const char *params; + /* expected value of qemu_opt_has_help_opt() with implied=false */ + bool expect; + /* expected value of qemu_opt_has_help_opt() with implied=true */ + bool expect_implied; + } test[] = { + { "help", true, false }, + { "?", true, false }, + { "helpme", false, false }, + { "?me", false, false }, + { "a,help", true, true }, + { "a,?", true, true }, + { "a=0,help,b", true, true }, + { "a=0,?,b", true, true }, + { "help,b=1", true, false }, + { "?,b=1", true, false }, + { "a,b,,help", true, true }, + { "a,b,,?", true, true }, + }; + int i; + QemuOpts *opts; + + for (i = 0; i < ARRAY_SIZE(test); i++) { + g_assert_cmpint(has_help_option(test[i].params), + ==, test[i].expect); + opts = qemu_opts_parse(&opts_list_03, test[i].params, false, + &error_abort); + g_assert_cmpint(qemu_opt_has_help_opt(opts), + ==, test[i].expect); + qemu_opts_del(opts); + opts = qemu_opts_parse(&opts_list_03, test[i].params, true, + &error_abort); + g_assert_cmpint(qemu_opt_has_help_opt(opts), + ==, test[i].expect_implied); + qemu_opts_del(opts); + } +} + +static void append_verify_list_01(QemuOptDesc *desc, bool with_overlapping) +{ + int i = 0; + + if (with_overlapping) { + g_assert_cmpstr(desc[i].name, ==, "str1"); + g_assert_cmpint(desc[i].type, ==, QEMU_OPT_STRING); + g_assert_cmpstr(desc[i].help, ==, + "Help texts are preserved in qemu_opts_append"); + g_assert_cmpstr(desc[i].def_value_str, ==, "default"); + i++; + + g_assert_cmpstr(desc[i].name, ==, "str2"); + g_assert_cmpint(desc[i].type, ==, QEMU_OPT_STRING); + g_assert_cmpstr(desc[i].help, ==, NULL); + g_assert_cmpstr(desc[i].def_value_str, ==, NULL); + i++; + } + + g_assert_cmpstr(desc[i].name, ==, "str3"); + g_assert_cmpint(desc[i].type, ==, QEMU_OPT_STRING); + g_assert_cmpstr(desc[i].help, ==, NULL); + g_assert_cmpstr(desc[i].def_value_str, ==, NULL); + i++; + + g_assert_cmpstr(desc[i].name, ==, "number1"); + g_assert_cmpint(desc[i].type, ==, QEMU_OPT_NUMBER); + g_assert_cmpstr(desc[i].help, ==, + "Having help texts only for some options is okay"); + g_assert_cmpstr(desc[i].def_value_str, ==, NULL); + i++; + + g_assert_cmpstr(desc[i].name, ==, "number2"); + g_assert_cmpint(desc[i].type, ==, QEMU_OPT_NUMBER); + g_assert_cmpstr(desc[i].help, ==, NULL); + g_assert_cmpstr(desc[i].def_value_str, ==, NULL); + i++; + + g_assert_cmpstr(desc[i].name, ==, NULL); +} + +static void append_verify_list_02(QemuOptDesc *desc) +{ + int i = 0; + + g_assert_cmpstr(desc[i].name, ==, "str1"); + g_assert_cmpint(desc[i].type, ==, QEMU_OPT_STRING); + g_assert_cmpstr(desc[i].help, ==, NULL); + g_assert_cmpstr(desc[i].def_value_str, ==, NULL); + i++; + + g_assert_cmpstr(desc[i].name, ==, "str2"); + g_assert_cmpint(desc[i].type, ==, QEMU_OPT_STRING); + g_assert_cmpstr(desc[i].help, ==, NULL); + g_assert_cmpstr(desc[i].def_value_str, ==, NULL); + i++; + + g_assert_cmpstr(desc[i].name, ==, "bool1"); + g_assert_cmpint(desc[i].type, ==, QEMU_OPT_BOOL); + g_assert_cmpstr(desc[i].help, ==, NULL); + g_assert_cmpstr(desc[i].def_value_str, ==, NULL); + i++; + + g_assert_cmpstr(desc[i].name, ==, "bool2"); + g_assert_cmpint(desc[i].type, ==, QEMU_OPT_BOOL); + g_assert_cmpstr(desc[i].help, ==, NULL); + g_assert_cmpstr(desc[i].def_value_str, ==, NULL); + i++; + + g_assert_cmpstr(desc[i].name, ==, "size1"); + g_assert_cmpint(desc[i].type, ==, QEMU_OPT_SIZE); + g_assert_cmpstr(desc[i].help, ==, NULL); + g_assert_cmpstr(desc[i].def_value_str, ==, NULL); + i++; + + g_assert_cmpstr(desc[i].name, ==, "size2"); + g_assert_cmpint(desc[i].type, ==, QEMU_OPT_SIZE); + g_assert_cmpstr(desc[i].help, ==, NULL); + g_assert_cmpstr(desc[i].def_value_str, ==, NULL); + i++; + + g_assert_cmpstr(desc[i].name, ==, "size3"); + g_assert_cmpint(desc[i].type, ==, QEMU_OPT_SIZE); + g_assert_cmpstr(desc[i].help, ==, NULL); + g_assert_cmpstr(desc[i].def_value_str, ==, NULL); +} + +static void test_opts_append_to_null(void) +{ + QemuOptsList *merged; + + merged = qemu_opts_append(NULL, &opts_list_01); + g_assert(merged != &opts_list_01); + + g_assert_cmpstr(merged->name, ==, NULL); + g_assert_cmpstr(merged->implied_opt_name, ==, NULL); + g_assert_false(merged->merge_lists); + + append_verify_list_01(merged->desc, true); + + qemu_opts_free(merged); +} + +static void test_opts_append(void) +{ + QemuOptsList *first, *merged; + + first = qemu_opts_append(NULL, &opts_list_02); + merged = qemu_opts_append(first, &opts_list_01); + g_assert(first != &opts_list_02); + g_assert(merged != &opts_list_01); + + g_assert_cmpstr(merged->name, ==, NULL); + g_assert_cmpstr(merged->implied_opt_name, ==, NULL); + g_assert_false(merged->merge_lists); + + append_verify_list_02(&merged->desc[0]); + append_verify_list_01(&merged->desc[7], false); + + qemu_opts_free(merged); +} + +static void test_opts_to_qdict_basic(void) +{ + QemuOpts *opts; + QDict *dict; + + opts = qemu_opts_parse(&opts_list_01, "str1=foo,str2=,str3=bar,number1=42", + false, &error_abort); + g_assert(opts != NULL); + + dict = qemu_opts_to_qdict(opts, NULL); + g_assert(dict != NULL); + + g_assert_cmpstr(qdict_get_str(dict, "str1"), ==, "foo"); + g_assert_cmpstr(qdict_get_str(dict, "str2"), ==, ""); + g_assert_cmpstr(qdict_get_str(dict, "str3"), ==, "bar"); + g_assert_cmpstr(qdict_get_str(dict, "number1"), ==, "42"); + g_assert_false(qdict_haskey(dict, "number2")); + + qobject_unref(dict); + qemu_opts_del(opts); +} + +static void test_opts_to_qdict_filtered(void) +{ + QemuOptsList *first, *merged; + QemuOpts *opts; + QDict *dict; + + first = qemu_opts_append(NULL, &opts_list_02); + merged = qemu_opts_append(first, &opts_list_01); + + opts = qemu_opts_parse(merged, + "str1=foo,str2=,str3=bar,bool1=off,number1=42", + false, &error_abort); + g_assert(opts != NULL); + + /* Convert to QDict without deleting from opts */ + dict = qemu_opts_to_qdict_filtered(opts, NULL, &opts_list_01, false); + g_assert(dict != NULL); + g_assert_cmpstr(qdict_get_str(dict, "str1"), ==, "foo"); + g_assert_cmpstr(qdict_get_str(dict, "str2"), ==, ""); + g_assert_cmpstr(qdict_get_str(dict, "str3"), ==, "bar"); + g_assert_cmpstr(qdict_get_str(dict, "number1"), ==, "42"); + g_assert_false(qdict_haskey(dict, "number2")); + g_assert_false(qdict_haskey(dict, "bool1")); + qobject_unref(dict); + + dict = qemu_opts_to_qdict_filtered(opts, NULL, &opts_list_02, false); + g_assert(dict != NULL); + g_assert_cmpstr(qdict_get_str(dict, "str1"), ==, "foo"); + g_assert_cmpstr(qdict_get_str(dict, "str2"), ==, ""); + g_assert_cmpstr(qdict_get_str(dict, "bool1"), ==, "off"); + g_assert_false(qdict_haskey(dict, "str3")); + g_assert_false(qdict_haskey(dict, "number1")); + g_assert_false(qdict_haskey(dict, "number2")); + qobject_unref(dict); + + /* Now delete converted options from opts */ + dict = qemu_opts_to_qdict_filtered(opts, NULL, &opts_list_01, true); + g_assert(dict != NULL); + g_assert_cmpstr(qdict_get_str(dict, "str1"), ==, "foo"); + g_assert_cmpstr(qdict_get_str(dict, "str2"), ==, ""); + g_assert_cmpstr(qdict_get_str(dict, "str3"), ==, "bar"); + g_assert_cmpstr(qdict_get_str(dict, "number1"), ==, "42"); + g_assert_false(qdict_haskey(dict, "number2")); + g_assert_false(qdict_haskey(dict, "bool1")); + qobject_unref(dict); + + dict = qemu_opts_to_qdict_filtered(opts, NULL, &opts_list_02, true); + g_assert(dict != NULL); + g_assert_cmpstr(qdict_get_str(dict, "bool1"), ==, "off"); + g_assert_false(qdict_haskey(dict, "str1")); + g_assert_false(qdict_haskey(dict, "str2")); + g_assert_false(qdict_haskey(dict, "str3")); + g_assert_false(qdict_haskey(dict, "number1")); + g_assert_false(qdict_haskey(dict, "number2")); + qobject_unref(dict); + + g_assert_true(QTAILQ_EMPTY(&opts->head)); + + qemu_opts_del(opts); + qemu_opts_free(merged); +} + +static void test_opts_to_qdict_duplicates(void) +{ + QemuOpts *opts; + QemuOpt *opt; + QDict *dict; + + opts = qemu_opts_parse(&opts_list_03, "foo=a,foo=b", false, &error_abort); + g_assert(opts != NULL); + + /* Verify that opts has two options with the same name */ + opt = QTAILQ_FIRST(&opts->head); + g_assert_cmpstr(opt->name, ==, "foo"); + g_assert_cmpstr(opt->str , ==, "a"); + + opt = QTAILQ_NEXT(opt, next); + g_assert_cmpstr(opt->name, ==, "foo"); + g_assert_cmpstr(opt->str , ==, "b"); + + opt = QTAILQ_NEXT(opt, next); + g_assert(opt == NULL); + + /* In the conversion to QDict, the last one wins */ + dict = qemu_opts_to_qdict(opts, NULL); + g_assert(dict != NULL); + g_assert_cmpstr(qdict_get_str(dict, "foo"), ==, "b"); + qobject_unref(dict); + + /* The last one still wins if entries are deleted, and both are deleted */ + dict = qemu_opts_to_qdict_filtered(opts, NULL, NULL, true); + g_assert(dict != NULL); + g_assert_cmpstr(qdict_get_str(dict, "foo"), ==, "b"); + qobject_unref(dict); + + g_assert_true(QTAILQ_EMPTY(&opts->head)); + + qemu_opts_del(opts); +} + +int main(int argc, char *argv[]) +{ + register_opts(); + g_test_init(&argc, &argv, NULL); + g_test_add_func("/qemu-opts/find_unknown_opts", test_find_unknown_opts); + g_test_add_func("/qemu-opts/find_opts", test_qemu_find_opts); + g_test_add_func("/qemu-opts/opts_create", test_qemu_opts_create); + g_test_add_func("/qemu-opts/opt_get", test_qemu_opt_get); + g_test_add_func("/qemu-opts/opt_get_bool", test_qemu_opt_get_bool); + g_test_add_func("/qemu-opts/opt_get_number", test_qemu_opt_get_number); + g_test_add_func("/qemu-opts/opt_get_size", test_qemu_opt_get_size); + g_test_add_func("/qemu-opts/opt_unset", test_qemu_opt_unset); + g_test_add_func("/qemu-opts/opts_reset", test_qemu_opts_reset); + g_test_add_func("/qemu-opts/opts_set", test_qemu_opts_set); + g_test_add_func("/qemu-opts/opts_parse/general", test_opts_parse); + g_test_add_func("/qemu-opts/opts_parse/bool", test_opts_parse_bool); + g_test_add_func("/qemu-opts/opts_parse/number", test_opts_parse_number); + g_test_add_func("/qemu-opts/opts_parse/size", test_opts_parse_size); + g_test_add_func("/qemu-opts/has_help_option", test_has_help_option); + g_test_add_func("/qemu-opts/append_to_null", test_opts_append_to_null); + g_test_add_func("/qemu-opts/append", test_opts_append); + g_test_add_func("/qemu-opts/to_qdict/basic", test_opts_to_qdict_basic); + g_test_add_func("/qemu-opts/to_qdict/filtered", test_opts_to_qdict_filtered); + g_test_add_func("/qemu-opts/to_qdict/duplicates", test_opts_to_qdict_duplicates); + g_test_run(); + return 0; +} diff --git a/tests/unit/test-qga.c b/tests/unit/test-qga.c new file mode 100644 index 0000000000..5cb140d1b5 --- /dev/null +++ b/tests/unit/test-qga.c @@ -0,0 +1,1019 @@ +#include "qemu/osdep.h" +#include +#include +#include +#include + +#include "../qtest/libqos/libqtest.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qmp/qlist.h" + +typedef struct { + char *test_dir; + GMainLoop *loop; + int fd; + GPid pid; +} TestFixture; + +static int connect_qga(char *path) +{ + int s, ret, len, i = 0; + struct sockaddr_un remote; + + s = socket(AF_UNIX, SOCK_STREAM, 0); + g_assert(s != -1); + + remote.sun_family = AF_UNIX; + do { + strcpy(remote.sun_path, path); + len = strlen(remote.sun_path) + sizeof(remote.sun_family); + ret = connect(s, (struct sockaddr *)&remote, len); + if (ret == -1) { + g_usleep(G_USEC_PER_SEC); + } + if (i++ == 10) { + return -1; + } + } while (ret == -1); + + return s; +} + +static void qga_watch(GPid pid, gint status, gpointer user_data) +{ + TestFixture *fixture = user_data; + + g_assert_cmpint(status, ==, 0); + g_main_loop_quit(fixture->loop); +} + +static void +fixture_setup(TestFixture *fixture, gconstpointer data, gchar **envp) +{ + const gchar *extra_arg = data; + GError *error = NULL; + gchar *cwd, *path, *cmd, **argv = NULL; + + fixture->loop = g_main_loop_new(NULL, FALSE); + + fixture->test_dir = g_strdup("/tmp/qgatest.XXXXXX"); + g_assert_nonnull(mkdtemp(fixture->test_dir)); + + path = g_build_filename(fixture->test_dir, "sock", NULL); + cwd = g_get_current_dir(); + cmd = g_strdup_printf("%s%cqga%cqemu-ga -m unix-listen -t %s -p %s %s %s", + cwd, G_DIR_SEPARATOR, G_DIR_SEPARATOR, + fixture->test_dir, path, + getenv("QTEST_LOG") ? "-v" : "", + extra_arg ?: ""); + g_shell_parse_argv(cmd, NULL, &argv, &error); + g_assert_no_error(error); + + g_spawn_async(fixture->test_dir, argv, envp, + G_SPAWN_SEARCH_PATH|G_SPAWN_DO_NOT_REAP_CHILD, + NULL, NULL, &fixture->pid, &error); + g_assert_no_error(error); + + g_child_watch_add(fixture->pid, qga_watch, fixture); + + fixture->fd = connect_qga(path); + g_assert_cmpint(fixture->fd, !=, -1); + + g_strfreev(argv); + g_free(cmd); + g_free(cwd); + g_free(path); +} + +static void +fixture_tear_down(TestFixture *fixture, gconstpointer data) +{ + gchar *tmp; + + kill(fixture->pid, SIGTERM); + + g_main_loop_run(fixture->loop); + g_main_loop_unref(fixture->loop); + + g_spawn_close_pid(fixture->pid); + + tmp = g_build_filename(fixture->test_dir, "foo", NULL); + g_unlink(tmp); + g_free(tmp); + + tmp = g_build_filename(fixture->test_dir, "qga.state", NULL); + g_unlink(tmp); + g_free(tmp); + + tmp = g_build_filename(fixture->test_dir, "sock", NULL); + g_unlink(tmp); + g_free(tmp); + + g_rmdir(fixture->test_dir); + g_free(fixture->test_dir); + close(fixture->fd); +} + +static void qmp_assertion_message_error(const char *domain, + const char *file, + int line, + const char *func, + const char *expr, + QDict *dict) +{ + const char *class, *desc; + char *s; + QDict *error; + + error = qdict_get_qdict(dict, "error"); + class = qdict_get_try_str(error, "class"); + desc = qdict_get_try_str(error, "desc"); + + s = g_strdup_printf("assertion failed %s: %s %s", expr, class, desc); + g_assertion_message(domain, file, line, func, s); + g_free(s); +} + +#define qmp_assert_no_error(err) do { \ + if (qdict_haskey(err, "error")) { \ + qmp_assertion_message_error(G_LOG_DOMAIN, __FILE__, __LINE__, \ + G_STRFUNC, #err, err); \ + } \ +} while (0) + +static void test_qga_sync_delimited(gconstpointer fix) +{ + const TestFixture *fixture = fix; + guint32 v, r = g_test_rand_int(); + unsigned char c; + QDict *ret; + + qmp_fd_send_raw(fixture->fd, "\xff"); + qmp_fd_send(fixture->fd, + "{'execute': 'guest-sync-delimited'," + " 'arguments': {'id': %u } }", + r); + + /* + * Read and ignore garbage until resynchronized. + * + * Note that the full reset sequence would involve checking the + * response of guest-sync-delimited and repeating the loop if + * 'id' field of the response does not match the 'id' field of + * the request. Testing this fully would require inserting + * garbage in the response stream and is left as a future test + * to implement. + * + * TODO: The server shouldn't emit so much garbage (among other + * things, it loudly complains about the client's \xff being + * invalid JSON, even though it is a documented part of the + * handshake. + */ + do { + v = read(fixture->fd, &c, 1); + g_assert_cmpint(v, ==, 1); + } while (c != 0xff); + + ret = qmp_fd_receive(fixture->fd); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + + v = qdict_get_int(ret, "return"); + g_assert_cmpint(r, ==, v); + + qobject_unref(ret); +} + +static void test_qga_sync(gconstpointer fix) +{ + const TestFixture *fixture = fix; + guint32 v, r = g_test_rand_int(); + QDict *ret; + + /* + * TODO guest-sync is inherently limited: we cannot distinguish + * failure caused by reacting to garbage on the wire prior to this + * command, from failure of this actual command. Clients are + * supposed to be able to send a raw '\xff' byte to at least + * re-synchronize the server's parser prior to this command, but + * we are not in a position to test that here because (at least + * for now) it causes the server to issue an error message about + * invalid JSON. Testing of '\xff' handling is done in + * guest-sync-delimited instead. + */ + ret = qmp_fd(fixture->fd, + "{'execute': 'guest-sync', 'arguments': {'id': %u } }", + r); + + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + + v = qdict_get_int(ret, "return"); + g_assert_cmpint(r, ==, v); + + qobject_unref(ret); +} + +static void test_qga_ping(gconstpointer fix) +{ + const TestFixture *fixture = fix; + QDict *ret; + + ret = qmp_fd(fixture->fd, "{'execute': 'guest-ping'}"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + + qobject_unref(ret); +} + +static void test_qga_id(gconstpointer fix) +{ + const TestFixture *fixture = fix; + QDict *ret; + + ret = qmp_fd(fixture->fd, "{'execute': 'guest-ping', 'id': 1}"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + g_assert_cmpint(qdict_get_int(ret, "id"), ==, 1); + + qobject_unref(ret); +} + +static void test_qga_invalid_oob(gconstpointer fix) +{ + const TestFixture *fixture = fix; + QDict *ret; + + ret = qmp_fd(fixture->fd, "{'exec-oob': 'guest-ping'}"); + g_assert_nonnull(ret); + + qmp_expect_error_and_unref(ret, "GenericError"); +} + +static void test_qga_invalid_args(gconstpointer fix) +{ + const TestFixture *fixture = fix; + QDict *ret, *error; + const gchar *class, *desc; + + ret = qmp_fd(fixture->fd, "{'execute': 'guest-ping', " + "'arguments': {'foo': 42 }}"); + g_assert_nonnull(ret); + + error = qdict_get_qdict(ret, "error"); + class = qdict_get_try_str(error, "class"); + desc = qdict_get_try_str(error, "desc"); + + g_assert_cmpstr(class, ==, "GenericError"); + g_assert_cmpstr(desc, ==, "Parameter 'foo' is unexpected"); + + qobject_unref(ret); +} + +static void test_qga_invalid_cmd(gconstpointer fix) +{ + const TestFixture *fixture = fix; + QDict *ret, *error; + const gchar *class, *desc; + + ret = qmp_fd(fixture->fd, "{'execute': 'guest-invalid-cmd'}"); + g_assert_nonnull(ret); + + error = qdict_get_qdict(ret, "error"); + class = qdict_get_try_str(error, "class"); + desc = qdict_get_try_str(error, "desc"); + + g_assert_cmpstr(class, ==, "CommandNotFound"); + g_assert_cmpint(strlen(desc), >, 0); + + qobject_unref(ret); +} + +static void test_qga_info(gconstpointer fix) +{ + const TestFixture *fixture = fix; + QDict *ret, *val; + const gchar *version; + + ret = qmp_fd(fixture->fd, "{'execute': 'guest-info'}"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + + val = qdict_get_qdict(ret, "return"); + version = qdict_get_try_str(val, "version"); + g_assert_cmpstr(version, ==, QEMU_VERSION); + + qobject_unref(ret); +} + +static void test_qga_get_vcpus(gconstpointer fix) +{ + const TestFixture *fixture = fix; + QDict *ret; + QList *list; + const QListEntry *entry; + + ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-vcpus'}"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + + /* check there is at least a cpu */ + list = qdict_get_qlist(ret, "return"); + entry = qlist_first(list); + g_assert(qdict_haskey(qobject_to(QDict, entry->value), "online")); + g_assert(qdict_haskey(qobject_to(QDict, entry->value), "logical-id")); + + qobject_unref(ret); +} + +static void test_qga_get_fsinfo(gconstpointer fix) +{ + const TestFixture *fixture = fix; + QDict *ret; + QList *list; + const QListEntry *entry; + + ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-fsinfo'}"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + + /* sanity-check the response if there are any filesystems */ + list = qdict_get_qlist(ret, "return"); + entry = qlist_first(list); + if (entry) { + g_assert(qdict_haskey(qobject_to(QDict, entry->value), "name")); + g_assert(qdict_haskey(qobject_to(QDict, entry->value), "mountpoint")); + g_assert(qdict_haskey(qobject_to(QDict, entry->value), "type")); + g_assert(qdict_haskey(qobject_to(QDict, entry->value), "disk")); + } + + qobject_unref(ret); +} + +static void test_qga_get_memory_block_info(gconstpointer fix) +{ + const TestFixture *fixture = fix; + QDict *ret, *val; + int64_t size; + + ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-memory-block-info'}"); + g_assert_nonnull(ret); + + /* some systems might not expose memory block info in sysfs */ + if (!qdict_haskey(ret, "error")) { + /* check there is at least some memory */ + val = qdict_get_qdict(ret, "return"); + size = qdict_get_int(val, "size"); + g_assert_cmpint(size, >, 0); + } + + qobject_unref(ret); +} + +static void test_qga_get_memory_blocks(gconstpointer fix) +{ + const TestFixture *fixture = fix; + QDict *ret; + QList *list; + const QListEntry *entry; + + ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-memory-blocks'}"); + g_assert_nonnull(ret); + + /* some systems might not expose memory block info in sysfs */ + if (!qdict_haskey(ret, "error")) { + list = qdict_get_qlist(ret, "return"); + entry = qlist_first(list); + /* newer versions of qga may return empty list without error */ + if (entry) { + g_assert(qdict_haskey(qobject_to(QDict, entry->value), + "phys-index")); + g_assert(qdict_haskey(qobject_to(QDict, entry->value), "online")); + } + } + + qobject_unref(ret); +} + +static void test_qga_network_get_interfaces(gconstpointer fix) +{ + const TestFixture *fixture = fix; + QDict *ret; + QList *list; + const QListEntry *entry; + + ret = qmp_fd(fixture->fd, "{'execute': 'guest-network-get-interfaces'}"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + + /* check there is at least an interface */ + list = qdict_get_qlist(ret, "return"); + entry = qlist_first(list); + g_assert(qdict_haskey(qobject_to(QDict, entry->value), "name")); + + qobject_unref(ret); +} + +static void test_qga_file_ops(gconstpointer fix) +{ + const TestFixture *fixture = fix; + const unsigned char helloworld[] = "Hello World!\n"; + const char *b64; + gchar *path, *enc; + unsigned char *dec; + QDict *ret, *val; + int64_t id, eof; + gsize count; + FILE *f; + char tmp[100]; + + /* open */ + ret = qmp_fd(fixture->fd, "{'execute': 'guest-file-open'," + " 'arguments': { 'path': 'foo', 'mode': 'w+' } }"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + id = qdict_get_int(ret, "return"); + qobject_unref(ret); + + enc = g_base64_encode(helloworld, sizeof(helloworld)); + /* write */ + ret = qmp_fd(fixture->fd, + "{'execute': 'guest-file-write'," + " 'arguments': { 'handle': %" PRId64 ", 'buf-b64': %s } }", + id, enc); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + + val = qdict_get_qdict(ret, "return"); + count = qdict_get_int(val, "count"); + eof = qdict_get_bool(val, "eof"); + g_assert_cmpint(count, ==, sizeof(helloworld)); + g_assert_cmpint(eof, ==, 0); + qobject_unref(ret); + + /* flush */ + ret = qmp_fd(fixture->fd, + "{'execute': 'guest-file-flush'," + " 'arguments': {'handle': %" PRId64 "} }", + id); + qobject_unref(ret); + + /* close */ + ret = qmp_fd(fixture->fd, + "{'execute': 'guest-file-close'," + " 'arguments': {'handle': %" PRId64 "} }", + id); + qobject_unref(ret); + + /* check content */ + path = g_build_filename(fixture->test_dir, "foo", NULL); + f = fopen(path, "r"); + g_free(path); + g_assert_nonnull(f); + count = fread(tmp, 1, sizeof(tmp), f); + g_assert_cmpint(count, ==, sizeof(helloworld)); + tmp[count] = 0; + g_assert_cmpstr(tmp, ==, (char *)helloworld); + fclose(f); + + /* open */ + ret = qmp_fd(fixture->fd, "{'execute': 'guest-file-open'," + " 'arguments': { 'path': 'foo', 'mode': 'r' } }"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + id = qdict_get_int(ret, "return"); + qobject_unref(ret); + + /* read */ + ret = qmp_fd(fixture->fd, + "{'execute': 'guest-file-read'," + " 'arguments': { 'handle': %" PRId64 "} }", + id); + val = qdict_get_qdict(ret, "return"); + count = qdict_get_int(val, "count"); + eof = qdict_get_bool(val, "eof"); + b64 = qdict_get_str(val, "buf-b64"); + g_assert_cmpint(count, ==, sizeof(helloworld)); + g_assert(eof); + g_assert_cmpstr(b64, ==, enc); + + qobject_unref(ret); + g_free(enc); + + /* read eof */ + ret = qmp_fd(fixture->fd, + "{'execute': 'guest-file-read'," + " 'arguments': { 'handle': %" PRId64 "} }", + id); + val = qdict_get_qdict(ret, "return"); + count = qdict_get_int(val, "count"); + eof = qdict_get_bool(val, "eof"); + b64 = qdict_get_str(val, "buf-b64"); + g_assert_cmpint(count, ==, 0); + g_assert(eof); + g_assert_cmpstr(b64, ==, ""); + qobject_unref(ret); + + /* seek */ + ret = qmp_fd(fixture->fd, + "{'execute': 'guest-file-seek'," + " 'arguments': { 'handle': %" PRId64 ", " + " 'offset': %d, 'whence': %s } }", + id, 6, "set"); + qmp_assert_no_error(ret); + val = qdict_get_qdict(ret, "return"); + count = qdict_get_int(val, "position"); + eof = qdict_get_bool(val, "eof"); + g_assert_cmpint(count, ==, 6); + g_assert(!eof); + qobject_unref(ret); + + /* partial read */ + ret = qmp_fd(fixture->fd, + "{'execute': 'guest-file-read'," + " 'arguments': { 'handle': %" PRId64 "} }", + id); + val = qdict_get_qdict(ret, "return"); + count = qdict_get_int(val, "count"); + eof = qdict_get_bool(val, "eof"); + b64 = qdict_get_str(val, "buf-b64"); + g_assert_cmpint(count, ==, sizeof(helloworld) - 6); + g_assert(eof); + dec = g_base64_decode(b64, &count); + g_assert_cmpint(count, ==, sizeof(helloworld) - 6); + g_assert_cmpmem(dec, count, helloworld + 6, sizeof(helloworld) - 6); + g_free(dec); + + qobject_unref(ret); + + /* close */ + ret = qmp_fd(fixture->fd, + "{'execute': 'guest-file-close'," + " 'arguments': {'handle': %" PRId64 "} }", + id); + qobject_unref(ret); +} + +static void test_qga_file_write_read(gconstpointer fix) +{ + const TestFixture *fixture = fix; + const unsigned char helloworld[] = "Hello World!\n"; + const char *b64; + gchar *enc; + QDict *ret, *val; + int64_t id, eof; + gsize count; + + /* open */ + ret = qmp_fd(fixture->fd, "{'execute': 'guest-file-open'," + " 'arguments': { 'path': 'foo', 'mode': 'w+' } }"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + id = qdict_get_int(ret, "return"); + qobject_unref(ret); + + enc = g_base64_encode(helloworld, sizeof(helloworld)); + /* write */ + ret = qmp_fd(fixture->fd, + "{'execute': 'guest-file-write'," + " 'arguments': { 'handle': %" PRId64 "," + " 'buf-b64': %s } }", id, enc); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + + val = qdict_get_qdict(ret, "return"); + count = qdict_get_int(val, "count"); + eof = qdict_get_bool(val, "eof"); + g_assert_cmpint(count, ==, sizeof(helloworld)); + g_assert_cmpint(eof, ==, 0); + qobject_unref(ret); + + /* read (check implicit flush) */ + ret = qmp_fd(fixture->fd, + "{'execute': 'guest-file-read'," + " 'arguments': { 'handle': %" PRId64 "} }", + id); + val = qdict_get_qdict(ret, "return"); + count = qdict_get_int(val, "count"); + eof = qdict_get_bool(val, "eof"); + b64 = qdict_get_str(val, "buf-b64"); + g_assert_cmpint(count, ==, 0); + g_assert(eof); + g_assert_cmpstr(b64, ==, ""); + qobject_unref(ret); + + /* seek to 0 */ + ret = qmp_fd(fixture->fd, + "{'execute': 'guest-file-seek'," + " 'arguments': { 'handle': %" PRId64 ", " + " 'offset': %d, 'whence': %s } }", + id, 0, "set"); + qmp_assert_no_error(ret); + val = qdict_get_qdict(ret, "return"); + count = qdict_get_int(val, "position"); + eof = qdict_get_bool(val, "eof"); + g_assert_cmpint(count, ==, 0); + g_assert(!eof); + qobject_unref(ret); + + /* read */ + ret = qmp_fd(fixture->fd, + "{'execute': 'guest-file-read'," + " 'arguments': { 'handle': %" PRId64 "} }", + id); + val = qdict_get_qdict(ret, "return"); + count = qdict_get_int(val, "count"); + eof = qdict_get_bool(val, "eof"); + b64 = qdict_get_str(val, "buf-b64"); + g_assert_cmpint(count, ==, sizeof(helloworld)); + g_assert(eof); + g_assert_cmpstr(b64, ==, enc); + qobject_unref(ret); + g_free(enc); + + /* close */ + ret = qmp_fd(fixture->fd, + "{'execute': 'guest-file-close'," + " 'arguments': {'handle': %" PRId64 "} }", + id); + qobject_unref(ret); +} + +static void test_qga_get_time(gconstpointer fix) +{ + const TestFixture *fixture = fix; + QDict *ret; + int64_t time; + + ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-time'}"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + + time = qdict_get_int(ret, "return"); + g_assert_cmpint(time, >, 0); + + qobject_unref(ret); +} + +static void test_qga_blacklist(gconstpointer data) +{ + TestFixture fix; + QDict *ret, *error; + const gchar *class, *desc; + + fixture_setup(&fix, "-b guest-ping,guest-get-time", NULL); + + /* check blacklist */ + ret = qmp_fd(fix.fd, "{'execute': 'guest-ping'}"); + g_assert_nonnull(ret); + error = qdict_get_qdict(ret, "error"); + class = qdict_get_try_str(error, "class"); + desc = qdict_get_try_str(error, "desc"); + g_assert_cmpstr(class, ==, "CommandNotFound"); + g_assert_nonnull(g_strstr_len(desc, -1, "has been disabled")); + qobject_unref(ret); + + ret = qmp_fd(fix.fd, "{'execute': 'guest-get-time'}"); + g_assert_nonnull(ret); + error = qdict_get_qdict(ret, "error"); + class = qdict_get_try_str(error, "class"); + desc = qdict_get_try_str(error, "desc"); + g_assert_cmpstr(class, ==, "CommandNotFound"); + g_assert_nonnull(g_strstr_len(desc, -1, "has been disabled")); + qobject_unref(ret); + + /* check something work */ + ret = qmp_fd(fix.fd, "{'execute': 'guest-get-fsinfo'}"); + qmp_assert_no_error(ret); + qobject_unref(ret); + + fixture_tear_down(&fix, NULL); +} + +static void test_qga_config(gconstpointer data) +{ + GError *error = NULL; + char *cwd, *cmd, *out, *err, *str, **strv, **argv = NULL; + char *env[2]; + int status; + gsize n; + GKeyFile *kf; + + cwd = g_get_current_dir(); + cmd = g_strdup_printf("%s%cqga%cqemu-ga -D", + cwd, G_DIR_SEPARATOR, G_DIR_SEPARATOR); + g_free(cwd); + g_shell_parse_argv(cmd, NULL, &argv, &error); + g_free(cmd); + g_assert_no_error(error); + + env[0] = g_strdup_printf("QGA_CONF=tests%cdata%ctest-qga-config", + G_DIR_SEPARATOR, G_DIR_SEPARATOR); + env[1] = NULL; + g_spawn_sync(NULL, argv, env, 0, + NULL, NULL, &out, &err, &status, &error); + g_strfreev(argv); + + g_assert_no_error(error); + g_assert_cmpstr(err, ==, ""); + g_assert_cmpint(status, ==, 0); + + kf = g_key_file_new(); + g_key_file_load_from_data(kf, out, -1, G_KEY_FILE_NONE, &error); + g_assert_no_error(error); + + str = g_key_file_get_start_group(kf); + g_assert_cmpstr(str, ==, "general"); + g_free(str); + + g_assert_false(g_key_file_get_boolean(kf, "general", "daemon", &error)); + g_assert_no_error(error); + + str = g_key_file_get_string(kf, "general", "method", &error); + g_assert_no_error(error); + g_assert_cmpstr(str, ==, "virtio-serial"); + g_free(str); + + str = g_key_file_get_string(kf, "general", "path", &error); + g_assert_no_error(error); + g_assert_cmpstr(str, ==, "/path/to/org.qemu.guest_agent.0"); + g_free(str); + + str = g_key_file_get_string(kf, "general", "pidfile", &error); + g_assert_no_error(error); + g_assert_cmpstr(str, ==, "/var/foo/qemu-ga.pid"); + g_free(str); + + str = g_key_file_get_string(kf, "general", "statedir", &error); + g_assert_no_error(error); + g_assert_cmpstr(str, ==, "/var/state"); + g_free(str); + + g_assert_true(g_key_file_get_boolean(kf, "general", "verbose", &error)); + g_assert_no_error(error); + + strv = g_key_file_get_string_list(kf, "general", "blacklist", &n, &error); + g_assert_cmpint(n, ==, 2); + g_assert_true(g_strv_contains((const char * const *)strv, + "guest-ping")); + g_assert_true(g_strv_contains((const char * const *)strv, + "guest-get-time")); + g_assert_no_error(error); + g_strfreev(strv); + + g_free(out); + g_free(err); + g_free(env[0]); + g_key_file_free(kf); +} + +static void test_qga_fsfreeze_status(gconstpointer fix) +{ + const TestFixture *fixture = fix; + QDict *ret; + const gchar *status; + + ret = qmp_fd(fixture->fd, "{'execute': 'guest-fsfreeze-status'}"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + + status = qdict_get_try_str(ret, "return"); + g_assert_cmpstr(status, ==, "thawed"); + + qobject_unref(ret); +} + +static void test_qga_guest_exec(gconstpointer fix) +{ + const TestFixture *fixture = fix; + QDict *ret, *val; + const gchar *out; + guchar *decoded; + int64_t pid, now, exitcode; + gsize len; + bool exited; + + /* exec 'echo foo bar' */ + ret = qmp_fd(fixture->fd, "{'execute': 'guest-exec', 'arguments': {" + " 'path': '/bin/echo', 'arg': [ '-n', '\" test_str \"' ]," + " 'capture-output': true } }"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + val = qdict_get_qdict(ret, "return"); + pid = qdict_get_int(val, "pid"); + g_assert_cmpint(pid, >, 0); + qobject_unref(ret); + + /* wait for completion */ + now = g_get_monotonic_time(); + do { + ret = qmp_fd(fixture->fd, + "{'execute': 'guest-exec-status'," + " 'arguments': { 'pid': %" PRId64 " } }", pid); + g_assert_nonnull(ret); + val = qdict_get_qdict(ret, "return"); + exited = qdict_get_bool(val, "exited"); + if (!exited) { + qobject_unref(ret); + } + } while (!exited && + g_get_monotonic_time() < now + 5 * G_TIME_SPAN_SECOND); + g_assert(exited); + + /* check stdout */ + exitcode = qdict_get_int(val, "exitcode"); + g_assert_cmpint(exitcode, ==, 0); + out = qdict_get_str(val, "out-data"); + decoded = g_base64_decode(out, &len); + g_assert_cmpint(len, ==, 12); + g_assert_cmpstr((char *)decoded, ==, "\" test_str \""); + g_free(decoded); + qobject_unref(ret); +} + +static void test_qga_guest_exec_invalid(gconstpointer fix) +{ + const TestFixture *fixture = fix; + QDict *ret, *error; + const gchar *class, *desc; + + /* invalid command */ + ret = qmp_fd(fixture->fd, "{'execute': 'guest-exec', 'arguments': {" + " 'path': '/bin/invalid-cmd42' } }"); + g_assert_nonnull(ret); + error = qdict_get_qdict(ret, "error"); + g_assert_nonnull(error); + class = qdict_get_str(error, "class"); + desc = qdict_get_str(error, "desc"); + g_assert_cmpstr(class, ==, "GenericError"); + g_assert_cmpint(strlen(desc), >, 0); + qobject_unref(ret); + + /* invalid pid */ + ret = qmp_fd(fixture->fd, "{'execute': 'guest-exec-status'," + " 'arguments': { 'pid': 0 } }"); + g_assert_nonnull(ret); + error = qdict_get_qdict(ret, "error"); + g_assert_nonnull(error); + class = qdict_get_str(error, "class"); + desc = qdict_get_str(error, "desc"); + g_assert_cmpstr(class, ==, "GenericError"); + g_assert_cmpint(strlen(desc), >, 0); + qobject_unref(ret); +} + +static void test_qga_guest_get_host_name(gconstpointer fix) +{ + const TestFixture *fixture = fix; + QDict *ret, *val; + + ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-host-name'}"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + + val = qdict_get_qdict(ret, "return"); + g_assert(qdict_haskey(val, "host-name")); + + qobject_unref(ret); +} + +static void test_qga_guest_get_timezone(gconstpointer fix) +{ + const TestFixture *fixture = fix; + QDict *ret, *val; + + ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-timezone'}"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + + /* Make sure there's at least offset */ + val = qdict_get_qdict(ret, "return"); + g_assert(qdict_haskey(val, "offset")); + + qobject_unref(ret); +} + +static void test_qga_guest_get_users(gconstpointer fix) +{ + const TestFixture *fixture = fix; + QDict *ret; + QList *val; + + ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-users'}"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + + /* There is not much to test here */ + val = qdict_get_qlist(ret, "return"); + g_assert_nonnull(val); + + qobject_unref(ret); +} + +static void test_qga_guest_get_osinfo(gconstpointer data) +{ + TestFixture fixture; + const gchar *str; + gchar *cwd, *env[2]; + QDict *ret, *val; + + cwd = g_get_current_dir(); + env[0] = g_strdup_printf( + "QGA_OS_RELEASE=%s%ctests%cdata%ctest-qga-os-release", + cwd, G_DIR_SEPARATOR, G_DIR_SEPARATOR, G_DIR_SEPARATOR); + env[1] = NULL; + g_free(cwd); + fixture_setup(&fixture, NULL, env); + + ret = qmp_fd(fixture.fd, "{'execute': 'guest-get-osinfo'}"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + + val = qdict_get_qdict(ret, "return"); + + str = qdict_get_try_str(val, "id"); + g_assert_nonnull(str); + g_assert_cmpstr(str, ==, "qemu-ga-test"); + + str = qdict_get_try_str(val, "name"); + g_assert_nonnull(str); + g_assert_cmpstr(str, ==, "QEMU-GA"); + + str = qdict_get_try_str(val, "pretty-name"); + g_assert_nonnull(str); + g_assert_cmpstr(str, ==, "QEMU Guest Agent test"); + + str = qdict_get_try_str(val, "version"); + g_assert_nonnull(str); + g_assert_cmpstr(str, ==, "Test 1"); + + str = qdict_get_try_str(val, "version-id"); + g_assert_nonnull(str); + g_assert_cmpstr(str, ==, "1"); + + str = qdict_get_try_str(val, "variant"); + g_assert_nonnull(str); + g_assert_cmpstr(str, ==, "Unit test \"'$`\\ and \\\\ etc."); + + str = qdict_get_try_str(val, "variant-id"); + g_assert_nonnull(str); + g_assert_cmpstr(str, ==, "unit-test"); + + qobject_unref(ret); + g_free(env[0]); + fixture_tear_down(&fixture, NULL); +} + +int main(int argc, char **argv) +{ + TestFixture fix; + int ret; + + setlocale (LC_ALL, ""); + g_test_init(&argc, &argv, NULL); + fixture_setup(&fix, NULL, NULL); + + g_test_add_data_func("/qga/sync-delimited", &fix, test_qga_sync_delimited); + g_test_add_data_func("/qga/sync", &fix, test_qga_sync); + g_test_add_data_func("/qga/ping", &fix, test_qga_ping); + g_test_add_data_func("/qga/info", &fix, test_qga_info); + g_test_add_data_func("/qga/network-get-interfaces", &fix, + test_qga_network_get_interfaces); + if (!access("/sys/devices/system/cpu/cpu0", F_OK)) { + g_test_add_data_func("/qga/get-vcpus", &fix, test_qga_get_vcpus); + } + g_test_add_data_func("/qga/get-fsinfo", &fix, test_qga_get_fsinfo); + g_test_add_data_func("/qga/get-memory-block-info", &fix, + test_qga_get_memory_block_info); + g_test_add_data_func("/qga/get-memory-blocks", &fix, + test_qga_get_memory_blocks); + g_test_add_data_func("/qga/file-ops", &fix, test_qga_file_ops); + g_test_add_data_func("/qga/file-write-read", &fix, test_qga_file_write_read); + g_test_add_data_func("/qga/get-time", &fix, test_qga_get_time); + g_test_add_data_func("/qga/id", &fix, test_qga_id); + g_test_add_data_func("/qga/invalid-oob", &fix, test_qga_invalid_oob); + g_test_add_data_func("/qga/invalid-cmd", &fix, test_qga_invalid_cmd); + g_test_add_data_func("/qga/invalid-args", &fix, test_qga_invalid_args); + g_test_add_data_func("/qga/fsfreeze-status", &fix, + test_qga_fsfreeze_status); + + g_test_add_data_func("/qga/blacklist", NULL, test_qga_blacklist); + g_test_add_data_func("/qga/config", NULL, test_qga_config); + g_test_add_data_func("/qga/guest-exec", &fix, test_qga_guest_exec); + g_test_add_data_func("/qga/guest-exec-invalid", &fix, + test_qga_guest_exec_invalid); + g_test_add_data_func("/qga/guest-get-osinfo", &fix, + test_qga_guest_get_osinfo); + g_test_add_data_func("/qga/guest-get-host-name", &fix, + test_qga_guest_get_host_name); + g_test_add_data_func("/qga/guest-get-timezone", &fix, + test_qga_guest_get_timezone); + g_test_add_data_func("/qga/guest-get-users", &fix, + test_qga_guest_get_users); + + ret = g_test_run(); + + fixture_tear_down(&fix, NULL); + + return ret; +} diff --git a/tests/unit/test-qgraph.c b/tests/unit/test-qgraph.c new file mode 100644 index 0000000000..f819430e2c --- /dev/null +++ b/tests/unit/test-qgraph.c @@ -0,0 +1,434 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + */ + +#include "qemu/osdep.h" +#include "../qtest/libqos/qgraph.h" +#include "../qtest/libqos/qgraph_internal.h" + +#define MACHINE_PC "x86_64/pc" +#define MACHINE_RASPI2 "arm/raspi2" +#define I440FX "i440FX-pcihost" +#define PCIBUS_PC "pcibus-pc" +#define SDHCI "sdhci" +#define PCIBUS "pci-bus" +#define SDHCI_PCI "sdhci-pci" +#define SDHCI_MM "generic-sdhci" +#define REGISTER_TEST "register-test" + +int npath; + +static void *machinefunct(QTestState *qts) +{ + return NULL; +} + +static void *driverfunct(void *obj, QGuestAllocator *machine, void *arg) +{ + return NULL; +} + +static void testfunct(void *obj, void *arg, QGuestAllocator *alloc) +{ + return; +} + +static void check_interface(const char *interface) +{ + g_assert_cmpint(qos_graph_has_machine(interface), ==, FALSE); + g_assert_nonnull(qos_graph_get_node(interface)); + g_assert_cmpint(qos_graph_has_node(interface), ==, TRUE); + g_assert_cmpint(qos_graph_get_node_type(interface), ==, QNODE_INTERFACE); + qos_graph_node_set_availability(interface, TRUE); + g_assert_cmpint(qos_graph_get_node_availability(interface), ==, TRUE); +} + +static void check_machine(const char *machine) +{ + qos_node_create_machine(machine, machinefunct); + g_assert_nonnull(qos_graph_get_machine(machine)); + g_assert_cmpint(qos_graph_has_machine(machine), ==, TRUE); + g_assert_nonnull(qos_graph_get_node(machine)); + g_assert_cmpint(qos_graph_get_node_availability(machine), ==, FALSE); + qos_graph_node_set_availability(machine, TRUE); + g_assert_cmpint(qos_graph_get_node_availability(machine), ==, TRUE); + g_assert_cmpint(qos_graph_has_node(machine), ==, TRUE); + g_assert_cmpint(qos_graph_get_node_type(machine), ==, QNODE_MACHINE); +} + +static void check_contains(const char *machine, const char *driver) +{ + QOSGraphEdge *edge; + qos_node_contains(machine, driver, NULL); + + edge = qos_graph_get_edge(machine, driver); + g_assert_nonnull(edge); + g_assert_cmpint(qos_graph_edge_get_type(edge), ==, QEDGE_CONTAINS); + g_assert_cmpint(qos_graph_has_edge(machine, driver), ==, TRUE); +} + +static void check_produces(const char *machine, const char *interface) +{ + QOSGraphEdge *edge; + + qos_node_produces(machine, interface); + check_interface(interface); + edge = qos_graph_get_edge(machine, interface); + g_assert_nonnull(edge); + g_assert_cmpint(qos_graph_edge_get_type(edge), ==, + QEDGE_PRODUCES); + g_assert_cmpint(qos_graph_has_edge(machine, interface), ==, TRUE); +} + +static void check_consumes(const char *driver, const char *interface) +{ + QOSGraphEdge *edge; + + qos_node_consumes(driver, interface, NULL); + check_interface(interface); + edge = qos_graph_get_edge(interface, driver); + g_assert_nonnull(edge); + g_assert_cmpint(qos_graph_edge_get_type(edge), ==, QEDGE_CONSUMED_BY); + g_assert_cmpint(qos_graph_has_edge(interface, driver), ==, TRUE); +} + +static void check_driver(const char *driver) +{ + qos_node_create_driver(driver, driverfunct); + g_assert_cmpint(qos_graph_has_machine(driver), ==, FALSE); + g_assert_nonnull(qos_graph_get_node(driver)); + g_assert_cmpint(qos_graph_has_node(driver), ==, TRUE); + g_assert_cmpint(qos_graph_get_node_type(driver), ==, QNODE_DRIVER); + g_assert_cmpint(qos_graph_get_node_availability(driver), ==, FALSE); + qos_graph_node_set_availability(driver, TRUE); + g_assert_cmpint(qos_graph_get_node_availability(driver), ==, TRUE); +} + +static void check_test(const char *test, const char *interface) +{ + QOSGraphEdge *edge; + char *full_name = g_strdup_printf("%s-tests/%s", interface, test); + + qos_add_test(test, interface, testfunct, NULL); + g_assert_cmpint(qos_graph_has_machine(test), ==, FALSE); + g_assert_cmpint(qos_graph_has_machine(full_name), ==, FALSE); + g_assert_nonnull(qos_graph_get_node(full_name)); + g_assert_cmpint(qos_graph_has_node(full_name), ==, TRUE); + g_assert_cmpint(qos_graph_get_node_type(full_name), ==, QNODE_TEST); + edge = qos_graph_get_edge(interface, full_name); + g_assert_nonnull(edge); + g_assert_cmpint(qos_graph_edge_get_type(edge), ==, + QEDGE_CONSUMED_BY); + g_assert_cmpint(qos_graph_has_edge(interface, full_name), ==, TRUE); + g_assert_cmpint(qos_graph_get_node_availability(full_name), ==, TRUE); + qos_graph_node_set_availability(full_name, FALSE); + g_assert_cmpint(qos_graph_get_node_availability(full_name), ==, FALSE); + g_free(full_name); +} + +static void count_each_test(QOSGraphNode *path, int len) +{ + npath++; +} + +static void check_leaf_discovered(int n) +{ + npath = 0; + qos_graph_foreach_test_path(count_each_test); + g_assert_cmpint(n, ==, npath); +} + +/* G_Test functions */ + +static void init_nop(void) +{ + qos_graph_init(); + qos_graph_destroy(); +} + +static void test_machine(void) +{ + qos_graph_init(); + check_machine(MACHINE_PC); + qos_graph_destroy(); +} + +static void test_contains(void) +{ + qos_graph_init(); + check_contains(MACHINE_PC, I440FX); + g_assert_null(qos_graph_get_machine(MACHINE_PC)); + g_assert_null(qos_graph_get_machine(I440FX)); + g_assert_null(qos_graph_get_node(MACHINE_PC)); + g_assert_null(qos_graph_get_node(I440FX)); + qos_graph_destroy(); +} + +static void test_multiple_contains(void) +{ + qos_graph_init(); + check_contains(MACHINE_PC, I440FX); + check_contains(MACHINE_PC, PCIBUS_PC); + qos_graph_destroy(); +} + +static void test_produces(void) +{ + qos_graph_init(); + check_produces(MACHINE_PC, I440FX); + g_assert_null(qos_graph_get_machine(MACHINE_PC)); + g_assert_null(qos_graph_get_machine(I440FX)); + g_assert_null(qos_graph_get_node(MACHINE_PC)); + g_assert_nonnull(qos_graph_get_node(I440FX)); + qos_graph_destroy(); +} + +static void test_multiple_produces(void) +{ + qos_graph_init(); + check_produces(MACHINE_PC, I440FX); + check_produces(MACHINE_PC, PCIBUS_PC); + qos_graph_destroy(); +} + +static void test_consumes(void) +{ + qos_graph_init(); + check_consumes(I440FX, SDHCI); + g_assert_null(qos_graph_get_machine(I440FX)); + g_assert_null(qos_graph_get_machine(SDHCI)); + g_assert_null(qos_graph_get_node(I440FX)); + g_assert_nonnull(qos_graph_get_node(SDHCI)); + qos_graph_destroy(); +} + +static void test_multiple_consumes(void) +{ + qos_graph_init(); + check_consumes(I440FX, SDHCI); + check_consumes(PCIBUS_PC, SDHCI); + qos_graph_destroy(); +} + +static void test_driver(void) +{ + qos_graph_init(); + check_driver(I440FX); + qos_graph_destroy(); +} + +static void test_test(void) +{ + qos_graph_init(); + check_test(REGISTER_TEST, SDHCI); + qos_graph_destroy(); +} + +static void test_machine_contains_driver(void) +{ + qos_graph_init(); + check_machine(MACHINE_PC); + check_driver(I440FX); + check_contains(MACHINE_PC, I440FX); + qos_graph_destroy(); +} + +static void test_driver_contains_driver(void) +{ + qos_graph_init(); + check_driver(PCIBUS_PC); + check_driver(I440FX); + check_contains(PCIBUS_PC, I440FX); + qos_graph_destroy(); +} + +static void test_machine_produces_interface(void) +{ + qos_graph_init(); + check_machine(MACHINE_PC); + check_produces(MACHINE_PC, SDHCI); + qos_graph_destroy(); +} + +static void test_driver_produces_interface(void) +{ + qos_graph_init(); + check_driver(I440FX); + check_produces(I440FX, SDHCI); + qos_graph_destroy(); +} + +static void test_machine_consumes_interface(void) +{ + qos_graph_init(); + check_machine(MACHINE_PC); + check_consumes(MACHINE_PC, SDHCI); + qos_graph_destroy(); +} + +static void test_driver_consumes_interface(void) +{ + qos_graph_init(); + check_driver(I440FX); + check_consumes(I440FX, SDHCI); + qos_graph_destroy(); +} + +static void test_test_consumes_interface(void) +{ + qos_graph_init(); + check_test(REGISTER_TEST, SDHCI); + qos_graph_destroy(); +} + +static void test_full_sample(void) +{ + qos_graph_init(); + check_machine(MACHINE_PC); + check_contains(MACHINE_PC, I440FX); + check_driver(I440FX); + check_driver(PCIBUS_PC); + check_contains(I440FX, PCIBUS_PC); + check_produces(PCIBUS_PC, PCIBUS); + check_driver(SDHCI_PCI); + qos_node_consumes(SDHCI_PCI, PCIBUS, NULL); + check_produces(SDHCI_PCI, SDHCI); + check_driver(SDHCI_MM); + check_produces(SDHCI_MM, SDHCI); + qos_add_test(REGISTER_TEST, SDHCI, testfunct, NULL); + check_leaf_discovered(1); + qos_print_graph(); + qos_graph_destroy(); +} + +static void test_full_sample_raspi(void) +{ + qos_graph_init(); + check_machine(MACHINE_PC); + check_contains(MACHINE_PC, I440FX); + check_driver(I440FX); + check_driver(PCIBUS_PC); + check_contains(I440FX, PCIBUS_PC); + check_produces(PCIBUS_PC, PCIBUS); + check_driver(SDHCI_PCI); + qos_node_consumes(SDHCI_PCI, PCIBUS, NULL); + check_produces(SDHCI_PCI, SDHCI); + check_machine(MACHINE_RASPI2); + check_contains(MACHINE_RASPI2, SDHCI_MM); + check_driver(SDHCI_MM); + check_produces(SDHCI_MM, SDHCI); + qos_add_test(REGISTER_TEST, SDHCI, testfunct, NULL); + qos_print_graph(); + check_leaf_discovered(2); + qos_graph_destroy(); +} + +static void test_cycle(void) +{ + qos_graph_init(); + check_machine(MACHINE_RASPI2); + check_driver("B"); + check_driver("C"); + check_driver("D"); + check_contains(MACHINE_RASPI2, "B"); + check_contains("B", "C"); + check_contains("C", "D"); + check_contains("D", MACHINE_RASPI2); + check_leaf_discovered(0); + qos_print_graph(); + qos_graph_destroy(); +} + +static void test_two_test_same_interface(void) +{ + qos_graph_init(); + check_machine(MACHINE_RASPI2); + check_produces(MACHINE_RASPI2, "B"); + qos_add_test("C", "B", testfunct, NULL); + qos_add_test("D", "B", testfunct, NULL); + check_contains(MACHINE_RASPI2, "B"); + check_leaf_discovered(4); + qos_print_graph(); + qos_graph_destroy(); +} + +static void test_test_in_path(void) +{ + qos_graph_init(); + check_machine(MACHINE_RASPI2); + check_produces(MACHINE_RASPI2, "B"); + qos_add_test("C", "B", testfunct, NULL); + check_driver("D"); + check_consumes("D", "B"); + check_produces("D", "E"); + qos_add_test("F", "E", testfunct, NULL); + check_leaf_discovered(2); + qos_print_graph(); + qos_graph_destroy(); +} + +static void test_double_edge(void) +{ + qos_graph_init(); + check_machine(MACHINE_RASPI2); + check_produces("B", "C"); + qos_node_consumes("C", "B", NULL); + qos_add_test("D", "C", testfunct, NULL); + check_contains(MACHINE_RASPI2, "B"); + qos_print_graph(); + qos_graph_destroy(); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + g_test_add_func("/qgraph/init_nop", init_nop); + g_test_add_func("/qgraph/test_machine", test_machine); + g_test_add_func("/qgraph/test_contains", test_contains); + g_test_add_func("/qgraph/test_multiple_contains", test_multiple_contains); + g_test_add_func("/qgraph/test_produces", test_produces); + g_test_add_func("/qgraph/test_multiple_produces", test_multiple_produces); + g_test_add_func("/qgraph/test_consumes", test_consumes); + g_test_add_func("/qgraph/test_multiple_consumes", + test_multiple_consumes); + g_test_add_func("/qgraph/test_driver", test_driver); + g_test_add_func("/qgraph/test_test", test_test); + g_test_add_func("/qgraph/test_machine_contains_driver", + test_machine_contains_driver); + g_test_add_func("/qgraph/test_driver_contains_driver", + test_driver_contains_driver); + g_test_add_func("/qgraph/test_machine_produces_interface", + test_machine_produces_interface); + g_test_add_func("/qgraph/test_driver_produces_interface", + test_driver_produces_interface); + g_test_add_func("/qgraph/test_machine_consumes_interface", + test_machine_consumes_interface); + g_test_add_func("/qgraph/test_driver_consumes_interface", + test_driver_consumes_interface); + g_test_add_func("/qgraph/test_test_consumes_interface", + test_test_consumes_interface); + g_test_add_func("/qgraph/test_full_sample", test_full_sample); + g_test_add_func("/qgraph/test_full_sample_raspi", test_full_sample_raspi); + g_test_add_func("/qgraph/test_cycle", test_cycle); + g_test_add_func("/qgraph/test_two_test_same_interface", + test_two_test_same_interface); + g_test_add_func("/qgraph/test_test_in_path", test_test_in_path); + g_test_add_func("/qgraph/test_double_edge", test_double_edge); + + g_test_run(); + return 0; +} diff --git a/tests/unit/test-qht.c b/tests/unit/test-qht.c new file mode 100644 index 0000000000..4d23cefab6 --- /dev/null +++ b/tests/unit/test-qht.c @@ -0,0 +1,260 @@ +/* + * Copyright (C) 2016, Emilio G. Cota + * + * License: GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#include "qemu/osdep.h" +#include "qemu/qht.h" +#include "qemu/rcu.h" + +#define N 5000 + +static struct qht ht; +static int32_t arr[N * 2]; + +static bool is_equal(const void *ap, const void *bp) +{ + const int32_t *a = ap; + const int32_t *b = bp; + + return *a == *b; +} + +static void insert(int a, int b) +{ + int i; + + for (i = a; i < b; i++) { + uint32_t hash; + void *existing; + bool inserted; + + arr[i] = i; + hash = i; + + inserted = qht_insert(&ht, &arr[i], hash, NULL); + g_assert_true(inserted); + inserted = qht_insert(&ht, &arr[i], hash, &existing); + g_assert_false(inserted); + g_assert_true(existing == &arr[i]); + } +} + +static void do_rm(int init, int end, bool exist) +{ + int i; + + for (i = init; i < end; i++) { + uint32_t hash; + + hash = arr[i]; + if (exist) { + g_assert_true(qht_remove(&ht, &arr[i], hash)); + } else { + g_assert_false(qht_remove(&ht, &arr[i], hash)); + } + } +} + +static void rm(int init, int end) +{ + do_rm(init, end, true); +} + +static void rm_nonexist(int init, int end) +{ + do_rm(init, end, false); +} + +static void check(int a, int b, bool expected) +{ + struct qht_stats stats; + int i; + + rcu_read_lock(); + for (i = a; i < b; i++) { + void *p; + uint32_t hash; + int32_t val; + + val = i; + hash = i; + /* test both lookup variants; results should be the same */ + if (i % 2) { + p = qht_lookup(&ht, &val, hash); + } else { + p = qht_lookup_custom(&ht, &val, hash, is_equal); + } + g_assert_true(!!p == expected); + } + rcu_read_unlock(); + + qht_statistics_init(&ht, &stats); + if (stats.used_head_buckets) { + g_assert_cmpfloat(qdist_avg(&stats.chain), >=, 1.0); + } + g_assert_cmpuint(stats.head_buckets, >, 0); + qht_statistics_destroy(&stats); +} + +static void count_func(void *p, uint32_t hash, void *userp) +{ + unsigned int *curr = userp; + + (*curr)++; +} + +static void check_n(size_t expected) +{ + struct qht_stats stats; + + qht_statistics_init(&ht, &stats); + g_assert_cmpuint(stats.entries, ==, expected); + qht_statistics_destroy(&stats); +} + +static void iter_check(unsigned int count) +{ + unsigned int curr = 0; + + qht_iter(&ht, count_func, &curr); + g_assert_cmpuint(curr, ==, count); +} + +static void sum_func(void *p, uint32_t hash, void *userp) +{ + uint32_t *sum = userp; + uint32_t a = *(uint32_t *)p; + + *sum += a; +} + +static void iter_sum_check(unsigned int expected) +{ + unsigned int sum = 0; + + qht_iter(&ht, sum_func, &sum); + g_assert_cmpuint(sum, ==, expected); +} + +static bool rm_mod_func(void *p, uint32_t hash, void *userp) +{ + uint32_t a = *(uint32_t *)p; + unsigned int mod = *(unsigned int *)userp; + + return a % mod == 0; +} + +static void iter_rm_mod(unsigned int mod) +{ + qht_iter_remove(&ht, rm_mod_func, &mod); +} + +static void iter_rm_mod_check(unsigned int mod) +{ + unsigned int expected = 0; + unsigned int i; + + for (i = 0; i < N; i++) { + if (i % mod == 0) { + continue; + } + expected += i; + } + iter_sum_check(expected); +} + +static void qht_do_test(unsigned int mode, size_t init_entries) +{ + /* under KVM we might fetch stats from an uninitialized qht */ + check_n(0); + + qht_init(&ht, is_equal, 0, mode); + rm_nonexist(0, 4); + /* + * Test that we successfully delete the last element in a bucket. + * This is a hard-to-reach code path when resizing is on, but without + * resizing we can easily hit it if init_entries <= 1. + * Given that the number of elements per bucket can be 4 or 6 depending on + * the host's pointer size, test the removal of the 4th and 6th elements. + */ + insert(0, 4); + rm_nonexist(5, 6); + rm(3, 4); + check_n(3); + insert(3, 6); + rm(5, 6); + check_n(5); + rm_nonexist(7, 8); + iter_rm_mod(1); + + if (!(mode & QHT_MODE_AUTO_RESIZE)) { + qht_resize(&ht, init_entries * 4 + 4); + } + + check_n(0); + rm_nonexist(0, 10); + insert(0, N); + check(0, N, true); + check_n(N); + check(-N, -1, false); + iter_check(N); + + rm(101, 102); + check_n(N - 1); + insert(N, N * 2); + check_n(N + N - 1); + rm(N, N * 2); + check_n(N - 1); + insert(101, 102); + check_n(N); + + rm(10, 200); + check_n(N - 190); + insert(150, 200); + check_n(N - 190 + 50); + insert(10, 150); + check_n(N); + + qht_reset(&ht); + insert(0, N); + rm_nonexist(N, N + 32); + iter_rm_mod(10); + iter_rm_mod_check(10); + check_n(N * 9 / 10); + qht_reset_size(&ht, 0); + check_n(0); + check(0, N, false); + + qht_destroy(&ht); +} + +static void qht_test(unsigned int mode) +{ + qht_do_test(mode, 0); + qht_do_test(mode, 1); + qht_do_test(mode, 2); + qht_do_test(mode, 8); + qht_do_test(mode, 16); + qht_do_test(mode, 8192); + qht_do_test(mode, 16384); +} + +static void test_default(void) +{ + qht_test(0); +} + +static void test_resize(void) +{ + qht_test(QHT_MODE_AUTO_RESIZE); +} + +int main(int argc, char *argv[]) +{ + g_test_init(&argc, &argv, NULL); + g_test_add_func("/qht/mode/default", test_default); + g_test_add_func("/qht/mode/resize", test_resize); + return g_test_run(); +} diff --git a/tests/unit/test-qmp-cmds.c b/tests/unit/test-qmp-cmds.c new file mode 100644 index 0000000000..d3413bfef0 --- /dev/null +++ b/tests/unit/test-qmp-cmds.c @@ -0,0 +1,359 @@ +#include "qemu/osdep.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qmp/qjson.h" +#include "qapi/qmp/qnum.h" +#include "qapi/qmp/qstring.h" +#include "qapi/error.h" +#include "qapi/qobject-input-visitor.h" +#include "tests/test-qapi-types.h" +#include "tests/test-qapi-visit.h" +#include "test-qapi-commands.h" +#include "test-qapi-init-commands.h" + +static QmpCommandList qmp_commands; + +#if defined(TEST_IF_STRUCT) && defined(TEST_IF_CMD) +UserDefThree *qmp_TestIfCmd(TestIfStruct *foo, Error **errp) +{ + return NULL; +} +#endif + +UserDefThree *qmp_TestCmdReturnDefThree(Error **errp) +{ + return NULL; +} + +void qmp_user_def_cmd(Error **errp) +{ +} + +void qmp_test_flags_command(Error **errp) +{ +} + +void qmp_cmd_success_response(Error **errp) +{ +} + +void qmp_coroutine_cmd(Error **errp) +{ +} + +Empty2 *qmp_user_def_cmd0(Error **errp) +{ + return g_new0(Empty2, 1); +} + +void qmp_user_def_cmd1(UserDefOne * ud1, Error **errp) +{ +} + +void qmp_test_features0(FeatureStruct0 *fs0, FeatureStruct1 *fs1, + FeatureStruct2 *fs2, FeatureStruct3 *fs3, + FeatureStruct4 *fs4, CondFeatureStruct1 *cfs1, + CondFeatureStruct2 *cfs2, CondFeatureStruct3 *cfs3, + Error **errp) +{ +} + +void qmp_test_command_features1(Error **errp) +{ +} + +void qmp_test_command_features3(Error **errp) +{ +} + +void qmp_test_command_cond_features1(Error **errp) +{ +} + +void qmp_test_command_cond_features2(Error **errp) +{ +} + +void qmp_test_command_cond_features3(Error **errp) +{ +} + +UserDefTwo *qmp_user_def_cmd2(UserDefOne *ud1a, + bool has_udb1, UserDefOne *ud1b, + Error **errp) +{ + UserDefTwo *ret; + UserDefOne *ud1c = g_malloc0(sizeof(UserDefOne)); + UserDefOne *ud1d = g_malloc0(sizeof(UserDefOne)); + + ud1c->string = strdup(ud1a->string); + ud1c->integer = ud1a->integer; + ud1d->string = strdup(has_udb1 ? ud1b->string : "blah0"); + ud1d->integer = has_udb1 ? ud1b->integer : 0; + + ret = g_new0(UserDefTwo, 1); + ret->string0 = strdup("blah1"); + ret->dict1 = g_new0(UserDefTwoDict, 1); + ret->dict1->string1 = strdup("blah2"); + ret->dict1->dict2 = g_new0(UserDefTwoDictDict, 1); + ret->dict1->dict2->userdef = ud1c; + ret->dict1->dict2->string = strdup("blah3"); + ret->dict1->dict3 = g_new0(UserDefTwoDictDict, 1); + ret->dict1->has_dict3 = true; + ret->dict1->dict3->userdef = ud1d; + ret->dict1->dict3->string = strdup("blah4"); + + return ret; +} + +int64_t qmp_guest_get_time(int64_t a, bool has_b, int64_t b, Error **errp) +{ + return a + (has_b ? b : 0); +} + +QObject *qmp_guest_sync(QObject *arg, Error **errp) +{ + return arg; +} + +void qmp_boxed_struct(UserDefZero *arg, Error **errp) +{ +} + +void qmp_boxed_union(UserDefListUnion *arg, Error **errp) +{ +} + +void qmp_boxed_empty(Empty1 *arg, Error **errp) +{ +} + +__org_qemu_x_Union1 *qmp___org_qemu_x_command(__org_qemu_x_EnumList *a, + __org_qemu_x_StructList *b, + __org_qemu_x_Union2 *c, + __org_qemu_x_Alt *d, + Error **errp) +{ + __org_qemu_x_Union1 *ret = g_new0(__org_qemu_x_Union1, 1); + + ret->type = ORG_QEMU_X_UNION1_KIND___ORG_QEMU_X_BRANCH; + ret->u.__org_qemu_x_branch.data = strdup("blah1"); + + /* Also test that 'wchar-t' was munged to 'q_wchar_t' */ + if (b && b->value && !b->value->has_q_wchar_t) { + b->value->q_wchar_t = 1; + } + return ret; +} + + +static QObject *do_qmp_dispatch(bool allow_oob, const char *template, ...) +{ + va_list ap; + QDict *req, *resp; + QObject *ret; + + va_start(ap, template); + req = qdict_from_vjsonf_nofail(template, ap); + va_end(ap); + + resp = qmp_dispatch(&qmp_commands, QOBJECT(req), allow_oob, NULL); + g_assert(resp); + ret = qdict_get(resp, "return"); + g_assert(ret); + g_assert(qdict_size(resp) == 1); + + qobject_ref(ret); + qobject_unref(resp); + qobject_unref(req); + return ret; +} + +static void do_qmp_dispatch_error(bool allow_oob, ErrorClass cls, + const char *template, ...) +{ + va_list ap; + QDict *req, *resp; + QDict *error; + + va_start(ap, template); + req = qdict_from_vjsonf_nofail(template, ap); + va_end(ap); + + resp = qmp_dispatch(&qmp_commands, QOBJECT(req), allow_oob, NULL); + g_assert(resp); + error = qdict_get_qdict(resp, "error"); + g_assert(error); + g_assert_cmpstr(qdict_get_try_str(error, "class"), + ==, QapiErrorClass_str(cls)); + g_assert(qdict_get_try_str(error, "desc")); + g_assert(qdict_size(error) == 2); + g_assert(qdict_size(resp) == 1); + + qobject_unref(resp); + qobject_unref(req); +} + +/* test commands with no input and no return value */ +static void test_dispatch_cmd(void) +{ + QDict *ret; + + ret = qobject_to(QDict, + do_qmp_dispatch(false, + "{ 'execute': 'user_def_cmd' }")); + assert(ret && qdict_size(ret) == 0); + qobject_unref(ret); +} + +static void test_dispatch_cmd_oob(void) +{ + QDict *ret; + + ret = qobject_to(QDict, + do_qmp_dispatch(true, + "{ 'exec-oob': 'test-flags-command' }")); + assert(ret && qdict_size(ret) == 0); + qobject_unref(ret); +} + +/* test commands that return an error due to invalid parameters */ +static void test_dispatch_cmd_failure(void) +{ + /* missing arguments */ + do_qmp_dispatch_error(false, ERROR_CLASS_GENERIC_ERROR, + "{ 'execute': 'user_def_cmd2' }"); + + /* extra arguments */ + do_qmp_dispatch_error(false, ERROR_CLASS_GENERIC_ERROR, + "{ 'execute': 'user_def_cmd'," + " 'arguments': { 'a': 66 } }"); +} + +static void test_dispatch_cmd_success_response(void) +{ + QDict *req = qdict_new(); + QDict *resp; + + qdict_put_str(req, "execute", "cmd-success-response"); + resp = qmp_dispatch(&qmp_commands, QOBJECT(req), false, NULL); + g_assert_null(resp); + qobject_unref(req); +} + +/* test commands that involve both input parameters and return values */ +static void test_dispatch_cmd_io(void) +{ + QDict *ret, *ret_dict, *ret_dict_dict, *ret_dict_dict_userdef; + QDict *ret_dict_dict2, *ret_dict_dict2_userdef; + QNum *ret3; + int64_t val; + + ret = qobject_to(QDict, do_qmp_dispatch(false, + "{ 'execute': 'user_def_cmd2', 'arguments': {" + " 'ud1a': { 'integer': 42, 'string': 'hello' }," + " 'ud1b': { 'integer': 422, 'string': 'hello2' } } }")); + + assert(!strcmp(qdict_get_str(ret, "string0"), "blah1")); + ret_dict = qdict_get_qdict(ret, "dict1"); + assert(!strcmp(qdict_get_str(ret_dict, "string1"), "blah2")); + ret_dict_dict = qdict_get_qdict(ret_dict, "dict2"); + ret_dict_dict_userdef = qdict_get_qdict(ret_dict_dict, "userdef"); + assert(qdict_get_int(ret_dict_dict_userdef, "integer") == 42); + assert(!strcmp(qdict_get_str(ret_dict_dict_userdef, "string"), "hello")); + assert(!strcmp(qdict_get_str(ret_dict_dict, "string"), "blah3")); + ret_dict_dict2 = qdict_get_qdict(ret_dict, "dict3"); + ret_dict_dict2_userdef = qdict_get_qdict(ret_dict_dict2, "userdef"); + assert(qdict_get_int(ret_dict_dict2_userdef, "integer") == 422); + assert(!strcmp(qdict_get_str(ret_dict_dict2_userdef, "string"), "hello2")); + assert(!strcmp(qdict_get_str(ret_dict_dict2, "string"), "blah4")); + qobject_unref(ret); + + ret3 = qobject_to(QNum, do_qmp_dispatch(false, + "{ 'execute': 'guest-get-time', 'arguments': { 'a': 66 } }")); + g_assert(qnum_get_try_int(ret3, &val)); + g_assert_cmpint(val, ==, 66); + qobject_unref(ret3); +} + +/* test generated dealloc functions for generated types */ +static void test_dealloc_types(void) +{ + UserDefOne *ud1test, *ud1a, *ud1b; + UserDefOneList *ud1list; + + ud1test = g_malloc0(sizeof(UserDefOne)); + ud1test->integer = 42; + ud1test->string = g_strdup("hi there 42"); + + qapi_free_UserDefOne(ud1test); + + ud1a = g_malloc0(sizeof(UserDefOne)); + ud1a->integer = 43; + ud1a->string = g_strdup("hi there 43"); + + ud1b = g_malloc0(sizeof(UserDefOne)); + ud1b->integer = 44; + ud1b->string = g_strdup("hi there 44"); + + ud1list = g_malloc0(sizeof(UserDefOneList)); + ud1list->value = ud1a; + ud1list->next = g_malloc0(sizeof(UserDefOneList)); + ud1list->next->value = ud1b; + + qapi_free_UserDefOneList(ud1list); +} + +/* test generated deallocation on an object whose construction was prematurely + * terminated due to an error */ +static void test_dealloc_partial(void) +{ + static const char text[] = "don't leak me"; + + UserDefTwo *ud2 = NULL; + Error *err = NULL; + + /* create partial object */ + { + QDict *ud2_dict; + Visitor *v; + + ud2_dict = qdict_new(); + qdict_put_str(ud2_dict, "string0", text); + + v = qobject_input_visitor_new(QOBJECT(ud2_dict)); + visit_type_UserDefTwo(v, NULL, &ud2, &err); + visit_free(v); + qobject_unref(ud2_dict); + } + + /* verify that visit_type_XXX() cleans up properly on error */ + error_free_or_abort(&err); + assert(!ud2); + + /* Manually create a partial object, leaving ud2->dict1 at NULL */ + ud2 = g_new0(UserDefTwo, 1); + ud2->string0 = g_strdup(text); + + /* tear down partial object */ + qapi_free_UserDefTwo(ud2); +} + + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/qmp/dispatch_cmd", test_dispatch_cmd); + g_test_add_func("/qmp/dispatch_cmd_oob", test_dispatch_cmd_oob); + g_test_add_func("/qmp/dispatch_cmd_failure", test_dispatch_cmd_failure); + g_test_add_func("/qmp/dispatch_cmd_io", test_dispatch_cmd_io); + g_test_add_func("/qmp/dispatch_cmd_success_response", + test_dispatch_cmd_success_response); + g_test_add_func("/qmp/dealloc_types", test_dealloc_types); + g_test_add_func("/qmp/dealloc_partial", test_dealloc_partial); + + test_qmp_init_marshal(&qmp_commands); + g_test_run(); + + return 0; +} diff --git a/tests/unit/test-qmp-event.c b/tests/unit/test-qmp-event.c new file mode 100644 index 0000000000..7dd0053190 --- /dev/null +++ b/tests/unit/test-qmp-event.c @@ -0,0 +1,154 @@ +/* + * qapi event unit-tests. + * + * Copyright (c) 2014 Wenchao Xia + * + * Authors: + * Wenchao Xia + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include "qemu/osdep.h" + +#include "qemu-common.h" +#include "qapi/error.h" +#include "qapi/qmp/qbool.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qmp/qjson.h" +#include "qapi/qmp/qnum.h" +#include "qapi/qmp/qstring.h" +#include "qapi/qmp-event.h" +#include "test-qapi-events.h" +#include "test-qapi-emit-events.h" + +typedef struct TestEventData { + QDict *expect; + bool emitted; +} TestEventData; + +TestEventData *test_event_data; +static GMutex test_event_lock; + +void test_qapi_event_emit(test_QAPIEvent event, QDict *d) +{ + QDict *t; + int64_t s, ms; + + /* Verify that we have timestamp, then remove it to compare other fields */ + t = qdict_get_qdict(d, "timestamp"); + g_assert(t); + s = qdict_get_try_int(t, "seconds", -2); + ms = qdict_get_try_int(t, "microseconds", -2); + if (s == -1) { + g_assert(ms == -1); + } else { + g_assert(s >= 0); + g_assert(ms >= 0 && ms <= 999999); + } + g_assert(qdict_size(t) == 2); + + qdict_del(d, "timestamp"); + + g_assert(qobject_is_equal(QOBJECT(d), QOBJECT(test_event_data->expect))); + test_event_data->emitted = true; +} + +static void event_prepare(TestEventData *data, + const void *unused) +{ + /* Global variable test_event_data was used to pass the expectation, so + test cases can't be executed at same time. */ + g_mutex_lock(&test_event_lock); + test_event_data = data; +} + +static void event_teardown(TestEventData *data, + const void *unused) +{ + test_event_data = NULL; + g_mutex_unlock(&test_event_lock); +} + +static void event_test_add(const char *testpath, + void (*test_func)(TestEventData *data, + const void *user_data)) +{ + g_test_add(testpath, TestEventData, NULL, event_prepare, test_func, + event_teardown); +} + + +/* Test cases */ + +static void test_event_a(TestEventData *data, + const void *unused) +{ + data->expect = qdict_from_jsonf_nofail("{ 'event': 'EVENT_A' }"); + qapi_event_send_event_a(); + g_assert(data->emitted); + qobject_unref(data->expect); +} + +static void test_event_b(TestEventData *data, + const void *unused) +{ + data->expect = qdict_from_jsonf_nofail("{ 'event': 'EVENT_B' }"); + qapi_event_send_event_b(); + g_assert(data->emitted); + qobject_unref(data->expect); +} + +static void test_event_c(TestEventData *data, + const void *unused) +{ + UserDefOne b = { .integer = 2, .string = (char *)"test1" }; + + data->expect = qdict_from_jsonf_nofail( + "{ 'event': 'EVENT_C', 'data': {" + " 'a': 1, 'b': { 'integer': 2, 'string': 'test1' }, 'c': 'test2' } }"); + qapi_event_send_event_c(true, 1, true, &b, "test2"); + g_assert(data->emitted); + qobject_unref(data->expect); +} + +/* Complex type */ +static void test_event_d(TestEventData *data, + const void *unused) +{ + UserDefOne struct1 = { + .integer = 2, .string = (char *)"test1", + .has_enum1 = true, .enum1 = ENUM_ONE_VALUE1, + }; + EventStructOne a = { + .struct1 = &struct1, + .string = (char *)"test2", + .has_enum2 = true, + .enum2 = ENUM_ONE_VALUE2, + }; + + data->expect = qdict_from_jsonf_nofail( + "{ 'event': 'EVENT_D', 'data': {" + " 'a': {" + " 'struct1': { 'integer': 2, 'string': 'test1', 'enum1': 'value1' }," + " 'string': 'test2', 'enum2': 'value2' }," + " 'b': 'test3', 'enum3': 'value3' } }"); + qapi_event_send_event_d(&a, "test3", false, NULL, true, ENUM_ONE_VALUE3); + g_assert(data->emitted); + qobject_unref(data->expect); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + event_test_add("/event/event_a", test_event_a); + event_test_add("/event/event_b", test_event_b); + event_test_add("/event/event_c", test_event_c); + event_test_add("/event/event_d", test_event_d); + g_test_run(); + + return 0; +} diff --git a/tests/unit/test-qobject-input-visitor.c b/tests/unit/test-qobject-input-visitor.c new file mode 100644 index 0000000000..e41b91a2a6 --- /dev/null +++ b/tests/unit/test-qobject-input-visitor.c @@ -0,0 +1,1379 @@ +/* + * QObject Input Visitor unit-tests. + * + * Copyright (C) 2011-2016 Red Hat Inc. + * + * Authors: + * Luiz Capitulino + * Paolo Bonzini + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" + +#include "qemu-common.h" +#include "qapi/error.h" +#include "qapi/qapi-visit-introspect.h" +#include "qapi/qobject-input-visitor.h" +#include "test-qapi-visit.h" +#include "qapi/qmp/qbool.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qmp/qnull.h" +#include "qapi/qmp/qnum.h" +#include "qapi/qmp/qstring.h" +#include "qapi/qmp/qjson.h" +#include "test-qapi-introspect.h" +#include "qapi/qapi-introspect.h" + +typedef struct TestInputVisitorData { + QObject *obj; + Visitor *qiv; +} TestInputVisitorData; + +static void visitor_input_teardown(TestInputVisitorData *data, + const void *unused) +{ + qobject_unref(data->obj); + data->obj = NULL; + + if (data->qiv) { + visit_free(data->qiv); + data->qiv = NULL; + } +} + +/* The various test_init functions are provided instead of a test setup + function so that the JSON string used by the tests are kept in the test + functions (and not in main()). */ + +static Visitor *test_init_internal(TestInputVisitorData *data, bool keyval, + QObject *obj) +{ + visitor_input_teardown(data, NULL); + + data->obj = obj; + + if (keyval) { + data->qiv = qobject_input_visitor_new_keyval(data->obj); + } else { + data->qiv = qobject_input_visitor_new(data->obj); + } + g_assert(data->qiv); + return data->qiv; +} + +static GCC_FMT_ATTR(3, 4) +Visitor *visitor_input_test_init_full(TestInputVisitorData *data, + bool keyval, + const char *json_string, ...) +{ + Visitor *v; + va_list ap; + + va_start(ap, json_string); + v = test_init_internal(data, keyval, + qobject_from_vjsonf_nofail(json_string, ap)); + va_end(ap); + return v; +} + +static GCC_FMT_ATTR(2, 3) +Visitor *visitor_input_test_init(TestInputVisitorData *data, + const char *json_string, ...) +{ + Visitor *v; + va_list ap; + + va_start(ap, json_string); + v = test_init_internal(data, false, + qobject_from_vjsonf_nofail(json_string, ap)); + va_end(ap); + return v; +} + +/* similar to visitor_input_test_init(), but does not expect a string + * literal/format json_string argument and so can be used for + * programatically generated strings (and we can't pass in programatically + * generated strings via %s format parameters since qobject_from_jsonv() + * will wrap those in double-quotes and treat the entire object as a + * string) + */ +static Visitor *visitor_input_test_init_raw(TestInputVisitorData *data, + const char *json_string) +{ + return test_init_internal(data, false, + qobject_from_json(json_string, &error_abort)); +} + +static void test_visitor_in_int(TestInputVisitorData *data, + const void *unused) +{ + int64_t res = 0; + double dbl; + int value = -42; + Visitor *v; + + v = visitor_input_test_init(data, "%d", value); + + visit_type_int(v, NULL, &res, &error_abort); + g_assert_cmpint(res, ==, value); + + visit_type_number(v, NULL, &dbl, &error_abort); + g_assert_cmpfloat(dbl, ==, -42.0); +} + +static void test_visitor_in_uint(TestInputVisitorData *data, + const void *unused) +{ + uint64_t res = 0; + int64_t i64; + double dbl; + int value = 42; + Visitor *v; + + v = visitor_input_test_init(data, "%d", value); + + visit_type_uint64(v, NULL, &res, &error_abort); + g_assert_cmpuint(res, ==, (uint64_t)value); + + visit_type_int(v, NULL, &i64, &error_abort); + g_assert_cmpint(i64, ==, value); + + visit_type_number(v, NULL, &dbl, &error_abort); + g_assert_cmpfloat(dbl, ==, value); + + /* BUG: value between INT64_MIN and -1 accepted modulo 2^64 */ + v = visitor_input_test_init(data, "%d", -value); + + visit_type_uint64(v, NULL, &res, &error_abort); + g_assert_cmpuint(res, ==, (uint64_t)-value); + + v = visitor_input_test_init(data, "18446744073709551574"); + + visit_type_uint64(v, NULL, &res, &error_abort); + g_assert_cmpuint(res, ==, 18446744073709551574U); + + visit_type_number(v, NULL, &dbl, &error_abort); + g_assert_cmpfloat(dbl, ==, 18446744073709552000.0); +} + +static void test_visitor_in_int_overflow(TestInputVisitorData *data, + const void *unused) +{ + int64_t res = 0; + Error *err = NULL; + Visitor *v; + + /* + * This will overflow a QNUM_I64, so should be deserialized into a + * QNUM_DOUBLE field instead, leading to an error if we pass it to + * visit_type_int(). Confirm this. + */ + v = visitor_input_test_init(data, "%f", DBL_MAX); + + visit_type_int(v, NULL, &res, &err); + error_free_or_abort(&err); +} + +static void test_visitor_in_int_keyval(TestInputVisitorData *data, + const void *unused) +{ + int64_t res = 0, value = -42; + Error *err = NULL; + Visitor *v; + + v = visitor_input_test_init_full(data, true, "%" PRId64, value); + visit_type_int(v, NULL, &res, &err); + error_free_or_abort(&err); +} + +static void test_visitor_in_int_str_keyval(TestInputVisitorData *data, + const void *unused) +{ + int64_t res = 0, value = -42; + Visitor *v; + + v = visitor_input_test_init_full(data, true, "\"-42\""); + + visit_type_int(v, NULL, &res, &error_abort); + g_assert_cmpint(res, ==, value); +} + +static void test_visitor_in_int_str_fail(TestInputVisitorData *data, + const void *unused) +{ + int64_t res = 0; + Visitor *v; + Error *err = NULL; + + v = visitor_input_test_init(data, "\"-42\""); + + visit_type_int(v, NULL, &res, &err); + error_free_or_abort(&err); +} + +static void test_visitor_in_bool(TestInputVisitorData *data, + const void *unused) +{ + bool res = false; + Visitor *v; + + v = visitor_input_test_init(data, "true"); + + visit_type_bool(v, NULL, &res, &error_abort); + g_assert_cmpint(res, ==, true); +} + +static void test_visitor_in_bool_keyval(TestInputVisitorData *data, + const void *unused) +{ + bool res = false; + Error *err = NULL; + Visitor *v; + + v = visitor_input_test_init_full(data, true, "true"); + + visit_type_bool(v, NULL, &res, &err); + error_free_or_abort(&err); +} + +static void test_visitor_in_bool_str_keyval(TestInputVisitorData *data, + const void *unused) +{ + bool res = false; + Visitor *v; + + v = visitor_input_test_init_full(data, true, "\"on\""); + + visit_type_bool(v, NULL, &res, &error_abort); + g_assert_cmpint(res, ==, true); +} + +static void test_visitor_in_bool_str_fail(TestInputVisitorData *data, + const void *unused) +{ + bool res = false; + Visitor *v; + Error *err = NULL; + + v = visitor_input_test_init(data, "\"true\""); + + visit_type_bool(v, NULL, &res, &err); + error_free_or_abort(&err); +} + +static void test_visitor_in_number(TestInputVisitorData *data, + const void *unused) +{ + double res = 0, value = 3.14; + Visitor *v; + + v = visitor_input_test_init(data, "%f", value); + + visit_type_number(v, NULL, &res, &error_abort); + g_assert_cmpfloat(res, ==, value); +} + +static void test_visitor_in_large_number(TestInputVisitorData *data, + const void *unused) +{ + Error *err = NULL; + double res = 0; + int64_t i64; + uint64_t u64; + Visitor *v; + + v = visitor_input_test_init(data, "-18446744073709551616"); /* -2^64 */ + + visit_type_number(v, NULL, &res, &error_abort); + g_assert_cmpfloat(res, ==, -18446744073709552e3); + + visit_type_int(v, NULL, &i64, &err); + error_free_or_abort(&err); + + visit_type_uint64(v, NULL, &u64, &err); + error_free_or_abort(&err); +} + +static void test_visitor_in_number_keyval(TestInputVisitorData *data, + const void *unused) +{ + double res = 0, value = 3.14; + Error *err = NULL; + Visitor *v; + + v = visitor_input_test_init_full(data, true, "%f", value); + + visit_type_number(v, NULL, &res, &err); + error_free_or_abort(&err); +} + +static void test_visitor_in_number_str_keyval(TestInputVisitorData *data, + const void *unused) +{ + double res = 0, value = 3.14; + Visitor *v; + Error *err = NULL; + + v = visitor_input_test_init_full(data, true, "\"3.14\""); + + visit_type_number(v, NULL, &res, &error_abort); + g_assert_cmpfloat(res, ==, value); + + v = visitor_input_test_init_full(data, true, "\"inf\""); + + visit_type_number(v, NULL, &res, &err); + error_free_or_abort(&err); +} + +static void test_visitor_in_number_str_fail(TestInputVisitorData *data, + const void *unused) +{ + double res = 0; + Visitor *v; + Error *err = NULL; + + v = visitor_input_test_init(data, "\"3.14\""); + + visit_type_number(v, NULL, &res, &err); + error_free_or_abort(&err); +} + +static void test_visitor_in_size_str_keyval(TestInputVisitorData *data, + const void *unused) +{ + uint64_t res, value = 500 * 1024 * 1024; + Visitor *v; + + v = visitor_input_test_init_full(data, true, "\"500M\""); + + visit_type_size(v, NULL, &res, &error_abort); + g_assert_cmpfloat(res, ==, value); +} + +static void test_visitor_in_size_str_fail(TestInputVisitorData *data, + const void *unused) +{ + uint64_t res = 0; + Visitor *v; + Error *err = NULL; + + v = visitor_input_test_init(data, "\"500M\""); + + visit_type_size(v, NULL, &res, &err); + error_free_or_abort(&err); +} + +static void test_visitor_in_string(TestInputVisitorData *data, + const void *unused) +{ + char *res = NULL, *value = (char *) "Q E M U"; + Visitor *v; + + v = visitor_input_test_init(data, "%s", value); + + visit_type_str(v, NULL, &res, &error_abort); + g_assert_cmpstr(res, ==, value); + + g_free(res); +} + +static void test_visitor_in_enum(TestInputVisitorData *data, + const void *unused) +{ + Visitor *v; + EnumOne i; + + for (i = 0; i < ENUM_ONE__MAX; i++) { + EnumOne res = -1; + + v = visitor_input_test_init(data, "%s", EnumOne_str(i)); + + visit_type_EnumOne(v, NULL, &res, &error_abort); + g_assert_cmpint(i, ==, res); + } +} + + +static void test_visitor_in_struct(TestInputVisitorData *data, + const void *unused) +{ + TestStruct *p = NULL; + Visitor *v; + + v = visitor_input_test_init(data, "{ 'integer': -42, 'boolean': true, 'string': 'foo' }"); + + visit_type_TestStruct(v, NULL, &p, &error_abort); + g_assert_cmpint(p->integer, ==, -42); + g_assert(p->boolean == true); + g_assert_cmpstr(p->string, ==, "foo"); + + g_free(p->string); + g_free(p); +} + +static void test_visitor_in_struct_nested(TestInputVisitorData *data, + const void *unused) +{ + g_autoptr(UserDefTwo) udp = NULL; + Visitor *v; + + v = visitor_input_test_init(data, "{ 'string0': 'string0', " + "'dict1': { 'string1': 'string1', " + "'dict2': { 'userdef': { 'integer': 42, " + "'string': 'string' }, 'string': 'string2'}}}"); + + visit_type_UserDefTwo(v, NULL, &udp, &error_abort); + + g_assert_cmpstr(udp->string0, ==, "string0"); + g_assert_cmpstr(udp->dict1->string1, ==, "string1"); + g_assert_cmpint(udp->dict1->dict2->userdef->integer, ==, 42); + g_assert_cmpstr(udp->dict1->dict2->userdef->string, ==, "string"); + g_assert_cmpstr(udp->dict1->dict2->string, ==, "string2"); + g_assert(udp->dict1->has_dict3 == false); +} + +static void test_visitor_in_list(TestInputVisitorData *data, + const void *unused) +{ + UserDefOneList *item, *head = NULL; + Visitor *v; + int i; + + v = visitor_input_test_init(data, "[ { 'string': 'string0', 'integer': 42 }, { 'string': 'string1', 'integer': 43 }, { 'string': 'string2', 'integer': 44 } ]"); + + visit_type_UserDefOneList(v, NULL, &head, &error_abort); + g_assert(head != NULL); + + for (i = 0, item = head; item; item = item->next, i++) { + char string[12]; + + snprintf(string, sizeof(string), "string%d", i); + g_assert_cmpstr(item->value->string, ==, string); + g_assert_cmpint(item->value->integer, ==, 42 + i); + } + + qapi_free_UserDefOneList(head); + head = NULL; + + /* An empty list is valid */ + v = visitor_input_test_init(data, "[]"); + visit_type_UserDefOneList(v, NULL, &head, &error_abort); + g_assert(!head); +} + +static void test_visitor_in_any(TestInputVisitorData *data, + const void *unused) +{ + QObject *res = NULL; + Visitor *v; + QNum *qnum; + QBool *qbool; + QString *qstring; + QDict *qdict; + QObject *qobj; + int64_t val; + + v = visitor_input_test_init(data, "-42"); + visit_type_any(v, NULL, &res, &error_abort); + qnum = qobject_to(QNum, res); + g_assert(qnum); + g_assert(qnum_get_try_int(qnum, &val)); + g_assert_cmpint(val, ==, -42); + qobject_unref(res); + + v = visitor_input_test_init(data, "{ 'integer': -42, 'boolean': true, 'string': 'foo' }"); + visit_type_any(v, NULL, &res, &error_abort); + qdict = qobject_to(QDict, res); + g_assert(qdict && qdict_size(qdict) == 3); + qobj = qdict_get(qdict, "integer"); + g_assert(qobj); + qnum = qobject_to(QNum, qobj); + g_assert(qnum); + g_assert(qnum_get_try_int(qnum, &val)); + g_assert_cmpint(val, ==, -42); + qobj = qdict_get(qdict, "boolean"); + g_assert(qobj); + qbool = qobject_to(QBool, qobj); + g_assert(qbool); + g_assert(qbool_get_bool(qbool) == true); + qobj = qdict_get(qdict, "string"); + g_assert(qobj); + qstring = qobject_to(QString, qobj); + g_assert(qstring); + g_assert_cmpstr(qstring_get_str(qstring), ==, "foo"); + qobject_unref(res); +} + +static void test_visitor_in_null(TestInputVisitorData *data, + const void *unused) +{ + Visitor *v; + Error *err = NULL; + QNull *null; + char *tmp; + + /* + * FIXME: Since QAPI doesn't know the 'null' type yet, we can't + * test visit_type_null() by reading into a QAPI struct then + * checking that it was populated correctly. The best we can do + * for now is ensure that we consumed null from the input, proven + * by the fact that we can't re-read the key; and that we detect + * when input is not null. + */ + + v = visitor_input_test_init_full(data, false, + "{ 'a': null, 'b': '' }"); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_type_null(v, "a", &null, &error_abort); + g_assert(qobject_type(QOBJECT(null)) == QTYPE_QNULL); + qobject_unref(null); + visit_type_null(v, "b", &null, &err); + error_free_or_abort(&err); + g_assert(!null); + visit_type_str(v, "c", &tmp, &err); + error_free_or_abort(&err); + g_assert(!tmp); + visit_check_struct(v, &error_abort); + visit_end_struct(v, NULL); +} + +static void test_visitor_in_union_flat(TestInputVisitorData *data, + const void *unused) +{ + Visitor *v; + g_autoptr(UserDefFlatUnion) tmp = NULL; + UserDefUnionBase *base; + + v = visitor_input_test_init(data, + "{ 'enum1': 'value1', " + "'integer': 41, " + "'string': 'str', " + "'boolean': true }"); + + visit_type_UserDefFlatUnion(v, NULL, &tmp, &error_abort); + g_assert_cmpint(tmp->enum1, ==, ENUM_ONE_VALUE1); + g_assert_cmpstr(tmp->string, ==, "str"); + g_assert_cmpint(tmp->integer, ==, 41); + g_assert_cmpint(tmp->u.value1.boolean, ==, true); + + base = qapi_UserDefFlatUnion_base(tmp); + g_assert(&base->enum1 == &tmp->enum1); +} + +static void test_visitor_in_alternate(TestInputVisitorData *data, + const void *unused) +{ + Visitor *v; + UserDefAlternate *tmp; + WrapAlternate *wrap; + + v = visitor_input_test_init(data, "42"); + visit_type_UserDefAlternate(v, NULL, &tmp, &error_abort); + g_assert_cmpint(tmp->type, ==, QTYPE_QNUM); + g_assert_cmpint(tmp->u.i, ==, 42); + qapi_free_UserDefAlternate(tmp); + + v = visitor_input_test_init(data, "'value1'"); + visit_type_UserDefAlternate(v, NULL, &tmp, &error_abort); + g_assert_cmpint(tmp->type, ==, QTYPE_QSTRING); + g_assert_cmpint(tmp->u.e, ==, ENUM_ONE_VALUE1); + qapi_free_UserDefAlternate(tmp); + + v = visitor_input_test_init(data, "null"); + visit_type_UserDefAlternate(v, NULL, &tmp, &error_abort); + g_assert_cmpint(tmp->type, ==, QTYPE_QNULL); + qapi_free_UserDefAlternate(tmp); + + v = visitor_input_test_init(data, "{'integer':1, 'string':'str', " + "'enum1':'value1', 'boolean':true}"); + visit_type_UserDefAlternate(v, NULL, &tmp, &error_abort); + g_assert_cmpint(tmp->type, ==, QTYPE_QDICT); + g_assert_cmpint(tmp->u.udfu.integer, ==, 1); + g_assert_cmpstr(tmp->u.udfu.string, ==, "str"); + g_assert_cmpint(tmp->u.udfu.enum1, ==, ENUM_ONE_VALUE1); + g_assert_cmpint(tmp->u.udfu.u.value1.boolean, ==, true); + g_assert_cmpint(tmp->u.udfu.u.value1.has_a_b, ==, false); + qapi_free_UserDefAlternate(tmp); + + v = visitor_input_test_init(data, "{ 'alt': 42 }"); + visit_type_WrapAlternate(v, NULL, &wrap, &error_abort); + g_assert_cmpint(wrap->alt->type, ==, QTYPE_QNUM); + g_assert_cmpint(wrap->alt->u.i, ==, 42); + qapi_free_WrapAlternate(wrap); + + v = visitor_input_test_init(data, "{ 'alt': 'value1' }"); + visit_type_WrapAlternate(v, NULL, &wrap, &error_abort); + g_assert_cmpint(wrap->alt->type, ==, QTYPE_QSTRING); + g_assert_cmpint(wrap->alt->u.e, ==, ENUM_ONE_VALUE1); + qapi_free_WrapAlternate(wrap); + + v = visitor_input_test_init(data, "{ 'alt': {'integer':1, 'string':'str', " + "'enum1':'value1', 'boolean':true} }"); + visit_type_WrapAlternate(v, NULL, &wrap, &error_abort); + g_assert_cmpint(wrap->alt->type, ==, QTYPE_QDICT); + g_assert_cmpint(wrap->alt->u.udfu.integer, ==, 1); + g_assert_cmpstr(wrap->alt->u.udfu.string, ==, "str"); + g_assert_cmpint(wrap->alt->u.udfu.enum1, ==, ENUM_ONE_VALUE1); + g_assert_cmpint(wrap->alt->u.udfu.u.value1.boolean, ==, true); + g_assert_cmpint(wrap->alt->u.udfu.u.value1.has_a_b, ==, false); + qapi_free_WrapAlternate(wrap); +} + +static void test_visitor_in_alternate_number(TestInputVisitorData *data, + const void *unused) +{ + Visitor *v; + Error *err = NULL; + AltEnumBool *aeb; + AltEnumNum *aen; + AltNumEnum *ans; + AltEnumInt *asi; + + /* Parsing an int */ + + v = visitor_input_test_init(data, "42"); + visit_type_AltEnumBool(v, NULL, &aeb, &err); + error_free_or_abort(&err); + qapi_free_AltEnumBool(aeb); + + v = visitor_input_test_init(data, "42"); + visit_type_AltEnumNum(v, NULL, &aen, &error_abort); + g_assert_cmpint(aen->type, ==, QTYPE_QNUM); + g_assert_cmpfloat(aen->u.n, ==, 42); + qapi_free_AltEnumNum(aen); + + v = visitor_input_test_init(data, "42"); + visit_type_AltNumEnum(v, NULL, &ans, &error_abort); + g_assert_cmpint(ans->type, ==, QTYPE_QNUM); + g_assert_cmpfloat(ans->u.n, ==, 42); + qapi_free_AltNumEnum(ans); + + v = visitor_input_test_init(data, "42"); + visit_type_AltEnumInt(v, NULL, &asi, &error_abort); + g_assert_cmpint(asi->type, ==, QTYPE_QNUM); + g_assert_cmpint(asi->u.i, ==, 42); + qapi_free_AltEnumInt(asi); + + /* Parsing a double */ + + v = visitor_input_test_init(data, "42.5"); + visit_type_AltEnumBool(v, NULL, &aeb, &err); + error_free_or_abort(&err); + qapi_free_AltEnumBool(aeb); + + v = visitor_input_test_init(data, "42.5"); + visit_type_AltEnumNum(v, NULL, &aen, &error_abort); + g_assert_cmpint(aen->type, ==, QTYPE_QNUM); + g_assert_cmpfloat(aen->u.n, ==, 42.5); + qapi_free_AltEnumNum(aen); + + v = visitor_input_test_init(data, "42.5"); + visit_type_AltNumEnum(v, NULL, &ans, &error_abort); + g_assert_cmpint(ans->type, ==, QTYPE_QNUM); + g_assert_cmpfloat(ans->u.n, ==, 42.5); + qapi_free_AltNumEnum(ans); + + v = visitor_input_test_init(data, "42.5"); + visit_type_AltEnumInt(v, NULL, &asi, &err); + error_free_or_abort(&err); + qapi_free_AltEnumInt(asi); +} + +static void test_list_union_integer_helper(TestInputVisitorData *data, + const void *unused, + UserDefListUnionKind kind) +{ + g_autoptr(UserDefListUnion) cvalue = NULL; + Visitor *v; + GString *gstr_list = g_string_new(""); + GString *gstr_union = g_string_new(""); + int i; + + for (i = 0; i < 32; i++) { + g_string_append_printf(gstr_list, "%d", i); + if (i != 31) { + g_string_append(gstr_list, ", "); + } + } + g_string_append_printf(gstr_union, "{ 'type': '%s', 'data': [ %s ] }", + UserDefListUnionKind_str(kind), + gstr_list->str); + v = visitor_input_test_init_raw(data, gstr_union->str); + + visit_type_UserDefListUnion(v, NULL, &cvalue, &error_abort); + g_assert(cvalue != NULL); + g_assert_cmpint(cvalue->type, ==, kind); + + switch (kind) { + case USER_DEF_LIST_UNION_KIND_INTEGER: { + intList *elem = NULL; + for (i = 0, elem = cvalue->u.integer.data; + elem; elem = elem->next, i++) { + g_assert_cmpint(elem->value, ==, i); + } + break; + } + case USER_DEF_LIST_UNION_KIND_S8: { + int8List *elem = NULL; + for (i = 0, elem = cvalue->u.s8.data; elem; elem = elem->next, i++) { + g_assert_cmpint(elem->value, ==, i); + } + break; + } + case USER_DEF_LIST_UNION_KIND_S16: { + int16List *elem = NULL; + for (i = 0, elem = cvalue->u.s16.data; elem; elem = elem->next, i++) { + g_assert_cmpint(elem->value, ==, i); + } + break; + } + case USER_DEF_LIST_UNION_KIND_S32: { + int32List *elem = NULL; + for (i = 0, elem = cvalue->u.s32.data; elem; elem = elem->next, i++) { + g_assert_cmpint(elem->value, ==, i); + } + break; + } + case USER_DEF_LIST_UNION_KIND_S64: { + int64List *elem = NULL; + for (i = 0, elem = cvalue->u.s64.data; elem; elem = elem->next, i++) { + g_assert_cmpint(elem->value, ==, i); + } + break; + } + case USER_DEF_LIST_UNION_KIND_U8: { + uint8List *elem = NULL; + for (i = 0, elem = cvalue->u.u8.data; elem; elem = elem->next, i++) { + g_assert_cmpint(elem->value, ==, i); + } + break; + } + case USER_DEF_LIST_UNION_KIND_U16: { + uint16List *elem = NULL; + for (i = 0, elem = cvalue->u.u16.data; elem; elem = elem->next, i++) { + g_assert_cmpint(elem->value, ==, i); + } + break; + } + case USER_DEF_LIST_UNION_KIND_U32: { + uint32List *elem = NULL; + for (i = 0, elem = cvalue->u.u32.data; elem; elem = elem->next, i++) { + g_assert_cmpint(elem->value, ==, i); + } + break; + } + case USER_DEF_LIST_UNION_KIND_U64: { + uint64List *elem = NULL; + for (i = 0, elem = cvalue->u.u64.data; elem; elem = elem->next, i++) { + g_assert_cmpint(elem->value, ==, i); + } + break; + } + default: + g_assert_not_reached(); + } + + g_string_free(gstr_union, true); + g_string_free(gstr_list, true); +} + +static void test_visitor_in_list_union_int(TestInputVisitorData *data, + const void *unused) +{ + test_list_union_integer_helper(data, unused, + USER_DEF_LIST_UNION_KIND_INTEGER); +} + +static void test_visitor_in_list_union_int8(TestInputVisitorData *data, + const void *unused) +{ + test_list_union_integer_helper(data, unused, + USER_DEF_LIST_UNION_KIND_S8); +} + +static void test_visitor_in_list_union_int16(TestInputVisitorData *data, + const void *unused) +{ + test_list_union_integer_helper(data, unused, + USER_DEF_LIST_UNION_KIND_S16); +} + +static void test_visitor_in_list_union_int32(TestInputVisitorData *data, + const void *unused) +{ + test_list_union_integer_helper(data, unused, + USER_DEF_LIST_UNION_KIND_S32); +} + +static void test_visitor_in_list_union_int64(TestInputVisitorData *data, + const void *unused) +{ + test_list_union_integer_helper(data, unused, + USER_DEF_LIST_UNION_KIND_S64); +} + +static void test_visitor_in_list_union_uint8(TestInputVisitorData *data, + const void *unused) +{ + test_list_union_integer_helper(data, unused, + USER_DEF_LIST_UNION_KIND_U8); +} + +static void test_visitor_in_list_union_uint16(TestInputVisitorData *data, + const void *unused) +{ + test_list_union_integer_helper(data, unused, + USER_DEF_LIST_UNION_KIND_U16); +} + +static void test_visitor_in_list_union_uint32(TestInputVisitorData *data, + const void *unused) +{ + test_list_union_integer_helper(data, unused, + USER_DEF_LIST_UNION_KIND_U32); +} + +static void test_visitor_in_list_union_uint64(TestInputVisitorData *data, + const void *unused) +{ + test_list_union_integer_helper(data, unused, + USER_DEF_LIST_UNION_KIND_U64); +} + +static void test_visitor_in_list_union_bool(TestInputVisitorData *data, + const void *unused) +{ + g_autoptr(UserDefListUnion) cvalue = NULL; + boolList *elem = NULL; + Visitor *v; + GString *gstr_list = g_string_new(""); + GString *gstr_union = g_string_new(""); + int i; + + for (i = 0; i < 32; i++) { + g_string_append_printf(gstr_list, "%s", + (i % 3 == 0) ? "true" : "false"); + if (i != 31) { + g_string_append(gstr_list, ", "); + } + } + g_string_append_printf(gstr_union, "{ 'type': 'boolean', 'data': [ %s ] }", + gstr_list->str); + v = visitor_input_test_init_raw(data, gstr_union->str); + + visit_type_UserDefListUnion(v, NULL, &cvalue, &error_abort); + g_assert(cvalue != NULL); + g_assert_cmpint(cvalue->type, ==, USER_DEF_LIST_UNION_KIND_BOOLEAN); + + for (i = 0, elem = cvalue->u.boolean.data; elem; elem = elem->next, i++) { + g_assert_cmpint(elem->value, ==, (i % 3 == 0) ? 1 : 0); + } + + g_string_free(gstr_union, true); + g_string_free(gstr_list, true); +} + +static void test_visitor_in_list_union_string(TestInputVisitorData *data, + const void *unused) +{ + g_autoptr(UserDefListUnion) cvalue = NULL; + strList *elem = NULL; + Visitor *v; + GString *gstr_list = g_string_new(""); + GString *gstr_union = g_string_new(""); + int i; + + for (i = 0; i < 32; i++) { + g_string_append_printf(gstr_list, "'%d'", i); + if (i != 31) { + g_string_append(gstr_list, ", "); + } + } + g_string_append_printf(gstr_union, "{ 'type': 'string', 'data': [ %s ] }", + gstr_list->str); + v = visitor_input_test_init_raw(data, gstr_union->str); + + visit_type_UserDefListUnion(v, NULL, &cvalue, &error_abort); + g_assert(cvalue != NULL); + g_assert_cmpint(cvalue->type, ==, USER_DEF_LIST_UNION_KIND_STRING); + + for (i = 0, elem = cvalue->u.string.data; elem; elem = elem->next, i++) { + gchar str[8]; + sprintf(str, "%d", i); + g_assert_cmpstr(elem->value, ==, str); + } + + g_string_free(gstr_union, true); + g_string_free(gstr_list, true); +} + +#define DOUBLE_STR_MAX 16 + +static void test_visitor_in_list_union_number(TestInputVisitorData *data, + const void *unused) +{ + g_autoptr(UserDefListUnion) cvalue = NULL; + numberList *elem = NULL; + Visitor *v; + GString *gstr_list = g_string_new(""); + GString *gstr_union = g_string_new(""); + int i; + + for (i = 0; i < 32; i++) { + g_string_append_printf(gstr_list, "%f", (double)i / 3); + if (i != 31) { + g_string_append(gstr_list, ", "); + } + } + g_string_append_printf(gstr_union, "{ 'type': 'number', 'data': [ %s ] }", + gstr_list->str); + v = visitor_input_test_init_raw(data, gstr_union->str); + + visit_type_UserDefListUnion(v, NULL, &cvalue, &error_abort); + g_assert(cvalue != NULL); + g_assert_cmpint(cvalue->type, ==, USER_DEF_LIST_UNION_KIND_NUMBER); + + for (i = 0, elem = cvalue->u.number.data; elem; elem = elem->next, i++) { + GString *double_expected = g_string_new(""); + GString *double_actual = g_string_new(""); + + g_string_printf(double_expected, "%.6f", (double)i / 3); + g_string_printf(double_actual, "%.6f", elem->value); + g_assert_cmpstr(double_expected->str, ==, double_actual->str); + + g_string_free(double_expected, true); + g_string_free(double_actual, true); + } + + g_string_free(gstr_union, true); + g_string_free(gstr_list, true); +} + +static void input_visitor_test_add(const char *testpath, + const void *user_data, + void (*test_func)(TestInputVisitorData *data, + const void *user_data)) +{ + g_test_add(testpath, TestInputVisitorData, user_data, NULL, test_func, + visitor_input_teardown); +} + +static void test_visitor_in_errors(TestInputVisitorData *data, + const void *unused) +{ + TestStruct *p = NULL; + Error *err = NULL; + Visitor *v; + strList *q = NULL; + UserDefTwo *r = NULL; + WrapAlternate *s = NULL; + + v = visitor_input_test_init(data, "{ 'integer': false, 'boolean': 'foo', " + "'string': -42 }"); + + visit_type_TestStruct(v, NULL, &p, &err); + error_free_or_abort(&err); + g_assert(!p); + + v = visitor_input_test_init(data, "[ '1', '2', false, '3' ]"); + visit_type_strList(v, NULL, &q, &err); + error_free_or_abort(&err); + assert(!q); + + v = visitor_input_test_init(data, "{ 'str':'hi' }"); + visit_type_UserDefTwo(v, NULL, &r, &err); + error_free_or_abort(&err); + assert(!r); + + v = visitor_input_test_init(data, "{ }"); + visit_type_WrapAlternate(v, NULL, &s, &err); + error_free_or_abort(&err); + assert(!s); +} + +static void test_visitor_in_wrong_type(TestInputVisitorData *data, + const void *unused) +{ + TestStruct *p = NULL; + Visitor *v; + strList *q = NULL; + int64_t i; + Error *err = NULL; + + /* Make sure arrays and structs cannot be confused */ + + v = visitor_input_test_init(data, "[]"); + visit_type_TestStruct(v, NULL, &p, &err); + error_free_or_abort(&err); + g_assert(!p); + + v = visitor_input_test_init(data, "{}"); + visit_type_strList(v, NULL, &q, &err); + error_free_or_abort(&err); + assert(!q); + + /* Make sure primitives and struct cannot be confused */ + + v = visitor_input_test_init(data, "1"); + visit_type_TestStruct(v, NULL, &p, &err); + error_free_or_abort(&err); + g_assert(!p); + + v = visitor_input_test_init(data, "{}"); + visit_type_int(v, NULL, &i, &err); + error_free_or_abort(&err); + + /* Make sure primitives and arrays cannot be confused */ + + v = visitor_input_test_init(data, "1"); + visit_type_strList(v, NULL, &q, &err); + error_free_or_abort(&err); + assert(!q); + + v = visitor_input_test_init(data, "[]"); + visit_type_int(v, NULL, &i, &err); + error_free_or_abort(&err); +} + +static void test_visitor_in_fail_struct(TestInputVisitorData *data, + const void *unused) +{ + TestStruct *p = NULL; + Error *err = NULL; + Visitor *v; + + v = visitor_input_test_init(data, "{ 'integer': -42, 'boolean': true, 'string': 'foo', 'extra': 42 }"); + + visit_type_TestStruct(v, NULL, &p, &err); + error_free_or_abort(&err); + g_assert(!p); +} + +static void test_visitor_in_fail_struct_nested(TestInputVisitorData *data, + const void *unused) +{ + UserDefTwo *udp = NULL; + Error *err = NULL; + Visitor *v; + + v = visitor_input_test_init(data, "{ 'string0': 'string0', 'dict1': { 'string1': 'string1', 'dict2': { 'userdef1': { 'integer': 42, 'string': 'string', 'extra': [42, 23, {'foo':'bar'}] }, 'string2': 'string2'}}}"); + + visit_type_UserDefTwo(v, NULL, &udp, &err); + error_free_or_abort(&err); + g_assert(!udp); +} + +static void test_visitor_in_fail_struct_in_list(TestInputVisitorData *data, + const void *unused) +{ + UserDefOneList *head = NULL; + Error *err = NULL; + Visitor *v; + + v = visitor_input_test_init(data, "[ { 'string': 'string0', 'integer': 42 }, { 'string': 'string1', 'integer': 43 }, { 'string': 'string2', 'integer': 44, 'extra': 'ggg' } ]"); + + visit_type_UserDefOneList(v, NULL, &head, &err); + error_free_or_abort(&err); + g_assert(!head); +} + +static void test_visitor_in_fail_struct_missing(TestInputVisitorData *data, + const void *unused) +{ + Error *err = NULL; + Visitor *v; + QObject *any; + QNull *null; + GenericAlternate *alt; + bool present; + int en; + int64_t i64; + uint32_t u32; + int8_t i8; + char *str; + double dbl; + + v = visitor_input_test_init(data, "{ 'sub': [ {} ] }"); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_start_struct(v, "struct", NULL, 0, &err); + error_free_or_abort(&err); + visit_start_list(v, "list", NULL, 0, &err); + error_free_or_abort(&err); + visit_start_alternate(v, "alternate", &alt, sizeof(*alt), &err); + error_free_or_abort(&err); + visit_optional(v, "optional", &present); + g_assert(!present); + visit_type_enum(v, "enum", &en, &EnumOne_lookup, &err); + error_free_or_abort(&err); + visit_type_int(v, "i64", &i64, &err); + error_free_or_abort(&err); + visit_type_uint32(v, "u32", &u32, &err); + error_free_or_abort(&err); + visit_type_int8(v, "i8", &i8, &err); + error_free_or_abort(&err); + visit_type_str(v, "i8", &str, &err); + error_free_or_abort(&err); + visit_type_number(v, "dbl", &dbl, &err); + error_free_or_abort(&err); + visit_type_any(v, "any", &any, &err); + error_free_or_abort(&err); + visit_type_null(v, "null", &null, &err); + error_free_or_abort(&err); + visit_start_list(v, "sub", NULL, 0, &error_abort); + visit_start_struct(v, NULL, NULL, 0, &error_abort); + visit_type_int(v, "i64", &i64, &err); + error_free_or_abort(&err); + visit_end_struct(v, NULL); + visit_end_list(v, NULL); + visit_end_struct(v, NULL); +} + +static void test_visitor_in_fail_list(TestInputVisitorData *data, + const void *unused) +{ + int64_t i64 = -1; + Error *err = NULL; + Visitor *v; + + /* Unvisited list tail */ + + v = visitor_input_test_init(data, "[ 1, 2, 3 ]"); + + visit_start_list(v, NULL, NULL, 0, &error_abort); + visit_type_int(v, NULL, &i64, &error_abort); + g_assert_cmpint(i64, ==, 1); + visit_type_int(v, NULL, &i64, &error_abort); + g_assert_cmpint(i64, ==, 2); + visit_check_list(v, &err); + error_free_or_abort(&err); + visit_end_list(v, NULL); + + /* Visit beyond end of list */ + v = visitor_input_test_init(data, "[]"); + + visit_start_list(v, NULL, NULL, 0, &error_abort); + visit_type_int(v, NULL, &i64, &err); + error_free_or_abort(&err); + visit_end_list(v, NULL); +} + +static void test_visitor_in_fail_list_nested(TestInputVisitorData *data, + const void *unused) +{ + int64_t i64 = -1; + Error *err = NULL; + Visitor *v; + + /* Unvisited nested list tail */ + + v = visitor_input_test_init(data, "[ 0, [ 1, 2, 3 ] ]"); + + visit_start_list(v, NULL, NULL, 0, &error_abort); + visit_type_int(v, NULL, &i64, &error_abort); + g_assert_cmpint(i64, ==, 0); + visit_start_list(v, NULL, NULL, 0, &error_abort); + visit_type_int(v, NULL, &i64, &error_abort); + g_assert_cmpint(i64, ==, 1); + visit_check_list(v, &err); + error_free_or_abort(&err); + visit_end_list(v, NULL); + visit_check_list(v, &error_abort); + visit_end_list(v, NULL); +} + +static void test_visitor_in_fail_union_list(TestInputVisitorData *data, + const void *unused) +{ + UserDefListUnion *tmp = NULL; + Error *err = NULL; + Visitor *v; + + v = visitor_input_test_init(data, + "{ 'type': 'integer', 'data' : [ 'string' ] }"); + + visit_type_UserDefListUnion(v, NULL, &tmp, &err); + error_free_or_abort(&err); + g_assert(!tmp); +} + +static void test_visitor_in_fail_union_flat(TestInputVisitorData *data, + const void *unused) +{ + UserDefFlatUnion *tmp = NULL; + Error *err = NULL; + Visitor *v; + + v = visitor_input_test_init(data, "{ 'string': 'c', 'integer': 41, 'boolean': true }"); + + visit_type_UserDefFlatUnion(v, NULL, &tmp, &err); + error_free_or_abort(&err); + g_assert(!tmp); +} + +static void test_visitor_in_fail_union_flat_no_discrim(TestInputVisitorData *data, + const void *unused) +{ + UserDefFlatUnion2 *tmp = NULL; + Error *err = NULL; + Visitor *v; + + /* test situation where discriminator field ('enum1' here) is missing */ + v = visitor_input_test_init(data, "{ 'integer': 42, 'string': 'c', 'string1': 'd', 'string2': 'e' }"); + + visit_type_UserDefFlatUnion2(v, NULL, &tmp, &err); + error_free_or_abort(&err); + g_assert(!tmp); +} + +static void test_visitor_in_fail_alternate(TestInputVisitorData *data, + const void *unused) +{ + UserDefAlternate *tmp; + Visitor *v; + Error *err = NULL; + + v = visitor_input_test_init(data, "3.14"); + + visit_type_UserDefAlternate(v, NULL, &tmp, &err); + error_free_or_abort(&err); + g_assert(!tmp); +} + +static void do_test_visitor_in_qmp_introspect(TestInputVisitorData *data, + const QLitObject *qlit) +{ + g_autoptr(SchemaInfoList) schema = NULL; + QObject *obj = qobject_from_qlit(qlit); + Visitor *v; + + v = qobject_input_visitor_new(obj); + + visit_type_SchemaInfoList(v, NULL, &schema, &error_abort); + g_assert(schema); + + qobject_unref(obj); + visit_free(v); +} + +static void test_visitor_in_qmp_introspect(TestInputVisitorData *data, + const void *unused) +{ + do_test_visitor_in_qmp_introspect(data, &test_qmp_schema_qlit); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + input_visitor_test_add("/visitor/input/int", + NULL, test_visitor_in_int); + input_visitor_test_add("/visitor/input/uint", + NULL, test_visitor_in_uint); + input_visitor_test_add("/visitor/input/int_overflow", + NULL, test_visitor_in_int_overflow); + input_visitor_test_add("/visitor/input/int_keyval", + NULL, test_visitor_in_int_keyval); + input_visitor_test_add("/visitor/input/int_str_keyval", + NULL, test_visitor_in_int_str_keyval); + input_visitor_test_add("/visitor/input/int_str_fail", + NULL, test_visitor_in_int_str_fail); + input_visitor_test_add("/visitor/input/bool", + NULL, test_visitor_in_bool); + input_visitor_test_add("/visitor/input/bool_keyval", + NULL, test_visitor_in_bool_keyval); + input_visitor_test_add("/visitor/input/bool_str_keyval", + NULL, test_visitor_in_bool_str_keyval); + input_visitor_test_add("/visitor/input/bool_str_fail", + NULL, test_visitor_in_bool_str_fail); + input_visitor_test_add("/visitor/input/number", + NULL, test_visitor_in_number); + input_visitor_test_add("/visitor/input/large_number", + NULL, test_visitor_in_large_number); + input_visitor_test_add("/visitor/input/number_keyval", + NULL, test_visitor_in_number_keyval); + input_visitor_test_add("/visitor/input/number_str_keyval", + NULL, test_visitor_in_number_str_keyval); + input_visitor_test_add("/visitor/input/number_str_fail", + NULL, test_visitor_in_number_str_fail); + input_visitor_test_add("/visitor/input/size_str_keyval", + NULL, test_visitor_in_size_str_keyval); + input_visitor_test_add("/visitor/input/size_str_fail", + NULL, test_visitor_in_size_str_fail); + input_visitor_test_add("/visitor/input/string", + NULL, test_visitor_in_string); + input_visitor_test_add("/visitor/input/enum", + NULL, test_visitor_in_enum); + input_visitor_test_add("/visitor/input/struct", + NULL, test_visitor_in_struct); + input_visitor_test_add("/visitor/input/struct-nested", + NULL, test_visitor_in_struct_nested); + input_visitor_test_add("/visitor/input/list", + NULL, test_visitor_in_list); + input_visitor_test_add("/visitor/input/any", + NULL, test_visitor_in_any); + input_visitor_test_add("/visitor/input/null", + NULL, test_visitor_in_null); + input_visitor_test_add("/visitor/input/union-flat", + NULL, test_visitor_in_union_flat); + input_visitor_test_add("/visitor/input/alternate", + NULL, test_visitor_in_alternate); + input_visitor_test_add("/visitor/input/errors", + NULL, test_visitor_in_errors); + input_visitor_test_add("/visitor/input/wrong-type", + NULL, test_visitor_in_wrong_type); + input_visitor_test_add("/visitor/input/alternate-number", + NULL, test_visitor_in_alternate_number); + input_visitor_test_add("/visitor/input/list_union/int", + NULL, test_visitor_in_list_union_int); + input_visitor_test_add("/visitor/input/list_union/int8", + NULL, test_visitor_in_list_union_int8); + input_visitor_test_add("/visitor/input/list_union/int16", + NULL, test_visitor_in_list_union_int16); + input_visitor_test_add("/visitor/input/list_union/int32", + NULL, test_visitor_in_list_union_int32); + input_visitor_test_add("/visitor/input/list_union/int64", + NULL, test_visitor_in_list_union_int64); + input_visitor_test_add("/visitor/input/list_union/uint8", + NULL, test_visitor_in_list_union_uint8); + input_visitor_test_add("/visitor/input/list_union/uint16", + NULL, test_visitor_in_list_union_uint16); + input_visitor_test_add("/visitor/input/list_union/uint32", + NULL, test_visitor_in_list_union_uint32); + input_visitor_test_add("/visitor/input/list_union/uint64", + NULL, test_visitor_in_list_union_uint64); + input_visitor_test_add("/visitor/input/list_union/bool", + NULL, test_visitor_in_list_union_bool); + input_visitor_test_add("/visitor/input/list_union/str", + NULL, test_visitor_in_list_union_string); + input_visitor_test_add("/visitor/input/list_union/number", + NULL, test_visitor_in_list_union_number); + input_visitor_test_add("/visitor/input/fail/struct", + NULL, test_visitor_in_fail_struct); + input_visitor_test_add("/visitor/input/fail/struct-nested", + NULL, test_visitor_in_fail_struct_nested); + input_visitor_test_add("/visitor/input/fail/struct-in-list", + NULL, test_visitor_in_fail_struct_in_list); + input_visitor_test_add("/visitor/input/fail/struct-missing", + NULL, test_visitor_in_fail_struct_missing); + input_visitor_test_add("/visitor/input/fail/list", + NULL, test_visitor_in_fail_list); + input_visitor_test_add("/visitor/input/fail/list-nested", + NULL, test_visitor_in_fail_list_nested); + input_visitor_test_add("/visitor/input/fail/union-flat", + NULL, test_visitor_in_fail_union_flat); + input_visitor_test_add("/visitor/input/fail/union-flat-no-discriminator", + NULL, test_visitor_in_fail_union_flat_no_discrim); + input_visitor_test_add("/visitor/input/fail/alternate", + NULL, test_visitor_in_fail_alternate); + input_visitor_test_add("/visitor/input/fail/union-list", + NULL, test_visitor_in_fail_union_list); + input_visitor_test_add("/visitor/input/qapi-introspect", + NULL, test_visitor_in_qmp_introspect); + + g_test_run(); + + return 0; +} diff --git a/tests/unit/test-qobject-output-visitor.c b/tests/unit/test-qobject-output-visitor.c new file mode 100644 index 0000000000..9dc1e075e7 --- /dev/null +++ b/tests/unit/test-qobject-output-visitor.c @@ -0,0 +1,807 @@ +/* + * QObject Output Visitor unit-tests. + * + * Copyright (C) 2011-2016 Red Hat Inc. + * + * Authors: + * Luiz Capitulino + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" + +#include "qemu-common.h" +#include "qapi/error.h" +#include "qapi/qobject-output-visitor.h" +#include "test-qapi-visit.h" +#include "qapi/qmp/qbool.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qmp/qlist.h" +#include "qapi/qmp/qnull.h" +#include "qapi/qmp/qnum.h" +#include "qapi/qmp/qstring.h" + +typedef struct TestOutputVisitorData { + Visitor *ov; + QObject *obj; +} TestOutputVisitorData; + +static void visitor_output_setup(TestOutputVisitorData *data, + const void *unused) +{ + data->ov = qobject_output_visitor_new(&data->obj); + g_assert(data->ov); +} + +static void visitor_output_teardown(TestOutputVisitorData *data, + const void *unused) +{ + visit_free(data->ov); + data->ov = NULL; + qobject_unref(data->obj); + data->obj = NULL; +} + +static QObject *visitor_get(TestOutputVisitorData *data) +{ + visit_complete(data->ov, &data->obj); + g_assert(data->obj); + return data->obj; +} + +static void visitor_reset(TestOutputVisitorData *data) +{ + visitor_output_teardown(data, NULL); + visitor_output_setup(data, NULL); +} + +static void test_visitor_out_int(TestOutputVisitorData *data, + const void *unused) +{ + int64_t value = -42; + int64_t val; + QNum *qnum; + + visit_type_int(data->ov, NULL, &value, &error_abort); + + qnum = qobject_to(QNum, visitor_get(data)); + g_assert(qnum); + g_assert(qnum_get_try_int(qnum, &val)); + g_assert_cmpint(val, ==, value); +} + +static void test_visitor_out_bool(TestOutputVisitorData *data, + const void *unused) +{ + bool value = true; + QBool *qbool; + + visit_type_bool(data->ov, NULL, &value, &error_abort); + + qbool = qobject_to(QBool, visitor_get(data)); + g_assert(qbool); + g_assert(qbool_get_bool(qbool) == value); +} + +static void test_visitor_out_number(TestOutputVisitorData *data, + const void *unused) +{ + double value = 3.14; + QNum *qnum; + + visit_type_number(data->ov, NULL, &value, &error_abort); + + qnum = qobject_to(QNum, visitor_get(data)); + g_assert(qnum); + g_assert(qnum_get_double(qnum) == value); +} + +static void test_visitor_out_string(TestOutputVisitorData *data, + const void *unused) +{ + char *string = (char *) "Q E M U"; + QString *qstr; + + visit_type_str(data->ov, NULL, &string, &error_abort); + + qstr = qobject_to(QString, visitor_get(data)); + g_assert(qstr); + g_assert_cmpstr(qstring_get_str(qstr), ==, string); +} + +static void test_visitor_out_no_string(TestOutputVisitorData *data, + const void *unused) +{ + char *string = NULL; + QString *qstr; + + /* A null string should return "" */ + visit_type_str(data->ov, NULL, &string, &error_abort); + + qstr = qobject_to(QString, visitor_get(data)); + g_assert(qstr); + g_assert_cmpstr(qstring_get_str(qstr), ==, ""); +} + +static void test_visitor_out_enum(TestOutputVisitorData *data, + const void *unused) +{ + EnumOne i; + QString *qstr; + + for (i = 0; i < ENUM_ONE__MAX; i++) { + visit_type_EnumOne(data->ov, "unused", &i, &error_abort); + + qstr = qobject_to(QString, visitor_get(data)); + g_assert(qstr); + g_assert_cmpstr(qstring_get_str(qstr), ==, EnumOne_str(i)); + visitor_reset(data); + } +} + +static void test_visitor_out_struct(TestOutputVisitorData *data, + const void *unused) +{ + TestStruct test_struct = { .integer = 42, + .boolean = false, + .string = (char *) "foo"}; + TestStruct *p = &test_struct; + QDict *qdict; + + visit_type_TestStruct(data->ov, NULL, &p, &error_abort); + + qdict = qobject_to(QDict, visitor_get(data)); + g_assert(qdict); + g_assert_cmpint(qdict_size(qdict), ==, 3); + g_assert_cmpint(qdict_get_int(qdict, "integer"), ==, 42); + g_assert_cmpint(qdict_get_bool(qdict, "boolean"), ==, false); + g_assert_cmpstr(qdict_get_str(qdict, "string"), ==, "foo"); +} + +static void test_visitor_out_struct_nested(TestOutputVisitorData *data, + const void *unused) +{ + int64_t value = 42; + UserDefTwo *ud2; + QDict *qdict, *dict1, *dict2, *dict3, *userdef; + const char *string = "user def string"; + const char *strings[] = { "forty two", "forty three", "forty four", + "forty five" }; + + ud2 = g_malloc0(sizeof(*ud2)); + ud2->string0 = g_strdup(strings[0]); + + ud2->dict1 = g_malloc0(sizeof(*ud2->dict1)); + ud2->dict1->string1 = g_strdup(strings[1]); + + ud2->dict1->dict2 = g_malloc0(sizeof(*ud2->dict1->dict2)); + ud2->dict1->dict2->userdef = g_new0(UserDefOne, 1); + ud2->dict1->dict2->userdef->string = g_strdup(string); + ud2->dict1->dict2->userdef->integer = value; + ud2->dict1->dict2->string = g_strdup(strings[2]); + + ud2->dict1->dict3 = g_malloc0(sizeof(*ud2->dict1->dict3)); + ud2->dict1->has_dict3 = true; + ud2->dict1->dict3->userdef = g_new0(UserDefOne, 1); + ud2->dict1->dict3->userdef->string = g_strdup(string); + ud2->dict1->dict3->userdef->integer = value; + ud2->dict1->dict3->string = g_strdup(strings[3]); + + visit_type_UserDefTwo(data->ov, "unused", &ud2, &error_abort); + + qdict = qobject_to(QDict, visitor_get(data)); + g_assert(qdict); + g_assert_cmpint(qdict_size(qdict), ==, 2); + g_assert_cmpstr(qdict_get_str(qdict, "string0"), ==, strings[0]); + + dict1 = qdict_get_qdict(qdict, "dict1"); + g_assert_cmpint(qdict_size(dict1), ==, 3); + g_assert_cmpstr(qdict_get_str(dict1, "string1"), ==, strings[1]); + + dict2 = qdict_get_qdict(dict1, "dict2"); + g_assert_cmpint(qdict_size(dict2), ==, 2); + g_assert_cmpstr(qdict_get_str(dict2, "string"), ==, strings[2]); + userdef = qdict_get_qdict(dict2, "userdef"); + g_assert_cmpint(qdict_size(userdef), ==, 2); + g_assert_cmpint(qdict_get_int(userdef, "integer"), ==, value); + g_assert_cmpstr(qdict_get_str(userdef, "string"), ==, string); + + dict3 = qdict_get_qdict(dict1, "dict3"); + g_assert_cmpint(qdict_size(dict3), ==, 2); + g_assert_cmpstr(qdict_get_str(dict3, "string"), ==, strings[3]); + userdef = qdict_get_qdict(dict3, "userdef"); + g_assert_cmpint(qdict_size(userdef), ==, 2); + g_assert_cmpint(qdict_get_int(userdef, "integer"), ==, value); + g_assert_cmpstr(qdict_get_str(userdef, "string"), ==, string); + + qapi_free_UserDefTwo(ud2); +} + +static void test_visitor_out_list(TestOutputVisitorData *data, + const void *unused) +{ + const char *value_str = "list value"; + TestStruct *value; + TestStructList *head = NULL; + const int max_items = 10; + bool value_bool = true; + int value_int = 10; + QListEntry *entry; + QList *qlist; + int i; + + /* Build the list in reverse order... */ + for (i = 0; i < max_items; i++) { + value = g_malloc0(sizeof(*value)); + value->integer = value_int + (max_items - i - 1); + value->boolean = value_bool; + value->string = g_strdup(value_str); + + QAPI_LIST_PREPEND(head, value); + } + + visit_type_TestStructList(data->ov, NULL, &head, &error_abort); + + qlist = qobject_to(QList, visitor_get(data)); + g_assert(qlist); + g_assert(!qlist_empty(qlist)); + + /* ...and ensure that the visitor sees it in order */ + i = 0; + QLIST_FOREACH_ENTRY(qlist, entry) { + QDict *qdict; + + qdict = qobject_to(QDict, entry->value); + g_assert(qdict); + g_assert_cmpint(qdict_size(qdict), ==, 3); + g_assert_cmpint(qdict_get_int(qdict, "integer"), ==, value_int + i); + g_assert_cmpint(qdict_get_bool(qdict, "boolean"), ==, value_bool); + g_assert_cmpstr(qdict_get_str(qdict, "string"), ==, value_str); + i++; + } + g_assert_cmpint(i, ==, max_items); + + qapi_free_TestStructList(head); +} + +static void test_visitor_out_list_qapi_free(TestOutputVisitorData *data, + const void *unused) +{ + UserDefTwo *value; + UserDefTwoList *head = NULL; + const char string[] = "foo bar"; + int i, max_count = 1024; + + for (i = 0; i < max_count; i++) { + value = g_malloc0(sizeof(*value)); + + value->string0 = g_strdup(string); + value->dict1 = g_new0(UserDefTwoDict, 1); + value->dict1->string1 = g_strdup(string); + value->dict1->dict2 = g_new0(UserDefTwoDictDict, 1); + value->dict1->dict2->userdef = g_new0(UserDefOne, 1); + value->dict1->dict2->userdef->string = g_strdup(string); + value->dict1->dict2->userdef->integer = 42; + value->dict1->dict2->string = g_strdup(string); + value->dict1->has_dict3 = false; + + QAPI_LIST_PREPEND(head, value); + } + + qapi_free_UserDefTwoList(head); +} + +static void test_visitor_out_any(TestOutputVisitorData *data, + const void *unused) +{ + QObject *qobj; + QNum *qnum; + QBool *qbool; + QString *qstring; + QDict *qdict; + int64_t val; + + qobj = QOBJECT(qnum_from_int(-42)); + visit_type_any(data->ov, NULL, &qobj, &error_abort); + qnum = qobject_to(QNum, visitor_get(data)); + g_assert(qnum); + g_assert(qnum_get_try_int(qnum, &val)); + g_assert_cmpint(val, ==, -42); + qobject_unref(qobj); + + visitor_reset(data); + qdict = qdict_new(); + qdict_put_int(qdict, "integer", -42); + qdict_put_bool(qdict, "boolean", true); + qdict_put_str(qdict, "string", "foo"); + qobj = QOBJECT(qdict); + visit_type_any(data->ov, NULL, &qobj, &error_abort); + qobject_unref(qobj); + qdict = qobject_to(QDict, visitor_get(data)); + g_assert(qdict); + qnum = qobject_to(QNum, qdict_get(qdict, "integer")); + g_assert(qnum); + g_assert(qnum_get_try_int(qnum, &val)); + g_assert_cmpint(val, ==, -42); + qbool = qobject_to(QBool, qdict_get(qdict, "boolean")); + g_assert(qbool); + g_assert(qbool_get_bool(qbool) == true); + qstring = qobject_to(QString, qdict_get(qdict, "string")); + g_assert(qstring); + g_assert_cmpstr(qstring_get_str(qstring), ==, "foo"); +} + +static void test_visitor_out_union_flat(TestOutputVisitorData *data, + const void *unused) +{ + QDict *qdict; + + UserDefFlatUnion *tmp = g_malloc0(sizeof(UserDefFlatUnion)); + tmp->enum1 = ENUM_ONE_VALUE1; + tmp->string = g_strdup("str"); + tmp->integer = 41; + tmp->u.value1.boolean = true; + + visit_type_UserDefFlatUnion(data->ov, NULL, &tmp, &error_abort); + qdict = qobject_to(QDict, visitor_get(data)); + g_assert(qdict); + g_assert_cmpstr(qdict_get_str(qdict, "enum1"), ==, "value1"); + g_assert_cmpstr(qdict_get_str(qdict, "string"), ==, "str"); + g_assert_cmpint(qdict_get_int(qdict, "integer"), ==, 41); + g_assert_cmpint(qdict_get_bool(qdict, "boolean"), ==, true); + + qapi_free_UserDefFlatUnion(tmp); +} + +static void test_visitor_out_alternate(TestOutputVisitorData *data, + const void *unused) +{ + UserDefAlternate *tmp; + QNum *qnum; + QString *qstr; + QDict *qdict; + int64_t val; + + tmp = g_new0(UserDefAlternate, 1); + tmp->type = QTYPE_QNUM; + tmp->u.i = 42; + + visit_type_UserDefAlternate(data->ov, NULL, &tmp, &error_abort); + qnum = qobject_to(QNum, visitor_get(data)); + g_assert(qnum); + g_assert(qnum_get_try_int(qnum, &val)); + g_assert_cmpint(val, ==, 42); + + qapi_free_UserDefAlternate(tmp); + + visitor_reset(data); + tmp = g_new0(UserDefAlternate, 1); + tmp->type = QTYPE_QSTRING; + tmp->u.e = ENUM_ONE_VALUE1; + + visit_type_UserDefAlternate(data->ov, NULL, &tmp, &error_abort); + qstr = qobject_to(QString, visitor_get(data)); + g_assert(qstr); + g_assert_cmpstr(qstring_get_str(qstr), ==, "value1"); + + qapi_free_UserDefAlternate(tmp); + + visitor_reset(data); + tmp = g_new0(UserDefAlternate, 1); + tmp->type = QTYPE_QNULL; + tmp->u.n = qnull(); + + visit_type_UserDefAlternate(data->ov, NULL, &tmp, &error_abort); + g_assert_cmpint(qobject_type(visitor_get(data)), ==, QTYPE_QNULL); + + qapi_free_UserDefAlternate(tmp); + + visitor_reset(data); + tmp = g_new0(UserDefAlternate, 1); + tmp->type = QTYPE_QDICT; + tmp->u.udfu.integer = 1; + tmp->u.udfu.string = g_strdup("str"); + tmp->u.udfu.enum1 = ENUM_ONE_VALUE1; + tmp->u.udfu.u.value1.boolean = true; + + visit_type_UserDefAlternate(data->ov, NULL, &tmp, &error_abort); + qdict = qobject_to(QDict, visitor_get(data)); + g_assert(qdict); + g_assert_cmpint(qdict_size(qdict), ==, 4); + g_assert_cmpint(qdict_get_int(qdict, "integer"), ==, 1); + g_assert_cmpstr(qdict_get_str(qdict, "string"), ==, "str"); + g_assert_cmpstr(qdict_get_str(qdict, "enum1"), ==, "value1"); + g_assert_cmpint(qdict_get_bool(qdict, "boolean"), ==, true); + + qapi_free_UserDefAlternate(tmp); +} + +static void test_visitor_out_null(TestOutputVisitorData *data, + const void *unused) +{ + QNull *null = NULL; + QDict *qdict; + QObject *nil; + + visit_start_struct(data->ov, NULL, NULL, 0, &error_abort); + visit_type_null(data->ov, "a", &null, &error_abort); + visit_check_struct(data->ov, &error_abort); + visit_end_struct(data->ov, NULL); + qdict = qobject_to(QDict, visitor_get(data)); + g_assert(qdict); + g_assert_cmpint(qdict_size(qdict), ==, 1); + nil = qdict_get(qdict, "a"); + g_assert(nil); + g_assert(qobject_type(nil) == QTYPE_QNULL); +} + +static void init_list_union(UserDefListUnion *cvalue) +{ + int i; + switch (cvalue->type) { + case USER_DEF_LIST_UNION_KIND_INTEGER: { + intList **tail = &cvalue->u.integer.data; + for (i = 0; i < 32; i++) { + QAPI_LIST_APPEND(tail, i); + } + break; + } + case USER_DEF_LIST_UNION_KIND_S8: { + int8List **tail = &cvalue->u.s8.data; + for (i = 0; i < 32; i++) { + QAPI_LIST_APPEND(tail, i); + } + break; + } + case USER_DEF_LIST_UNION_KIND_S16: { + int16List **tail = &cvalue->u.s16.data; + for (i = 0; i < 32; i++) { + QAPI_LIST_APPEND(tail, i); + } + break; + } + case USER_DEF_LIST_UNION_KIND_S32: { + int32List **tail = &cvalue->u.s32.data; + for (i = 0; i < 32; i++) { + QAPI_LIST_APPEND(tail, i); + } + break; + } + case USER_DEF_LIST_UNION_KIND_S64: { + int64List **tail = &cvalue->u.s64.data; + for (i = 0; i < 32; i++) { + QAPI_LIST_APPEND(tail, i); + } + break; + } + case USER_DEF_LIST_UNION_KIND_U8: { + uint8List **tail = &cvalue->u.u8.data; + for (i = 0; i < 32; i++) { + QAPI_LIST_APPEND(tail, i); + } + break; + } + case USER_DEF_LIST_UNION_KIND_U16: { + uint16List **tail = &cvalue->u.u16.data; + for (i = 0; i < 32; i++) { + QAPI_LIST_APPEND(tail, i); + } + break; + } + case USER_DEF_LIST_UNION_KIND_U32: { + uint32List **tail = &cvalue->u.u32.data; + for (i = 0; i < 32; i++) { + QAPI_LIST_APPEND(tail, i); + } + break; + } + case USER_DEF_LIST_UNION_KIND_U64: { + uint64List **tail = &cvalue->u.u64.data; + for (i = 0; i < 32; i++) { + QAPI_LIST_APPEND(tail, i); + } + break; + } + case USER_DEF_LIST_UNION_KIND_BOOLEAN: { + boolList **tail = &cvalue->u.boolean.data; + for (i = 0; i < 32; i++) { + QAPI_LIST_APPEND(tail, QEMU_IS_ALIGNED(i, 3)); + } + break; + } + case USER_DEF_LIST_UNION_KIND_STRING: { + strList **tail = &cvalue->u.string.data; + for (i = 0; i < 32; i++) { + QAPI_LIST_APPEND(tail, g_strdup_printf("%d", i)); + } + break; + } + case USER_DEF_LIST_UNION_KIND_NUMBER: { + numberList **tail = &cvalue->u.number.data; + for (i = 0; i < 32; i++) { + QAPI_LIST_APPEND(tail, (double)i / 3); + } + break; + } + default: + g_assert_not_reached(); + } +} + +static void check_list_union(QObject *qobj, + UserDefListUnionKind kind) +{ + QDict *qdict; + QList *qlist; + int i; + + qdict = qobject_to(QDict, qobj); + g_assert(qdict); + g_assert(qdict_haskey(qdict, "data")); + qlist = qlist_copy(qobject_to(QList, qdict_get(qdict, "data"))); + + switch (kind) { + case USER_DEF_LIST_UNION_KIND_U8: + case USER_DEF_LIST_UNION_KIND_U16: + case USER_DEF_LIST_UNION_KIND_U32: + case USER_DEF_LIST_UNION_KIND_U64: + for (i = 0; i < 32; i++) { + QObject *tmp; + QNum *qvalue; + uint64_t val; + + tmp = qlist_peek(qlist); + g_assert(tmp); + qvalue = qobject_to(QNum, tmp); + g_assert(qnum_get_try_uint(qvalue, &val)); + g_assert_cmpint(val, ==, i); + qobject_unref(qlist_pop(qlist)); + } + break; + + case USER_DEF_LIST_UNION_KIND_S8: + case USER_DEF_LIST_UNION_KIND_S16: + case USER_DEF_LIST_UNION_KIND_S32: + case USER_DEF_LIST_UNION_KIND_S64: + /* + * All integer elements in JSON arrays get stored into QNums + * when we convert to QObjects, so we can check them all in + * the same fashion, so simply fall through here. + */ + case USER_DEF_LIST_UNION_KIND_INTEGER: + for (i = 0; i < 32; i++) { + QObject *tmp; + QNum *qvalue; + int64_t val; + + tmp = qlist_peek(qlist); + g_assert(tmp); + qvalue = qobject_to(QNum, tmp); + g_assert(qnum_get_try_int(qvalue, &val)); + g_assert_cmpint(val, ==, i); + qobject_unref(qlist_pop(qlist)); + } + break; + case USER_DEF_LIST_UNION_KIND_BOOLEAN: + for (i = 0; i < 32; i++) { + QObject *tmp; + QBool *qvalue; + tmp = qlist_peek(qlist); + g_assert(tmp); + qvalue = qobject_to(QBool, tmp); + g_assert_cmpint(qbool_get_bool(qvalue), ==, i % 3 == 0); + qobject_unref(qlist_pop(qlist)); + } + break; + case USER_DEF_LIST_UNION_KIND_STRING: + for (i = 0; i < 32; i++) { + QObject *tmp; + QString *qvalue; + gchar str[8]; + tmp = qlist_peek(qlist); + g_assert(tmp); + qvalue = qobject_to(QString, tmp); + sprintf(str, "%d", i); + g_assert_cmpstr(qstring_get_str(qvalue), ==, str); + qobject_unref(qlist_pop(qlist)); + } + break; + case USER_DEF_LIST_UNION_KIND_NUMBER: + for (i = 0; i < 32; i++) { + QObject *tmp; + QNum *qvalue; + GString *double_expected = g_string_new(""); + GString *double_actual = g_string_new(""); + + tmp = qlist_peek(qlist); + g_assert(tmp); + qvalue = qobject_to(QNum, tmp); + g_string_printf(double_expected, "%.6f", (double)i / 3); + g_string_printf(double_actual, "%.6f", qnum_get_double(qvalue)); + g_assert_cmpstr(double_actual->str, ==, double_expected->str); + + qobject_unref(qlist_pop(qlist)); + g_string_free(double_expected, true); + g_string_free(double_actual, true); + } + break; + default: + g_assert_not_reached(); + } + qobject_unref(qlist); +} + +static void test_list_union(TestOutputVisitorData *data, + const void *unused, + UserDefListUnionKind kind) +{ + UserDefListUnion *cvalue = g_new0(UserDefListUnion, 1); + QObject *obj; + + cvalue->type = kind; + init_list_union(cvalue); + + visit_type_UserDefListUnion(data->ov, NULL, &cvalue, &error_abort); + + obj = visitor_get(data); + check_list_union(obj, cvalue->type); + qapi_free_UserDefListUnion(cvalue); +} + +static void test_visitor_out_list_union_int(TestOutputVisitorData *data, + const void *unused) +{ + test_list_union(data, unused, USER_DEF_LIST_UNION_KIND_INTEGER); +} + +static void test_visitor_out_list_union_int8(TestOutputVisitorData *data, + const void *unused) +{ + test_list_union(data, unused, USER_DEF_LIST_UNION_KIND_S8); +} + +static void test_visitor_out_list_union_int16(TestOutputVisitorData *data, + const void *unused) +{ + test_list_union(data, unused, USER_DEF_LIST_UNION_KIND_S16); +} + +static void test_visitor_out_list_union_int32(TestOutputVisitorData *data, + const void *unused) +{ + test_list_union(data, unused, USER_DEF_LIST_UNION_KIND_S32); +} + +static void test_visitor_out_list_union_int64(TestOutputVisitorData *data, + const void *unused) +{ + test_list_union(data, unused, USER_DEF_LIST_UNION_KIND_S64); +} + +static void test_visitor_out_list_union_uint8(TestOutputVisitorData *data, + const void *unused) +{ + test_list_union(data, unused, USER_DEF_LIST_UNION_KIND_U8); +} + +static void test_visitor_out_list_union_uint16(TestOutputVisitorData *data, + const void *unused) +{ + test_list_union(data, unused, USER_DEF_LIST_UNION_KIND_U16); +} + +static void test_visitor_out_list_union_uint32(TestOutputVisitorData *data, + const void *unused) +{ + test_list_union(data, unused, USER_DEF_LIST_UNION_KIND_U32); +} + +static void test_visitor_out_list_union_uint64(TestOutputVisitorData *data, + const void *unused) +{ + test_list_union(data, unused, USER_DEF_LIST_UNION_KIND_U64); +} + +static void test_visitor_out_list_union_bool(TestOutputVisitorData *data, + const void *unused) +{ + test_list_union(data, unused, USER_DEF_LIST_UNION_KIND_BOOLEAN); +} + +static void test_visitor_out_list_union_str(TestOutputVisitorData *data, + const void *unused) +{ + test_list_union(data, unused, USER_DEF_LIST_UNION_KIND_STRING); +} + +static void test_visitor_out_list_union_number(TestOutputVisitorData *data, + const void *unused) +{ + test_list_union(data, unused, USER_DEF_LIST_UNION_KIND_NUMBER); +} + +static void output_visitor_test_add(const char *testpath, + TestOutputVisitorData *data, + void (*test_func)(TestOutputVisitorData *data, const void *user_data)) +{ + g_test_add(testpath, TestOutputVisitorData, data, visitor_output_setup, + test_func, visitor_output_teardown); +} + +int main(int argc, char **argv) +{ + TestOutputVisitorData out_visitor_data; + + g_test_init(&argc, &argv, NULL); + + output_visitor_test_add("/visitor/output/int", + &out_visitor_data, test_visitor_out_int); + output_visitor_test_add("/visitor/output/bool", + &out_visitor_data, test_visitor_out_bool); + output_visitor_test_add("/visitor/output/number", + &out_visitor_data, test_visitor_out_number); + output_visitor_test_add("/visitor/output/string", + &out_visitor_data, test_visitor_out_string); + output_visitor_test_add("/visitor/output/no-string", + &out_visitor_data, test_visitor_out_no_string); + output_visitor_test_add("/visitor/output/enum", + &out_visitor_data, test_visitor_out_enum); + output_visitor_test_add("/visitor/output/struct", + &out_visitor_data, test_visitor_out_struct); + output_visitor_test_add("/visitor/output/struct-nested", + &out_visitor_data, test_visitor_out_struct_nested); + output_visitor_test_add("/visitor/output/list", + &out_visitor_data, test_visitor_out_list); + output_visitor_test_add("/visitor/output/any", + &out_visitor_data, test_visitor_out_any); + output_visitor_test_add("/visitor/output/list-qapi-free", + &out_visitor_data, test_visitor_out_list_qapi_free); + output_visitor_test_add("/visitor/output/union-flat", + &out_visitor_data, test_visitor_out_union_flat); + output_visitor_test_add("/visitor/output/alternate", + &out_visitor_data, test_visitor_out_alternate); + output_visitor_test_add("/visitor/output/null", + &out_visitor_data, test_visitor_out_null); + output_visitor_test_add("/visitor/output/list_union/int", + &out_visitor_data, + test_visitor_out_list_union_int); + output_visitor_test_add("/visitor/output/list_union/int8", + &out_visitor_data, + test_visitor_out_list_union_int8); + output_visitor_test_add("/visitor/output/list_union/int16", + &out_visitor_data, + test_visitor_out_list_union_int16); + output_visitor_test_add("/visitor/output/list_union/int32", + &out_visitor_data, + test_visitor_out_list_union_int32); + output_visitor_test_add("/visitor/output/list_union/int64", + &out_visitor_data, + test_visitor_out_list_union_int64); + output_visitor_test_add("/visitor/output/list_union/uint8", + &out_visitor_data, + test_visitor_out_list_union_uint8); + output_visitor_test_add("/visitor/output/list_union/uint16", + &out_visitor_data, + test_visitor_out_list_union_uint16); + output_visitor_test_add("/visitor/output/list_union/uint32", + &out_visitor_data, + test_visitor_out_list_union_uint32); + output_visitor_test_add("/visitor/output/list_union/uint64", + &out_visitor_data, + test_visitor_out_list_union_uint64); + output_visitor_test_add("/visitor/output/list_union/bool", + &out_visitor_data, + test_visitor_out_list_union_bool); + output_visitor_test_add("/visitor/output/list_union/string", + &out_visitor_data, + test_visitor_out_list_union_str); + output_visitor_test_add("/visitor/output/list_union/number", + &out_visitor_data, + test_visitor_out_list_union_number); + + g_test_run(); + + return 0; +} diff --git a/tests/unit/test-rcu-list.c b/tests/unit/test-rcu-list.c new file mode 100644 index 0000000000..49641e1936 --- /dev/null +++ b/tests/unit/test-rcu-list.c @@ -0,0 +1,381 @@ +/* + * rcuq_test.c + * + * usage: rcuq_test + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (c) 2013 Mike D. Day, IBM Corporation. + */ + +#include "qemu/osdep.h" +#include "qemu/atomic.h" +#include "qemu/rcu.h" +#include "qemu/thread.h" +#include "qemu/rcu_queue.h" + +/* + * Test variables. + */ + +static QemuMutex counts_mutex; +static long long n_reads = 0LL; +static long long n_updates = 0LL; +static int64_t n_reclaims; +static int64_t n_nodes_removed; +static long long n_nodes = 0LL; +static int g_test_in_charge = 0; + +static int nthreadsrunning; + +#define GOFLAG_INIT 0 +#define GOFLAG_RUN 1 +#define GOFLAG_STOP 2 + +static int goflag = GOFLAG_INIT; + +#define RCU_READ_RUN 1000 +#define RCU_UPDATE_RUN 10 +#define NR_THREADS 100 +#define RCU_Q_LEN 100 + +static QemuThread threads[NR_THREADS]; +static struct rcu_reader_data *data[NR_THREADS]; +static int n_threads; + +static int select_random_el(int max) +{ + return (rand() % max); +} + + +static void create_thread(void *(*func)(void *)) +{ + if (n_threads >= NR_THREADS) { + fprintf(stderr, "Thread limit of %d exceeded!\n", NR_THREADS); + exit(-1); + } + qemu_thread_create(&threads[n_threads], "test", func, &data[n_threads], + QEMU_THREAD_JOINABLE); + n_threads++; +} + +static void wait_all_threads(void) +{ + int i; + + for (i = 0; i < n_threads; i++) { + qemu_thread_join(&threads[i]); + } + n_threads = 0; +} + +#ifndef TEST_LIST_TYPE +#define TEST_LIST_TYPE 1 +#endif + +struct list_element { +#if TEST_LIST_TYPE == 1 + QLIST_ENTRY(list_element) entry; +#elif TEST_LIST_TYPE == 2 + QSIMPLEQ_ENTRY(list_element) entry; +#elif TEST_LIST_TYPE == 3 + QTAILQ_ENTRY(list_element) entry; +#elif TEST_LIST_TYPE == 4 + QSLIST_ENTRY(list_element) entry; +#else +#error Invalid TEST_LIST_TYPE +#endif + struct rcu_head rcu; +}; + +static void reclaim_list_el(struct rcu_head *prcu) +{ + struct list_element *el = container_of(prcu, struct list_element, rcu); + g_free(el); + /* Accessed only from call_rcu thread. */ + qatomic_set_i64(&n_reclaims, n_reclaims + 1); +} + +#if TEST_LIST_TYPE == 1 +static QLIST_HEAD(, list_element) Q_list_head; + +#define TEST_NAME "qlist" +#define TEST_LIST_REMOVE_RCU QLIST_REMOVE_RCU +#define TEST_LIST_INSERT_AFTER_RCU QLIST_INSERT_AFTER_RCU +#define TEST_LIST_INSERT_HEAD_RCU QLIST_INSERT_HEAD_RCU +#define TEST_LIST_FOREACH_RCU QLIST_FOREACH_RCU +#define TEST_LIST_FOREACH_SAFE_RCU QLIST_FOREACH_SAFE_RCU + +#elif TEST_LIST_TYPE == 2 +static QSIMPLEQ_HEAD(, list_element) Q_list_head = + QSIMPLEQ_HEAD_INITIALIZER(Q_list_head); + +#define TEST_NAME "qsimpleq" +#define TEST_LIST_REMOVE_RCU(el, f) \ + QSIMPLEQ_REMOVE_RCU(&Q_list_head, el, list_element, f) + +#define TEST_LIST_INSERT_AFTER_RCU(list_el, el, f) \ + QSIMPLEQ_INSERT_AFTER_RCU(&Q_list_head, list_el, el, f) + +#define TEST_LIST_INSERT_HEAD_RCU QSIMPLEQ_INSERT_HEAD_RCU +#define TEST_LIST_FOREACH_RCU QSIMPLEQ_FOREACH_RCU +#define TEST_LIST_FOREACH_SAFE_RCU QSIMPLEQ_FOREACH_SAFE_RCU + +#elif TEST_LIST_TYPE == 3 +static QTAILQ_HEAD(, list_element) Q_list_head; + +#define TEST_NAME "qtailq" +#define TEST_LIST_REMOVE_RCU(el, f) QTAILQ_REMOVE_RCU(&Q_list_head, el, f) + +#define TEST_LIST_INSERT_AFTER_RCU(list_el, el, f) \ + QTAILQ_INSERT_AFTER_RCU(&Q_list_head, list_el, el, f) + +#define TEST_LIST_INSERT_HEAD_RCU QTAILQ_INSERT_HEAD_RCU +#define TEST_LIST_FOREACH_RCU QTAILQ_FOREACH_RCU +#define TEST_LIST_FOREACH_SAFE_RCU QTAILQ_FOREACH_SAFE_RCU + +#elif TEST_LIST_TYPE == 4 +static QSLIST_HEAD(, list_element) Q_list_head; + +#define TEST_NAME "qslist" +#define TEST_LIST_REMOVE_RCU(el, f) \ + QSLIST_REMOVE_RCU(&Q_list_head, el, list_element, f) + +#define TEST_LIST_INSERT_AFTER_RCU(list_el, el, f) \ + QSLIST_INSERT_AFTER_RCU(&Q_list_head, list_el, el, f) + +#define TEST_LIST_INSERT_HEAD_RCU QSLIST_INSERT_HEAD_RCU +#define TEST_LIST_FOREACH_RCU QSLIST_FOREACH_RCU +#define TEST_LIST_FOREACH_SAFE_RCU QSLIST_FOREACH_SAFE_RCU +#else +#error Invalid TEST_LIST_TYPE +#endif + +static void *rcu_q_reader(void *arg) +{ + long long n_reads_local = 0; + struct list_element *el; + + rcu_register_thread(); + + *(struct rcu_reader_data **)arg = &rcu_reader; + qatomic_inc(&nthreadsrunning); + while (qatomic_read(&goflag) == GOFLAG_INIT) { + g_usleep(1000); + } + + while (qatomic_read(&goflag) == GOFLAG_RUN) { + rcu_read_lock(); + TEST_LIST_FOREACH_RCU(el, &Q_list_head, entry) { + n_reads_local++; + if (qatomic_read(&goflag) == GOFLAG_STOP) { + break; + } + } + rcu_read_unlock(); + + g_usleep(100); + } + qemu_mutex_lock(&counts_mutex); + n_reads += n_reads_local; + qemu_mutex_unlock(&counts_mutex); + + rcu_unregister_thread(); + return NULL; +} + + +static void *rcu_q_updater(void *arg) +{ + int j, target_el; + long long n_nodes_local = 0; + long long n_updates_local = 0; + long long n_removed_local = 0; + struct list_element *el, *prev_el; + + *(struct rcu_reader_data **)arg = &rcu_reader; + qatomic_inc(&nthreadsrunning); + while (qatomic_read(&goflag) == GOFLAG_INIT) { + g_usleep(1000); + } + + while (qatomic_read(&goflag) == GOFLAG_RUN) { + target_el = select_random_el(RCU_Q_LEN); + j = 0; + /* FOREACH_RCU could work here but let's use both macros */ + TEST_LIST_FOREACH_SAFE_RCU(prev_el, &Q_list_head, entry, el) { + j++; + if (target_el == j) { + TEST_LIST_REMOVE_RCU(prev_el, entry); + /* may be more than one updater in the future */ + call_rcu1(&prev_el->rcu, reclaim_list_el); + n_removed_local++; + break; + } + } + if (qatomic_read(&goflag) == GOFLAG_STOP) { + break; + } + target_el = select_random_el(RCU_Q_LEN); + j = 0; + TEST_LIST_FOREACH_RCU(el, &Q_list_head, entry) { + j++; + if (target_el == j) { + struct list_element *new_el = g_new(struct list_element, 1); + n_nodes_local++; + TEST_LIST_INSERT_AFTER_RCU(el, new_el, entry); + break; + } + } + + n_updates_local += 2; + synchronize_rcu(); + } + synchronize_rcu(); + qemu_mutex_lock(&counts_mutex); + n_nodes += n_nodes_local; + n_updates += n_updates_local; + qatomic_set_i64(&n_nodes_removed, n_nodes_removed + n_removed_local); + qemu_mutex_unlock(&counts_mutex); + return NULL; +} + +static void rcu_qtest_init(void) +{ + struct list_element *new_el; + int i; + nthreadsrunning = 0; + srand(time(0)); + for (i = 0; i < RCU_Q_LEN; i++) { + new_el = g_new(struct list_element, 1); + TEST_LIST_INSERT_HEAD_RCU(&Q_list_head, new_el, entry); + } + qemu_mutex_lock(&counts_mutex); + n_nodes += RCU_Q_LEN; + qemu_mutex_unlock(&counts_mutex); +} + +static void rcu_qtest_run(int duration, int nreaders) +{ + int nthreads = nreaders + 1; + while (qatomic_read(&nthreadsrunning) < nthreads) { + g_usleep(1000); + } + + qatomic_set(&goflag, GOFLAG_RUN); + sleep(duration); + qatomic_set(&goflag, GOFLAG_STOP); + wait_all_threads(); +} + + +static void rcu_qtest(const char *test, int duration, int nreaders) +{ + int i; + long long n_removed_local = 0; + + struct list_element *el, *prev_el; + + rcu_qtest_init(); + for (i = 0; i < nreaders; i++) { + create_thread(rcu_q_reader); + } + create_thread(rcu_q_updater); + rcu_qtest_run(duration, nreaders); + + TEST_LIST_FOREACH_SAFE_RCU(prev_el, &Q_list_head, entry, el) { + TEST_LIST_REMOVE_RCU(prev_el, entry); + call_rcu1(&prev_el->rcu, reclaim_list_el); + n_removed_local++; + } + qemu_mutex_lock(&counts_mutex); + qatomic_set_i64(&n_nodes_removed, n_nodes_removed + n_removed_local); + qemu_mutex_unlock(&counts_mutex); + synchronize_rcu(); + while (qatomic_read_i64(&n_nodes_removed) > + qatomic_read_i64(&n_reclaims)) { + g_usleep(100); + synchronize_rcu(); + } + if (g_test_in_charge) { + g_assert_cmpint(qatomic_read_i64(&n_nodes_removed), ==, + qatomic_read_i64(&n_reclaims)); + } else { + printf("%s: %d readers; 1 updater; nodes read: " \ + "%lld, nodes removed: %"PRIi64"; nodes reclaimed: %"PRIi64"\n", + test, nthreadsrunning - 1, n_reads, + qatomic_read_i64(&n_nodes_removed), + qatomic_read_i64(&n_reclaims)); + exit(0); + } +} + +static void usage(int argc, char *argv[]) +{ + fprintf(stderr, "Usage: %s duration nreaders\n", argv[0]); + exit(-1); +} + +static int gtest_seconds; + +static void gtest_rcuq_one(void) +{ + rcu_qtest("rcuqtest", gtest_seconds / 4, 1); +} + +static void gtest_rcuq_few(void) +{ + rcu_qtest("rcuqtest", gtest_seconds / 4, 5); +} + +static void gtest_rcuq_many(void) +{ + rcu_qtest("rcuqtest", gtest_seconds / 2, 20); +} + + +int main(int argc, char *argv[]) +{ + int duration = 0, readers = 0; + + qemu_mutex_init(&counts_mutex); + if (argc >= 2) { + if (argv[1][0] == '-') { + g_test_init(&argc, &argv, NULL); + if (g_test_quick()) { + gtest_seconds = 4; + } else { + gtest_seconds = 20; + } + g_test_add_func("/rcu/"TEST_NAME"/single-threaded", gtest_rcuq_one); + g_test_add_func("/rcu/"TEST_NAME"/short-few", gtest_rcuq_few); + g_test_add_func("/rcu/"TEST_NAME"/long-many", gtest_rcuq_many); + g_test_in_charge = 1; + return g_test_run(); + } + duration = strtoul(argv[1], NULL, 0); + } + if (argc >= 3) { + readers = strtoul(argv[2], NULL, 0); + } + if (duration && readers) { + rcu_qtest(argv[0], duration, readers); + return 0; + } + + usage(argc, argv); + return -1; +} diff --git a/tests/unit/test-rcu-simpleq.c b/tests/unit/test-rcu-simpleq.c new file mode 100644 index 0000000000..057f7d33f7 --- /dev/null +++ b/tests/unit/test-rcu-simpleq.c @@ -0,0 +1,2 @@ +#define TEST_LIST_TYPE 2 +#include "test-rcu-list.c" diff --git a/tests/unit/test-rcu-slist.c b/tests/unit/test-rcu-slist.c new file mode 100644 index 0000000000..868e1e472e --- /dev/null +++ b/tests/unit/test-rcu-slist.c @@ -0,0 +1,2 @@ +#define TEST_LIST_TYPE 4 +#include "test-rcu-list.c" diff --git a/tests/unit/test-rcu-tailq.c b/tests/unit/test-rcu-tailq.c new file mode 100644 index 0000000000..8d487e0ee0 --- /dev/null +++ b/tests/unit/test-rcu-tailq.c @@ -0,0 +1,2 @@ +#define TEST_LIST_TYPE 3 +#include "test-rcu-list.c" diff --git a/tests/unit/test-replication.c b/tests/unit/test-replication.c new file mode 100644 index 0000000000..b067240add --- /dev/null +++ b/tests/unit/test-replication.c @@ -0,0 +1,623 @@ +/* + * Block replication tests + * + * Copyright (c) 2016 FUJITSU LIMITED + * Author: Changlong Xie + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" + +#include "qapi/error.h" +#include "qapi/qmp/qdict.h" +#include "qemu/option.h" +#include "qemu/main-loop.h" +#include "replication.h" +#include "block/block_int.h" +#include "block/qdict.h" +#include "sysemu/block-backend.h" + +#define IMG_SIZE (64 * 1024 * 1024) + +/* primary */ +#define P_ID "primary-id" +static char *p_local_disk; + +/* secondary */ +#define S_ID "secondary-id" +#define S_LOCAL_DISK_ID "secondary-local-disk-id" +static char *s_local_disk; +static char *s_active_disk; +static char *s_hidden_disk; + +/* FIXME: steal from blockdev.c */ +QemuOptsList qemu_drive_opts = { + .name = "drive", + .head = QTAILQ_HEAD_INITIALIZER(qemu_drive_opts.head), + .desc = { + { /* end of list */ } + }, +}; + +#define NOT_DONE 0x7fffffff + +static void blk_rw_done(void *opaque, int ret) +{ + *(int *)opaque = ret; +} + +static void test_blk_read(BlockBackend *blk, long pattern, + int64_t pattern_offset, int64_t pattern_count, + int64_t offset, int64_t count, + bool expect_failed) +{ + void *pattern_buf = NULL; + QEMUIOVector qiov; + void *cmp_buf = NULL; + int async_ret = NOT_DONE; + + if (pattern) { + cmp_buf = g_malloc(pattern_count); + memset(cmp_buf, pattern, pattern_count); + } + + pattern_buf = g_malloc(count); + if (pattern) { + memset(pattern_buf, pattern, count); + } else { + memset(pattern_buf, 0x00, count); + } + + qemu_iovec_init(&qiov, 1); + qemu_iovec_add(&qiov, pattern_buf, count); + + blk_aio_preadv(blk, offset, &qiov, 0, blk_rw_done, &async_ret); + while (async_ret == NOT_DONE) { + main_loop_wait(false); + } + + if (expect_failed) { + g_assert(async_ret != 0); + } else { + g_assert(async_ret == 0); + if (pattern) { + g_assert(memcmp(pattern_buf + pattern_offset, + cmp_buf, pattern_count) <= 0); + } + } + + g_free(pattern_buf); + g_free(cmp_buf); + qemu_iovec_destroy(&qiov); +} + +static void test_blk_write(BlockBackend *blk, long pattern, int64_t offset, + int64_t count, bool expect_failed) +{ + void *pattern_buf = NULL; + QEMUIOVector qiov; + int async_ret = NOT_DONE; + + pattern_buf = g_malloc(count); + if (pattern) { + memset(pattern_buf, pattern, count); + } else { + memset(pattern_buf, 0x00, count); + } + + qemu_iovec_init(&qiov, 1); + qemu_iovec_add(&qiov, pattern_buf, count); + + blk_aio_pwritev(blk, offset, &qiov, 0, blk_rw_done, &async_ret); + while (async_ret == NOT_DONE) { + main_loop_wait(false); + } + + if (expect_failed) { + g_assert(async_ret != 0); + } else { + g_assert(async_ret == 0); + } + + g_free(pattern_buf); + qemu_iovec_destroy(&qiov); +} + +/* + * Create a uniquely-named empty temporary file. + */ +static void make_temp(char *template) +{ + int fd; + + fd = mkstemp(template); + g_assert(fd >= 0); + close(fd); +} + +static void prepare_imgs(void) +{ + make_temp(p_local_disk); + make_temp(s_local_disk); + make_temp(s_active_disk); + make_temp(s_hidden_disk); + + /* Primary */ + bdrv_img_create(p_local_disk, "qcow2", NULL, NULL, NULL, IMG_SIZE, + BDRV_O_RDWR, true, &error_abort); + + /* Secondary */ + bdrv_img_create(s_local_disk, "qcow2", NULL, NULL, NULL, IMG_SIZE, + BDRV_O_RDWR, true, &error_abort); + bdrv_img_create(s_active_disk, "qcow2", NULL, NULL, NULL, IMG_SIZE, + BDRV_O_RDWR, true, &error_abort); + bdrv_img_create(s_hidden_disk, "qcow2", NULL, NULL, NULL, IMG_SIZE, + BDRV_O_RDWR, true, &error_abort); +} + +static void cleanup_imgs(void) +{ + /* Primary */ + unlink(p_local_disk); + + /* Secondary */ + unlink(s_local_disk); + unlink(s_active_disk); + unlink(s_hidden_disk); +} + +static BlockBackend *start_primary(void) +{ + BlockBackend *blk; + QemuOpts *opts; + QDict *qdict; + char *cmdline; + + cmdline = g_strdup_printf("driver=replication,mode=primary,node-name=xxx," + "file.driver=qcow2,file.file.filename=%s," + "file.file.locking=off" + , p_local_disk); + opts = qemu_opts_parse_noisily(&qemu_drive_opts, cmdline, false); + g_free(cmdline); + + qdict = qemu_opts_to_qdict(opts, NULL); + qdict_set_default_str(qdict, BDRV_OPT_CACHE_DIRECT, "off"); + qdict_set_default_str(qdict, BDRV_OPT_CACHE_NO_FLUSH, "off"); + + blk = blk_new_open(NULL, NULL, qdict, BDRV_O_RDWR, &error_abort); + g_assert(blk); + + monitor_add_blk(blk, P_ID, &error_abort); + + qemu_opts_del(opts); + + return blk; +} + +static void teardown_primary(void) +{ + BlockBackend *blk; + AioContext *ctx; + + /* remove P_ID */ + blk = blk_by_name(P_ID); + assert(blk); + + ctx = blk_get_aio_context(blk); + aio_context_acquire(ctx); + monitor_remove_blk(blk); + blk_unref(blk); + aio_context_release(ctx); +} + +static void test_primary_read(void) +{ + BlockBackend *blk; + + blk = start_primary(); + + /* read from 0 to IMG_SIZE */ + test_blk_read(blk, 0, 0, IMG_SIZE, 0, IMG_SIZE, true); + + teardown_primary(); +} + +static void test_primary_write(void) +{ + BlockBackend *blk; + + blk = start_primary(); + + /* write from 0 to IMG_SIZE */ + test_blk_write(blk, 0, 0, IMG_SIZE, true); + + teardown_primary(); +} + +static void test_primary_start(void) +{ + BlockBackend *blk = NULL; + + blk = start_primary(); + + replication_start_all(REPLICATION_MODE_PRIMARY, &error_abort); + + /* read from 0 to IMG_SIZE */ + test_blk_read(blk, 0, 0, IMG_SIZE, 0, IMG_SIZE, true); + + /* write 0x22 from 0 to IMG_SIZE */ + test_blk_write(blk, 0x22, 0, IMG_SIZE, false); + + teardown_primary(); +} + +static void test_primary_stop(void) +{ + bool failover = true; + + start_primary(); + + replication_start_all(REPLICATION_MODE_PRIMARY, &error_abort); + + replication_stop_all(failover, &error_abort); + + teardown_primary(); +} + +static void test_primary_do_checkpoint(void) +{ + start_primary(); + + replication_start_all(REPLICATION_MODE_PRIMARY, &error_abort); + + replication_do_checkpoint_all(&error_abort); + + teardown_primary(); +} + +static void test_primary_get_error_all(void) +{ + start_primary(); + + replication_start_all(REPLICATION_MODE_PRIMARY, &error_abort); + + replication_get_error_all(&error_abort); + + teardown_primary(); +} + +static BlockBackend *start_secondary(void) +{ + QemuOpts *opts; + QDict *qdict; + BlockBackend *blk; + char *cmdline; + + /* add s_local_disk and forge S_LOCAL_DISK_ID */ + cmdline = g_strdup_printf("file.filename=%s,driver=qcow2," + "file.locking=off", + s_local_disk); + opts = qemu_opts_parse_noisily(&qemu_drive_opts, cmdline, false); + g_free(cmdline); + + qdict = qemu_opts_to_qdict(opts, NULL); + qdict_set_default_str(qdict, BDRV_OPT_CACHE_DIRECT, "off"); + qdict_set_default_str(qdict, BDRV_OPT_CACHE_NO_FLUSH, "off"); + + blk = blk_new_open(NULL, NULL, qdict, BDRV_O_RDWR, &error_abort); + assert(blk); + monitor_add_blk(blk, S_LOCAL_DISK_ID, &error_abort); + + /* format s_local_disk with pattern "0x11" */ + test_blk_write(blk, 0x11, 0, IMG_SIZE, false); + + qemu_opts_del(opts); + + /* add S_(ACTIVE/HIDDEN)_DISK and forge S_ID */ + cmdline = g_strdup_printf("driver=replication,mode=secondary,top-id=%s," + "file.driver=qcow2,file.file.filename=%s," + "file.file.locking=off," + "file.backing.driver=qcow2," + "file.backing.file.filename=%s," + "file.backing.file.locking=off," + "file.backing.backing=%s" + , S_ID, s_active_disk, s_hidden_disk + , S_LOCAL_DISK_ID); + opts = qemu_opts_parse_noisily(&qemu_drive_opts, cmdline, false); + g_free(cmdline); + + qdict = qemu_opts_to_qdict(opts, NULL); + qdict_set_default_str(qdict, BDRV_OPT_CACHE_DIRECT, "off"); + qdict_set_default_str(qdict, BDRV_OPT_CACHE_NO_FLUSH, "off"); + + blk = blk_new_open(NULL, NULL, qdict, BDRV_O_RDWR, &error_abort); + assert(blk); + monitor_add_blk(blk, S_ID, &error_abort); + + qemu_opts_del(opts); + + return blk; +} + +static void teardown_secondary(void) +{ + /* only need to destroy two BBs */ + BlockBackend *blk; + AioContext *ctx; + + /* remove S_LOCAL_DISK_ID */ + blk = blk_by_name(S_LOCAL_DISK_ID); + assert(blk); + + ctx = blk_get_aio_context(blk); + aio_context_acquire(ctx); + monitor_remove_blk(blk); + blk_unref(blk); + aio_context_release(ctx); + + /* remove S_ID */ + blk = blk_by_name(S_ID); + assert(blk); + + ctx = blk_get_aio_context(blk); + aio_context_acquire(ctx); + monitor_remove_blk(blk); + blk_unref(blk); + aio_context_release(ctx); +} + +static void test_secondary_read(void) +{ + BlockBackend *blk; + + blk = start_secondary(); + + /* read from 0 to IMG_SIZE */ + test_blk_read(blk, 0, 0, IMG_SIZE, 0, IMG_SIZE, true); + + teardown_secondary(); +} + +static void test_secondary_write(void) +{ + BlockBackend *blk; + + blk = start_secondary(); + + /* write from 0 to IMG_SIZE */ + test_blk_write(blk, 0, 0, IMG_SIZE, true); + + teardown_secondary(); +} + +#ifndef _WIN32 +static void test_secondary_start(void) +{ + BlockBackend *top_blk, *local_blk; + bool failover = true; + + top_blk = start_secondary(); + replication_start_all(REPLICATION_MODE_SECONDARY, &error_abort); + + /* read from s_local_disk (0, IMG_SIZE) */ + test_blk_read(top_blk, 0x11, 0, IMG_SIZE, 0, IMG_SIZE, false); + + /* write 0x22 to s_local_disk (IMG_SIZE / 2, IMG_SIZE) */ + local_blk = blk_by_name(S_LOCAL_DISK_ID); + test_blk_write(local_blk, 0x22, IMG_SIZE / 2, IMG_SIZE / 2, false); + + /* replication will backup s_local_disk to s_hidden_disk */ + test_blk_read(top_blk, 0x11, IMG_SIZE / 2, + IMG_SIZE / 2, 0, IMG_SIZE, false); + + /* write 0x33 to s_active_disk (0, IMG_SIZE / 2) */ + test_blk_write(top_blk, 0x33, 0, IMG_SIZE / 2, false); + + /* read from s_active_disk (0, IMG_SIZE/2) */ + test_blk_read(top_blk, 0x33, 0, IMG_SIZE / 2, + 0, IMG_SIZE / 2, false); + + /* unblock top_bs */ + replication_stop_all(failover, &error_abort); + + teardown_secondary(); +} + + +static void test_secondary_stop(void) +{ + BlockBackend *top_blk, *local_blk; + bool failover = true; + + top_blk = start_secondary(); + replication_start_all(REPLICATION_MODE_SECONDARY, &error_abort); + + /* write 0x22 to s_local_disk (IMG_SIZE / 2, IMG_SIZE) */ + local_blk = blk_by_name(S_LOCAL_DISK_ID); + test_blk_write(local_blk, 0x22, IMG_SIZE / 2, IMG_SIZE / 2, false); + + /* replication will backup s_local_disk to s_hidden_disk */ + test_blk_read(top_blk, 0x11, IMG_SIZE / 2, + IMG_SIZE / 2, 0, IMG_SIZE, false); + + /* write 0x33 to s_active_disk (0, IMG_SIZE / 2) */ + test_blk_write(top_blk, 0x33, 0, IMG_SIZE / 2, false); + + /* do active commit */ + replication_stop_all(failover, &error_abort); + + /* read from s_local_disk (0, IMG_SIZE / 2) */ + test_blk_read(top_blk, 0x33, 0, IMG_SIZE / 2, + 0, IMG_SIZE / 2, false); + + + /* read from s_local_disk (IMG_SIZE / 2, IMG_SIZE) */ + test_blk_read(top_blk, 0x22, IMG_SIZE / 2, + IMG_SIZE / 2, 0, IMG_SIZE, false); + + teardown_secondary(); +} + +static void test_secondary_continuous_replication(void) +{ + BlockBackend *top_blk, *local_blk; + + top_blk = start_secondary(); + replication_start_all(REPLICATION_MODE_SECONDARY, &error_abort); + + /* write 0x22 to s_local_disk (IMG_SIZE / 2, IMG_SIZE) */ + local_blk = blk_by_name(S_LOCAL_DISK_ID); + test_blk_write(local_blk, 0x22, IMG_SIZE / 2, IMG_SIZE / 2, false); + + /* replication will backup s_local_disk to s_hidden_disk */ + test_blk_read(top_blk, 0x11, IMG_SIZE / 2, + IMG_SIZE / 2, 0, IMG_SIZE, false); + + /* write 0x33 to s_active_disk (0, IMG_SIZE / 2) */ + test_blk_write(top_blk, 0x33, 0, IMG_SIZE / 2, false); + + /* do failover (active commit) */ + replication_stop_all(true, &error_abort); + + /* it should ignore all requests from now on */ + + /* start after failover */ + replication_start_all(REPLICATION_MODE_PRIMARY, &error_abort); + + /* checkpoint */ + replication_do_checkpoint_all(&error_abort); + + /* stop */ + replication_stop_all(true, &error_abort); + + /* read from s_local_disk (0, IMG_SIZE / 2) */ + test_blk_read(top_blk, 0x33, 0, IMG_SIZE / 2, + 0, IMG_SIZE / 2, false); + + + /* read from s_local_disk (IMG_SIZE / 2, IMG_SIZE) */ + test_blk_read(top_blk, 0x22, IMG_SIZE / 2, + IMG_SIZE / 2, 0, IMG_SIZE, false); + + teardown_secondary(); +} + +static void test_secondary_do_checkpoint(void) +{ + BlockBackend *top_blk, *local_blk; + bool failover = true; + + top_blk = start_secondary(); + replication_start_all(REPLICATION_MODE_SECONDARY, &error_abort); + + /* write 0x22 to s_local_disk (IMG_SIZE / 2, IMG_SIZE) */ + local_blk = blk_by_name(S_LOCAL_DISK_ID); + test_blk_write(local_blk, 0x22, IMG_SIZE / 2, + IMG_SIZE / 2, false); + + /* replication will backup s_local_disk to s_hidden_disk */ + test_blk_read(top_blk, 0x11, IMG_SIZE / 2, + IMG_SIZE / 2, 0, IMG_SIZE, false); + + replication_do_checkpoint_all(&error_abort); + + /* after checkpoint, read pattern 0x22 from s_local_disk */ + test_blk_read(top_blk, 0x22, IMG_SIZE / 2, + IMG_SIZE / 2, 0, IMG_SIZE, false); + + /* unblock top_bs */ + replication_stop_all(failover, &error_abort); + + teardown_secondary(); +} + +static void test_secondary_get_error_all(void) +{ + bool failover = true; + + start_secondary(); + replication_start_all(REPLICATION_MODE_SECONDARY, &error_abort); + + replication_get_error_all(&error_abort); + + /* unblock top_bs */ + replication_stop_all(failover, &error_abort); + + teardown_secondary(); +} +#endif + +static void sigabrt_handler(int signo) +{ + cleanup_imgs(); +} + +static void setup_sigabrt_handler(void) +{ +#ifdef _WIN32 + signal(SIGABRT, sigabrt_handler); +#else + struct sigaction sigact; + + sigact = (struct sigaction) { + .sa_handler = sigabrt_handler, + .sa_flags = SA_RESETHAND, + }; + sigemptyset(&sigact.sa_mask); + sigaction(SIGABRT, &sigact, NULL); +#endif +} + +int main(int argc, char **argv) +{ + int ret; + const char *tmpdir = g_get_tmp_dir(); + p_local_disk = g_strdup_printf("%s/p_local_disk.XXXXXX", tmpdir); + s_local_disk = g_strdup_printf("%s/s_local_disk.XXXXXX", tmpdir); + s_active_disk = g_strdup_printf("%s/s_active_disk.XXXXXX", tmpdir); + s_hidden_disk = g_strdup_printf("%s/s_hidden_disk.XXXXXX", tmpdir); + qemu_init_main_loop(&error_fatal); + bdrv_init(); + + g_test_init(&argc, &argv, NULL); + setup_sigabrt_handler(); + + prepare_imgs(); + + /* Primary */ + g_test_add_func("/replication/primary/read", test_primary_read); + g_test_add_func("/replication/primary/write", test_primary_write); + g_test_add_func("/replication/primary/start", test_primary_start); + g_test_add_func("/replication/primary/stop", test_primary_stop); + g_test_add_func("/replication/primary/do_checkpoint", + test_primary_do_checkpoint); + g_test_add_func("/replication/primary/get_error_all", + test_primary_get_error_all); + + /* Secondary */ + g_test_add_func("/replication/secondary/read", test_secondary_read); + g_test_add_func("/replication/secondary/write", test_secondary_write); +#ifndef _WIN32 + g_test_add_func("/replication/secondary/start", test_secondary_start); + g_test_add_func("/replication/secondary/stop", test_secondary_stop); + g_test_add_func("/replication/secondary/continuous_replication", + test_secondary_continuous_replication); + g_test_add_func("/replication/secondary/do_checkpoint", + test_secondary_do_checkpoint); + g_test_add_func("/replication/secondary/get_error_all", + test_secondary_get_error_all); +#endif + + ret = g_test_run(); + + cleanup_imgs(); + + g_free(p_local_disk); + g_free(s_local_disk); + g_free(s_active_disk); + g_free(s_hidden_disk); + + return ret; +} diff --git a/tests/unit/test-shift128.c b/tests/unit/test-shift128.c new file mode 100644 index 0000000000..f3ff736e5c --- /dev/null +++ b/tests/unit/test-shift128.c @@ -0,0 +1,139 @@ +/* + * Test unsigned left and right shift + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "qemu/host-utils.h" + +typedef struct { + uint64_t low; + uint64_t high; + uint64_t rlow; + uint64_t rhigh; + int32_t shift; + bool overflow; +} test_data; + +static const test_data test_ltable[] = { + { 0x4C7ULL, 0x0ULL, 0x00000000000004C7ULL, + 0x0000000000000000ULL, 0, false }, + { 0x001ULL, 0x0ULL, 0x0000000000000002ULL, + 0x0000000000000000ULL, 1, false }, + { 0x001ULL, 0x0ULL, 0x0000000000000004ULL, + 0x0000000000000000ULL, 2, false }, + { 0x001ULL, 0x0ULL, 0x0000000000000010ULL, + 0x0000000000000000ULL, 4, false }, + { 0x001ULL, 0x0ULL, 0x0000000000000100ULL, + 0x0000000000000000ULL, 8, false }, + { 0x001ULL, 0x0ULL, 0x0000000000010000ULL, + 0x0000000000000000ULL, 16, false }, + { 0x001ULL, 0x0ULL, 0x0000000080000000ULL, + 0x0000000000000000ULL, 31, false }, + { 0x001ULL, 0x0ULL, 0x0000200000000000ULL, + 0x0000000000000000ULL, 45, false }, + { 0x001ULL, 0x0ULL, 0x1000000000000000ULL, + 0x0000000000000000ULL, 60, false }, + { 0x001ULL, 0x0ULL, 0x0000000000000000ULL, + 0x0000000000000001ULL, 64, false }, + { 0x001ULL, 0x0ULL, 0x0000000000000000ULL, + 0x0000000000010000ULL, 80, false }, + { 0x001ULL, 0x0ULL, 0x0000000000000000ULL, + 0x8000000000000000ULL, 127, false }, + { 0x000ULL, 0x1ULL, 0x0000000000000000ULL, + 0x0000000000000000ULL, 64, true }, + { 0x008ULL, 0x0ULL, 0x0000000000000000ULL, + 0x0000000000000008ULL, 64, false }, + { 0x008ULL, 0x0ULL, 0x0000000000000000ULL, + 0x8000000000000000ULL, 124, false }, + { 0x001ULL, 0x0ULL, 0x0000000000000000ULL, + 0x4000000000000000ULL, 126, false }, + { 0x001ULL, 0x0ULL, 0x0000000000000000ULL, + 0x8000000000000000ULL, 127, false }, + { 0x001ULL, 0x0ULL, 0x0000000000000001ULL, + 0x0000000000000000ULL, 128, false }, + { 0x000ULL, 0x0ULL, 0x0000000000000000ULL, + 0x0000000000000000ULL, 200, false }, + { 0x001ULL, 0x0ULL, 0x0000000000000000ULL, + 0x0000000000000100ULL, 200, false }, + { 0x001ULL, 0x0ULL, 0x0000000000000000ULL, + 0x8000000000000000ULL, -1, false }, + { 0x001ULL, 0x0ULL, 0x0000000000000000ULL, + 0x8000000000000000ULL, INT32_MAX, false }, + { 0x001ULL, 0x0ULL, 0x0000000000000000ULL, + 0x4000000000000000ULL, -2, false }, + { 0x001ULL, 0x0ULL, 0x0000000000000000ULL, + 0x4000000000000000ULL, INT32_MAX - 1, false }, + { 0x8888888888888888ULL, 0x9999999999999999ULL, + 0x8000000000000000ULL, 0x9888888888888888ULL, 60, true }, + { 0x8888888888888888ULL, 0x9999999999999999ULL, + 0x0000000000000000ULL, 0x8888888888888888ULL, 64, true }, +}; + +static const test_data test_rtable[] = { + { 0x00000000000004C7ULL, 0x0ULL, 0x00000000000004C7ULL, 0x0ULL, 0, false }, + { 0x0800000000000000ULL, 0x0ULL, 0x0400000000000000ULL, 0x0ULL, 1, false }, + { 0x0800000000000000ULL, 0x0ULL, 0x0200000000000000ULL, 0x0ULL, 2, false }, + { 0x0800000000000000ULL, 0x0ULL, 0x0008000000000000ULL, 0x0ULL, 8, false }, + { 0x0800000000000000ULL, 0x0ULL, 0x0000080000000000ULL, 0x0ULL, 16, false }, + { 0x0800000000000000ULL, 0x0ULL, 0x0000000008000000ULL, 0x0ULL, 32, false }, + { 0x8000000000000000ULL, 0x0ULL, 0x0000000000000001ULL, 0x0ULL, 63, false }, + { 0x8000000000000000ULL, 0x0ULL, 0x0000000000000000ULL, 0x0ULL, 64, false }, + { 0x0000000000000000ULL, 0x8000000000000000ULL, + 0x0000000000000000ULL, 0x8000000000000000ULL, 128, false }, + { 0x0000000000000000ULL, 0x8000000000000000ULL, + 0x0080000000000000ULL, 0x0000000000000000ULL, 200, false }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, + 0x0000000000000000ULL, 0x0000000000000000ULL, 200, false }, + { 0x0000000000000000ULL, 0x8000000000000000ULL, + 0x0000000000000000ULL, 0x0000000000000080ULL, -200, false }, + { 0x8000000000000000ULL, 0x8000000000000000ULL, + 0x0000000080000000ULL, 0x0000000080000000ULL, 32, false }, + { 0x0800000000000000ULL, 0x0800000000000000ULL, + 0x0800000000000000ULL, 0x0000000000000000ULL, 64, false }, + { 0x0800000000000000ULL, 0x0800000000000000ULL, + 0x0008000000000000ULL, 0x0000000000000000ULL, 72, false }, + { 0x8000000000000000ULL, 0x8000000000000000ULL, + 0x0000000000000001ULL, 0x0000000000000000ULL, 127, false }, + { 0x0000000000000000ULL, 0x8000000000000000ULL, + 0x0000000000000001ULL, 0x0000000000000000ULL, -1, false }, + { 0x0000000000000000ULL, 0x8000000000000000ULL, + 0x0000000000000002ULL, 0x0000000000000000ULL, -2, false }, +}; + +static void test_lshift(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(test_ltable); ++i) { + bool overflow = false; + test_data tmp = test_ltable[i]; + ulshift(&tmp.low, &tmp.high, tmp.shift, &overflow); + g_assert_cmpuint(tmp.low, ==, tmp.rlow); + g_assert_cmpuint(tmp.high, ==, tmp.rhigh); + g_assert_cmpuint(tmp.overflow, ==, overflow); + } +} + +static void test_rshift(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(test_rtable); ++i) { + test_data tmp = test_rtable[i]; + urshift(&tmp.low, &tmp.high, tmp.shift); + g_assert_cmpuint(tmp.low, ==, tmp.rlow); + g_assert_cmpuint(tmp.high, ==, tmp.rhigh); + } +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + g_test_add_func("/host-utils/test_lshift", test_lshift); + g_test_add_func("/host-utils/test_rshift", test_rshift); + return g_test_run(); +} diff --git a/tests/unit/test-string-input-visitor.c b/tests/unit/test-string-input-visitor.c new file mode 100644 index 0000000000..249faafc9d --- /dev/null +++ b/tests/unit/test-string-input-visitor.c @@ -0,0 +1,501 @@ +/* + * String Input Visitor unit-tests. + * + * Copyright (C) 2012 Red Hat Inc. + * + * Authors: + * Paolo Bonzini (based on test-qobject-input-visitor) + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" + +#include "qemu-common.h" +#include "qapi/error.h" +#include "qapi/string-input-visitor.h" +#include "test-qapi-visit.h" + +typedef struct TestInputVisitorData { + Visitor *v; +} TestInputVisitorData; + +static void visitor_input_teardown(TestInputVisitorData *data, + const void *unused) +{ + if (data->v) { + visit_free(data->v); + data->v = NULL; + } +} + +/* This is provided instead of a test setup function so that the JSON + string used by the tests are kept in the test functions (and not + int main()) */ +static +Visitor *visitor_input_test_init(TestInputVisitorData *data, + const char *string) +{ + visitor_input_teardown(data, NULL); + + data->v = string_input_visitor_new(string); + g_assert(data->v); + return data->v; +} + +static void test_visitor_in_int(TestInputVisitorData *data, + const void *unused) +{ + int64_t res = 0, value = -42; + Error *err = NULL; + Visitor *v; + + v = visitor_input_test_init(data, "-42"); + + visit_type_int(v, NULL, &res, &error_abort); + g_assert_cmpint(res, ==, value); + + v = visitor_input_test_init(data, "not an int"); + + visit_type_int(v, NULL, &res, &err); + error_free_or_abort(&err); + + v = visitor_input_test_init(data, ""); + + visit_type_int(v, NULL, &res, &err); + error_free_or_abort(&err); +} + +static void check_ilist(Visitor *v, int64_t *expected, size_t n) +{ + int64List *res = NULL; + int64List *tail; + int i; + + visit_type_int64List(v, NULL, &res, &error_abort); + tail = res; + for (i = 0; i < n; i++) { + g_assert(tail); + g_assert_cmpint(tail->value, ==, expected[i]); + tail = tail->next; + } + g_assert(!tail); + + qapi_free_int64List(res); +} + +static void check_ulist(Visitor *v, uint64_t *expected, size_t n) +{ + uint64List *res = NULL; + uint64List *tail; + int i; + + visit_type_uint64List(v, NULL, &res, &error_abort); + tail = res; + for (i = 0; i < n; i++) { + g_assert(tail); + g_assert_cmpuint(tail->value, ==, expected[i]); + tail = tail->next; + } + g_assert(!tail); + + qapi_free_uint64List(res); +} + +static void test_visitor_in_intList(TestInputVisitorData *data, + const void *unused) +{ + int64_t expect1[] = { 1, 2, 0, 2, 3, 4, 20, 5, 6, 7, + 8, 9, 1, 2, 3, 4, 5, 6, 7, 8 }; + int64_t expect2[] = { 32767, -32768, -32767 }; + int64_t expect3[] = { INT64_MIN, INT64_MAX }; + int64_t expect4[] = { 1 }; + int64_t expect5[] = { INT64_MAX - 2, INT64_MAX - 1, INT64_MAX }; + Error *err = NULL; + int64List *res = NULL; + Visitor *v; + int64_t val; + + /* Valid lists */ + + v = visitor_input_test_init(data, "1,2,0,2-4,20,5-9,1-8"); + check_ilist(v, expect1, ARRAY_SIZE(expect1)); + + v = visitor_input_test_init(data, "32767,-32768--32767"); + check_ilist(v, expect2, ARRAY_SIZE(expect2)); + + v = visitor_input_test_init(data, + "-9223372036854775808,9223372036854775807"); + check_ilist(v, expect3, ARRAY_SIZE(expect3)); + + v = visitor_input_test_init(data, "1-1"); + check_ilist(v, expect4, ARRAY_SIZE(expect4)); + + v = visitor_input_test_init(data, + "9223372036854775805-9223372036854775807"); + check_ilist(v, expect5, ARRAY_SIZE(expect5)); + + /* Value too large */ + + v = visitor_input_test_init(data, "9223372036854775808"); + visit_type_int64List(v, NULL, &res, &err); + error_free_or_abort(&err); + g_assert(!res); + + /* Value too small */ + + v = visitor_input_test_init(data, "-9223372036854775809"); + visit_type_int64List(v, NULL, &res, &err); + error_free_or_abort(&err); + g_assert(!res); + + /* Range not ascending */ + + v = visitor_input_test_init(data, "3-1"); + visit_type_int64List(v, NULL, &res, &err); + error_free_or_abort(&err); + g_assert(!res); + + v = visitor_input_test_init(data, "9223372036854775807-0"); + visit_type_int64List(v, NULL, &res, &err); + error_free_or_abort(&err); + g_assert(!res); + + /* Range too big (65536 is the limit against DOS attacks) */ + + v = visitor_input_test_init(data, "0-65536"); + visit_type_int64List(v, NULL, &res, &err); + error_free_or_abort(&err); + g_assert(!res); + + /* Empty list */ + + v = visitor_input_test_init(data, ""); + visit_type_int64List(v, NULL, &res, &error_abort); + g_assert(!res); + + /* Not a list */ + + v = visitor_input_test_init(data, "not an int list"); + + visit_type_int64List(v, NULL, &res, &err); + error_free_or_abort(&err); + g_assert(!res); + + /* Unvisited list tail */ + + v = visitor_input_test_init(data, "0,2-3"); + + visit_start_list(v, NULL, NULL, 0, &error_abort); + visit_type_int64(v, NULL, &val, &error_abort); + g_assert_cmpint(val, ==, 0); + visit_type_int64(v, NULL, &val, &error_abort); + g_assert_cmpint(val, ==, 2); + + visit_check_list(v, &err); + error_free_or_abort(&err); + visit_end_list(v, NULL); + + /* Visit beyond end of list */ + + v = visitor_input_test_init(data, "0"); + + visit_start_list(v, NULL, NULL, 0, &error_abort); + visit_type_int64(v, NULL, &val, &err); + g_assert_cmpint(val, ==, 0); + visit_type_int64(v, NULL, &val, &err); + error_free_or_abort(&err); + + visit_check_list(v, &error_abort); + visit_end_list(v, NULL); +} + +static void test_visitor_in_uintList(TestInputVisitorData *data, + const void *unused) +{ + uint64_t expect1[] = { 1, 2, 0, 2, 3, 4, 20, 5, 6, 7, + 8, 9, 1, 2, 3, 4, 5, 6, 7, 8 }; + uint64_t expect2[] = { 32767, -32768, -32767 }; + uint64_t expect3[] = { INT64_MIN, INT64_MAX }; + uint64_t expect4[] = { 1 }; + uint64_t expect5[] = { UINT64_MAX }; + uint64_t expect6[] = { UINT64_MAX - 2, UINT64_MAX - 1, UINT64_MAX }; + Error *err = NULL; + uint64List *res = NULL; + Visitor *v; + uint64_t val; + + /* Valid lists */ + + v = visitor_input_test_init(data, "1,2,0,2-4,20,5-9,1-8"); + check_ulist(v, expect1, ARRAY_SIZE(expect1)); + + v = visitor_input_test_init(data, "32767,-32768--32767"); + check_ulist(v, expect2, ARRAY_SIZE(expect2)); + + v = visitor_input_test_init(data, + "-9223372036854775808,9223372036854775807"); + check_ulist(v, expect3, ARRAY_SIZE(expect3)); + + v = visitor_input_test_init(data, "1-1"); + check_ulist(v, expect4, ARRAY_SIZE(expect4)); + + v = visitor_input_test_init(data, "18446744073709551615"); + check_ulist(v, expect5, ARRAY_SIZE(expect5)); + + v = visitor_input_test_init(data, + "18446744073709551613-18446744073709551615"); + check_ulist(v, expect6, ARRAY_SIZE(expect6)); + + /* Value too large */ + + v = visitor_input_test_init(data, "18446744073709551616"); + visit_type_uint64List(v, NULL, &res, &err); + error_free_or_abort(&err); + g_assert(!res); + + /* Value too small */ + + v = visitor_input_test_init(data, "-18446744073709551616"); + visit_type_uint64List(v, NULL, &res, &err); + error_free_or_abort(&err); + g_assert(!res); + + /* Range not ascending */ + + v = visitor_input_test_init(data, "3-1"); + visit_type_uint64List(v, NULL, &res, &err); + error_free_or_abort(&err); + g_assert(!res); + + v = visitor_input_test_init(data, "18446744073709551615-0"); + visit_type_uint64List(v, NULL, &res, &err); + error_free_or_abort(&err); + g_assert(!res); + + /* Range too big (65536 is the limit against DOS attacks) */ + + v = visitor_input_test_init(data, "0-65536"); + visit_type_uint64List(v, NULL, &res, &err); + error_free_or_abort(&err); + g_assert(!res); + + /* Empty list */ + + v = visitor_input_test_init(data, ""); + visit_type_uint64List(v, NULL, &res, &error_abort); + g_assert(!res); + + /* Not a list */ + + v = visitor_input_test_init(data, "not an uint list"); + + visit_type_uint64List(v, NULL, &res, &err); + error_free_or_abort(&err); + g_assert(!res); + + /* Unvisited list tail */ + + v = visitor_input_test_init(data, "0,2-3"); + + visit_start_list(v, NULL, NULL, 0, &error_abort); + visit_type_uint64(v, NULL, &val, &error_abort); + g_assert_cmpuint(val, ==, 0); + visit_type_uint64(v, NULL, &val, &error_abort); + g_assert_cmpuint(val, ==, 2); + + visit_check_list(v, &err); + error_free_or_abort(&err); + visit_end_list(v, NULL); + + /* Visit beyond end of list */ + + v = visitor_input_test_init(data, "0"); + + visit_start_list(v, NULL, NULL, 0, &error_abort); + visit_type_uint64(v, NULL, &val, &err); + g_assert_cmpuint(val, ==, 0); + visit_type_uint64(v, NULL, &val, &err); + error_free_or_abort(&err); + + visit_check_list(v, &error_abort); + visit_end_list(v, NULL); +} + +static void test_visitor_in_bool(TestInputVisitorData *data, + const void *unused) +{ + bool res = false; + Visitor *v; + + v = visitor_input_test_init(data, "true"); + + visit_type_bool(v, NULL, &res, &error_abort); + g_assert_cmpint(res, ==, true); + + v = visitor_input_test_init(data, "yes"); + + visit_type_bool(v, NULL, &res, &error_abort); + g_assert_cmpint(res, ==, true); + + v = visitor_input_test_init(data, "on"); + + visit_type_bool(v, NULL, &res, &error_abort); + g_assert_cmpint(res, ==, true); + + v = visitor_input_test_init(data, "false"); + + visit_type_bool(v, NULL, &res, &error_abort); + g_assert_cmpint(res, ==, false); + + v = visitor_input_test_init(data, "no"); + + visit_type_bool(v, NULL, &res, &error_abort); + g_assert_cmpint(res, ==, false); + + v = visitor_input_test_init(data, "off"); + + visit_type_bool(v, NULL, &res, &error_abort); + g_assert_cmpint(res, ==, false); +} + +static void test_visitor_in_number(TestInputVisitorData *data, + const void *unused) +{ + double res = 0, value = 3.14; + Error *err = NULL; + Visitor *v; + + v = visitor_input_test_init(data, "3.14"); + + visit_type_number(v, NULL, &res, &error_abort); + g_assert_cmpfloat(res, ==, value); + + /* NaN and infinity has to be rejected */ + + v = visitor_input_test_init(data, "NaN"); + + visit_type_number(v, NULL, &res, &err); + error_free_or_abort(&err); + + v = visitor_input_test_init(data, "inf"); + + visit_type_number(v, NULL, &res, &err); + error_free_or_abort(&err); + +} + +static void test_visitor_in_string(TestInputVisitorData *data, + const void *unused) +{ + char *res = NULL, *value = (char *) "Q E M U"; + Visitor *v; + + v = visitor_input_test_init(data, value); + + visit_type_str(v, NULL, &res, &error_abort); + g_assert_cmpstr(res, ==, value); + + g_free(res); +} + +static void test_visitor_in_enum(TestInputVisitorData *data, + const void *unused) +{ + Visitor *v; + EnumOne i; + + for (i = 0; i < ENUM_ONE__MAX; i++) { + EnumOne res = -1; + + v = visitor_input_test_init(data, EnumOne_str(i)); + + visit_type_EnumOne(v, NULL, &res, &error_abort); + g_assert_cmpint(i, ==, res); + } +} + +/* Try to crash the visitors */ +static void test_visitor_in_fuzz(TestInputVisitorData *data, + const void *unused) +{ + int64_t ires; + intList *ilres; + bool bres; + double nres; + char *sres; + EnumOne eres; + Visitor *v; + unsigned int i; + char buf[10000]; + + for (i = 0; i < 100; i++) { + unsigned int j, k; + + j = g_test_rand_int_range(0, sizeof(buf) - 1); + + buf[j] = '\0'; + + for (k = 0; k != j; k++) { + buf[k] = (char)g_test_rand_int_range(0, 256); + } + + v = visitor_input_test_init(data, buf); + visit_type_int(v, NULL, &ires, NULL); + + v = visitor_input_test_init(data, buf); + visit_type_intList(v, NULL, &ilres, NULL); + qapi_free_intList(ilres); + + v = visitor_input_test_init(data, buf); + visit_type_bool(v, NULL, &bres, NULL); + + v = visitor_input_test_init(data, buf); + visit_type_number(v, NULL, &nres, NULL); + + v = visitor_input_test_init(data, buf); + sres = NULL; + visit_type_str(v, NULL, &sres, NULL); + g_free(sres); + + v = visitor_input_test_init(data, buf); + visit_type_EnumOne(v, NULL, &eres, NULL); + } +} + +static void input_visitor_test_add(const char *testpath, + TestInputVisitorData *data, + void (*test_func)(TestInputVisitorData *data, const void *user_data)) +{ + g_test_add(testpath, TestInputVisitorData, data, NULL, test_func, + visitor_input_teardown); +} + +int main(int argc, char **argv) +{ + TestInputVisitorData in_visitor_data; + + g_test_init(&argc, &argv, NULL); + + input_visitor_test_add("/string-visitor/input/int", + &in_visitor_data, test_visitor_in_int); + input_visitor_test_add("/string-visitor/input/intList", + &in_visitor_data, test_visitor_in_intList); + input_visitor_test_add("/string-visitor/input/uintList", + &in_visitor_data, test_visitor_in_uintList); + input_visitor_test_add("/string-visitor/input/bool", + &in_visitor_data, test_visitor_in_bool); + input_visitor_test_add("/string-visitor/input/number", + &in_visitor_data, test_visitor_in_number); + input_visitor_test_add("/string-visitor/input/string", + &in_visitor_data, test_visitor_in_string); + input_visitor_test_add("/string-visitor/input/enum", + &in_visitor_data, test_visitor_in_enum); + input_visitor_test_add("/string-visitor/input/fuzz", + &in_visitor_data, test_visitor_in_fuzz); + + g_test_run(); + + return 0; +} diff --git a/tests/unit/test-string-output-visitor.c b/tests/unit/test-string-output-visitor.c new file mode 100644 index 0000000000..e2bedc5c7c --- /dev/null +++ b/tests/unit/test-string-output-visitor.c @@ -0,0 +1,248 @@ +/* + * String Output Visitor unit-tests. + * + * Copyright (C) 2012 Red Hat Inc. + * + * Authors: + * Paolo Bonzini (based on test-qobject-output-visitor) + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" + +#include "qemu-common.h" +#include "qapi/error.h" +#include "qapi/string-output-visitor.h" +#include "test-qapi-visit.h" + +typedef struct TestOutputVisitorData { + Visitor *ov; + char *str; + bool human; +} TestOutputVisitorData; + +static void visitor_output_setup_internal(TestOutputVisitorData *data, + bool human) +{ + data->human = human; + data->ov = string_output_visitor_new(human, &data->str); + g_assert(data->ov); +} + +static void visitor_output_setup(TestOutputVisitorData *data, + const void *unused) +{ + return visitor_output_setup_internal(data, false); +} + +static void visitor_output_setup_human(TestOutputVisitorData *data, + const void *unused) +{ + return visitor_output_setup_internal(data, true); +} + +static void visitor_output_teardown(TestOutputVisitorData *data, + const void *unused) +{ + visit_free(data->ov); + data->ov = NULL; + g_free(data->str); + data->str = NULL; +} + +static char *visitor_get(TestOutputVisitorData *data) +{ + visit_complete(data->ov, &data->str); + g_assert(data->str); + return data->str; +} + +static void visitor_reset(TestOutputVisitorData *data) +{ + bool human = data->human; + + visitor_output_teardown(data, NULL); + visitor_output_setup_internal(data, human); +} + +static void test_visitor_out_int(TestOutputVisitorData *data, + const void *unused) +{ + int64_t value = 42; + char *str; + + visit_type_int(data->ov, NULL, &value, &error_abort); + + str = visitor_get(data); + if (data->human) { + g_assert_cmpstr(str, ==, "42 (0x2a)"); + } else { + g_assert_cmpstr(str, ==, "42"); + } +} + +static void test_visitor_out_intList(TestOutputVisitorData *data, + const void *unused) +{ + int64_t value[] = {0, 1, 9, 10, 16, 15, 14, + 3, 4, 5, 6, 11, 12, 13, 21, 22, INT64_MAX - 1, INT64_MAX}; + intList *list = NULL, **tail = &list; + int i; + Error *err = NULL; + char *str; + + for (i = 0; i < ARRAY_SIZE(value); i++) { + QAPI_LIST_APPEND(tail, value[i]); + } + + visit_type_intList(data->ov, NULL, &list, &err); + g_assert(err == NULL); + + str = visitor_get(data); + if (data->human) { + g_assert_cmpstr(str, ==, + "0-1,3-6,9-16,21-22,9223372036854775806-9223372036854775807 " + "(0x0-0x1,0x3-0x6,0x9-0x10,0x15-0x16," + "0x7ffffffffffffffe-0x7fffffffffffffff)"); + } else { + g_assert_cmpstr(str, ==, + "0-1,3-6,9-16,21-22,9223372036854775806-9223372036854775807"); + } + qapi_free_intList(list); +} + +static void test_visitor_out_bool(TestOutputVisitorData *data, + const void *unused) +{ + bool value = true; + char *str; + + visit_type_bool(data->ov, NULL, &value, &error_abort); + + str = visitor_get(data); + g_assert_cmpstr(str, ==, "true"); +} + +static void test_visitor_out_number(TestOutputVisitorData *data, + const void *unused) +{ + double value = 3.1415926535897932; + char *str; + + visit_type_number(data->ov, NULL, &value, &error_abort); + + str = visitor_get(data); + g_assert_cmpstr(str, ==, "3.1415926535897931"); +} + +static void test_visitor_out_string(TestOutputVisitorData *data, + const void *unused) +{ + char *string = (char *) "Q E M U"; + const char *string_human = "\"Q E M U\""; + char *str; + + visit_type_str(data->ov, NULL, &string, &error_abort); + + str = visitor_get(data); + if (data->human) { + g_assert_cmpstr(str, ==, string_human); + } else { + g_assert_cmpstr(str, ==, string); + } +} + +static void test_visitor_out_no_string(TestOutputVisitorData *data, + const void *unused) +{ + char *string = NULL; + char *str; + + /* A null string should return "" */ + visit_type_str(data->ov, NULL, &string, &error_abort); + + str = visitor_get(data); + if (data->human) { + g_assert_cmpstr(str, ==, ""); + } else { + g_assert_cmpstr(str, ==, ""); + } +} + +static void test_visitor_out_enum(TestOutputVisitorData *data, + const void *unused) +{ + char *str; + EnumOne i; + + for (i = 0; i < ENUM_ONE__MAX; i++) { + visit_type_EnumOne(data->ov, "unused", &i, &error_abort); + + str = visitor_get(data); + if (data->human) { + char *str_human = g_strdup_printf("\"%s\"", EnumOne_str(i)); + + g_assert_cmpstr(str, ==, str_human); + g_free(str_human); + } else { + g_assert_cmpstr(str, ==, EnumOne_str(i)); + } + visitor_reset(data); + } +} + +static void +output_visitor_test_add(const char *testpath, + TestOutputVisitorData *data, + void (*test_func)(TestOutputVisitorData *data, + const void *user_data), + bool human) +{ + g_test_add(testpath, TestOutputVisitorData, data, + human ? visitor_output_setup_human : visitor_output_setup, + test_func, visitor_output_teardown); +} + +int main(int argc, char **argv) +{ + TestOutputVisitorData out_visitor_data; + + g_test_init(&argc, &argv, NULL); + + output_visitor_test_add("/string-visitor/output/int", + &out_visitor_data, test_visitor_out_int, false); + output_visitor_test_add("/string-visitor/output/int-human", + &out_visitor_data, test_visitor_out_int, true); + output_visitor_test_add("/string-visitor/output/bool", + &out_visitor_data, test_visitor_out_bool, false); + output_visitor_test_add("/string-visitor/output/bool-human", + &out_visitor_data, test_visitor_out_bool, true); + output_visitor_test_add("/string-visitor/output/number", + &out_visitor_data, test_visitor_out_number, false); + output_visitor_test_add("/string-visitor/output/number-human", + &out_visitor_data, test_visitor_out_number, true); + output_visitor_test_add("/string-visitor/output/string", + &out_visitor_data, test_visitor_out_string, false); + output_visitor_test_add("/string-visitor/output/string-human", + &out_visitor_data, test_visitor_out_string, true); + output_visitor_test_add("/string-visitor/output/no-string", + &out_visitor_data, test_visitor_out_no_string, + false); + output_visitor_test_add("/string-visitor/output/no-string-human", + &out_visitor_data, test_visitor_out_no_string, + true); + output_visitor_test_add("/string-visitor/output/enum", + &out_visitor_data, test_visitor_out_enum, false); + output_visitor_test_add("/string-visitor/output/enum-human", + &out_visitor_data, test_visitor_out_enum, true); + output_visitor_test_add("/string-visitor/output/intList", + &out_visitor_data, test_visitor_out_intList, false); + output_visitor_test_add("/string-visitor/output/intList-human", + &out_visitor_data, test_visitor_out_intList, true); + + g_test_run(); + + return 0; +} diff --git a/tests/unit/test-thread-pool.c b/tests/unit/test-thread-pool.c new file mode 100644 index 0000000000..70dc6314a1 --- /dev/null +++ b/tests/unit/test-thread-pool.c @@ -0,0 +1,250 @@ +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "block/aio.h" +#include "block/thread-pool.h" +#include "block/block.h" +#include "qapi/error.h" +#include "qemu/timer.h" +#include "qemu/error-report.h" +#include "qemu/main-loop.h" + +static AioContext *ctx; +static ThreadPool *pool; +static int active; + +typedef struct { + BlockAIOCB *aiocb; + int n; + int ret; +} WorkerTestData; + +static int worker_cb(void *opaque) +{ + WorkerTestData *data = opaque; + return qatomic_fetch_inc(&data->n); +} + +static int long_cb(void *opaque) +{ + WorkerTestData *data = opaque; + if (qatomic_cmpxchg(&data->n, 0, 1) == 0) { + g_usleep(2000000); + qatomic_or(&data->n, 2); + } + return 0; +} + +static void done_cb(void *opaque, int ret) +{ + WorkerTestData *data = opaque; + g_assert(data->ret == -EINPROGRESS || data->ret == -ECANCELED); + data->ret = ret; + data->aiocb = NULL; + + /* Callbacks are serialized, so no need to use atomic ops. */ + active--; +} + +static void test_submit(void) +{ + WorkerTestData data = { .n = 0 }; + thread_pool_submit(pool, worker_cb, &data); + while (data.n == 0) { + aio_poll(ctx, true); + } + g_assert_cmpint(data.n, ==, 1); +} + +static void test_submit_aio(void) +{ + WorkerTestData data = { .n = 0, .ret = -EINPROGRESS }; + data.aiocb = thread_pool_submit_aio(pool, worker_cb, &data, + done_cb, &data); + + /* The callbacks are not called until after the first wait. */ + active = 1; + g_assert_cmpint(data.ret, ==, -EINPROGRESS); + while (data.ret == -EINPROGRESS) { + aio_poll(ctx, true); + } + g_assert_cmpint(active, ==, 0); + g_assert_cmpint(data.n, ==, 1); + g_assert_cmpint(data.ret, ==, 0); +} + +static void co_test_cb(void *opaque) +{ + WorkerTestData *data = opaque; + + active = 1; + data->n = 0; + data->ret = -EINPROGRESS; + thread_pool_submit_co(pool, worker_cb, data); + + /* The test continues in test_submit_co, after qemu_coroutine_enter... */ + + g_assert_cmpint(data->n, ==, 1); + data->ret = 0; + active--; + + /* The test continues in test_submit_co, after aio_poll... */ +} + +static void test_submit_co(void) +{ + WorkerTestData data; + Coroutine *co = qemu_coroutine_create(co_test_cb, &data); + + qemu_coroutine_enter(co); + + /* Back here once the worker has started. */ + + g_assert_cmpint(active, ==, 1); + g_assert_cmpint(data.ret, ==, -EINPROGRESS); + + /* aio_poll will execute the rest of the coroutine. */ + + while (data.ret == -EINPROGRESS) { + aio_poll(ctx, true); + } + + /* Back here after the coroutine has finished. */ + + g_assert_cmpint(active, ==, 0); + g_assert_cmpint(data.ret, ==, 0); +} + +static void test_submit_many(void) +{ + WorkerTestData data[100]; + int i; + + /* Start more work items than there will be threads. */ + for (i = 0; i < 100; i++) { + data[i].n = 0; + data[i].ret = -EINPROGRESS; + thread_pool_submit_aio(pool, worker_cb, &data[i], done_cb, &data[i]); + } + + active = 100; + while (active > 0) { + aio_poll(ctx, true); + } + for (i = 0; i < 100; i++) { + g_assert_cmpint(data[i].n, ==, 1); + g_assert_cmpint(data[i].ret, ==, 0); + } +} + +static void do_test_cancel(bool sync) +{ + WorkerTestData data[100]; + int num_canceled; + int i; + + /* Start more work items than there will be threads, to ensure + * the pool is full. + */ + test_submit_many(); + + /* Start long running jobs, to ensure we can cancel some. */ + for (i = 0; i < 100; i++) { + data[i].n = 0; + data[i].ret = -EINPROGRESS; + data[i].aiocb = thread_pool_submit_aio(pool, long_cb, &data[i], + done_cb, &data[i]); + } + + /* Starting the threads may be left to a bottom half. Let it + * run, but do not waste too much time... + */ + active = 100; + aio_notify(ctx); + aio_poll(ctx, false); + + /* Wait some time for the threads to start, with some sanity + * testing on the behavior of the scheduler... + */ + g_assert_cmpint(active, ==, 100); + g_usleep(1000000); + g_assert_cmpint(active, >, 50); + + /* Cancel the jobs that haven't been started yet. */ + num_canceled = 0; + for (i = 0; i < 100; i++) { + if (qatomic_cmpxchg(&data[i].n, 0, 4) == 0) { + data[i].ret = -ECANCELED; + if (sync) { + bdrv_aio_cancel(data[i].aiocb); + } else { + bdrv_aio_cancel_async(data[i].aiocb); + } + num_canceled++; + } + } + g_assert_cmpint(active, >, 0); + g_assert_cmpint(num_canceled, <, 100); + + for (i = 0; i < 100; i++) { + if (data[i].aiocb && qatomic_read(&data[i].n) < 4) { + if (sync) { + /* Canceling the others will be a blocking operation. */ + bdrv_aio_cancel(data[i].aiocb); + } else { + bdrv_aio_cancel_async(data[i].aiocb); + } + } + } + + /* Finish execution and execute any remaining callbacks. */ + while (active > 0) { + aio_poll(ctx, true); + } + g_assert_cmpint(active, ==, 0); + for (i = 0; i < 100; i++) { + g_assert(data[i].aiocb == NULL); + switch (data[i].n) { + case 0: + fprintf(stderr, "Callback not canceled but never started?\n"); + abort(); + case 3: + /* Couldn't be canceled asynchronously, must have completed. */ + g_assert_cmpint(data[i].ret, ==, 0); + break; + case 4: + /* Could be canceled asynchronously, never started. */ + g_assert_cmpint(data[i].ret, ==, -ECANCELED); + break; + default: + fprintf(stderr, "Callback aborted while running?\n"); + abort(); + } + } +} + +static void test_cancel(void) +{ + do_test_cancel(true); +} + +static void test_cancel_async(void) +{ + do_test_cancel(false); +} + +int main(int argc, char **argv) +{ + qemu_init_main_loop(&error_abort); + ctx = qemu_get_current_aio_context(); + pool = aio_get_thread_pool(ctx); + + g_test_init(&argc, &argv, NULL); + g_test_add_func("/thread-pool/submit", test_submit); + g_test_add_func("/thread-pool/submit-aio", test_submit_aio); + g_test_add_func("/thread-pool/submit-co", test_submit_co); + g_test_add_func("/thread-pool/submit-many", test_submit_many); + g_test_add_func("/thread-pool/cancel", test_cancel); + g_test_add_func("/thread-pool/cancel-async", test_cancel_async); + + return g_test_run(); +} diff --git a/tests/unit/test-throttle.c b/tests/unit/test-throttle.c new file mode 100644 index 0000000000..7adb5e6652 --- /dev/null +++ b/tests/unit/test-throttle.c @@ -0,0 +1,770 @@ +/* + * Throttle infrastructure tests + * + * Copyright Nodalink, EURL. 2013-2014 + * Copyright Igalia, S.L. 2015 + * + * Authors: + * Benoît Canet + * Alberto Garcia + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include +#include "block/aio.h" +#include "qapi/error.h" +#include "qemu/throttle.h" +#include "qemu/error-report.h" +#include "qemu/main-loop.h" +#include "qemu/module.h" +#include "block/throttle-groups.h" +#include "sysemu/block-backend.h" + +static AioContext *ctx; +static LeakyBucket bkt; +static ThrottleConfig cfg; +static ThrottleGroupMember tgm; +static ThrottleState ts; +static ThrottleTimers *tt; + +/* useful function */ +static bool double_cmp(double x, double y) +{ + return fabsl(x - y) < 1e-6; +} + +/* tests for single bucket operations */ +static void test_leak_bucket(void) +{ + throttle_config_init(&cfg); + bkt = cfg.buckets[THROTTLE_BPS_TOTAL]; + + /* set initial value */ + bkt.avg = 150; + bkt.max = 15; + bkt.level = 1.5; + + /* leak an op work of time */ + throttle_leak_bucket(&bkt, NANOSECONDS_PER_SECOND / 150); + g_assert(bkt.avg == 150); + g_assert(bkt.max == 15); + g_assert(double_cmp(bkt.level, 0.5)); + + /* leak again emptying the bucket */ + throttle_leak_bucket(&bkt, NANOSECONDS_PER_SECOND / 150); + g_assert(bkt.avg == 150); + g_assert(bkt.max == 15); + g_assert(double_cmp(bkt.level, 0)); + + /* check that the bucket level won't go lower */ + throttle_leak_bucket(&bkt, NANOSECONDS_PER_SECOND / 150); + g_assert(bkt.avg == 150); + g_assert(bkt.max == 15); + g_assert(double_cmp(bkt.level, 0)); + + /* check that burst_level leaks correctly */ + bkt.burst_level = 6; + bkt.max = 250; + bkt.burst_length = 2; /* otherwise burst_level will not leak */ + throttle_leak_bucket(&bkt, NANOSECONDS_PER_SECOND / 100); + g_assert(double_cmp(bkt.burst_level, 3.5)); + + throttle_leak_bucket(&bkt, NANOSECONDS_PER_SECOND / 100); + g_assert(double_cmp(bkt.burst_level, 1)); + + throttle_leak_bucket(&bkt, NANOSECONDS_PER_SECOND / 100); + g_assert(double_cmp(bkt.burst_level, 0)); + + throttle_leak_bucket(&bkt, NANOSECONDS_PER_SECOND / 100); + g_assert(double_cmp(bkt.burst_level, 0)); +} + +static void test_compute_wait(void) +{ + unsigned i; + int64_t wait; + int64_t result; + + throttle_config_init(&cfg); + bkt = cfg.buckets[THROTTLE_BPS_TOTAL]; + + /* no operation limit set */ + bkt.avg = 0; + bkt.max = 15; + bkt.level = 1.5; + wait = throttle_compute_wait(&bkt); + g_assert(!wait); + + /* zero delta */ + bkt.avg = 150; + bkt.max = 15; + bkt.level = 15; + wait = throttle_compute_wait(&bkt); + g_assert(!wait); + + /* below zero delta */ + bkt.avg = 150; + bkt.max = 15; + bkt.level = 9; + wait = throttle_compute_wait(&bkt); + g_assert(!wait); + + /* half an operation above max */ + bkt.avg = 150; + bkt.max = 15; + bkt.level = 15.5; + wait = throttle_compute_wait(&bkt); + /* time required to do half an operation */ + result = (int64_t) NANOSECONDS_PER_SECOND / 150 / 2; + g_assert(wait == result); + + /* Perform I/O for 2.2 seconds at a rate of bkt.max */ + bkt.burst_length = 2; + bkt.level = 0; + bkt.avg = 10; + bkt.max = 200; + for (i = 0; i < 22; i++) { + double units = bkt.max / 10; + bkt.level += units; + bkt.burst_level += units; + throttle_leak_bucket(&bkt, NANOSECONDS_PER_SECOND / 10); + wait = throttle_compute_wait(&bkt); + g_assert(double_cmp(bkt.burst_level, 0)); + g_assert(double_cmp(bkt.level, (i + 1) * (bkt.max - bkt.avg) / 10)); + /* We can do bursts for the 2 seconds we have configured in + * burst_length. We have 100 extra miliseconds of burst + * because bkt.level has been leaking during this time. + * After that, we have to wait. */ + result = i < 21 ? 0 : 1.8 * NANOSECONDS_PER_SECOND; + g_assert(wait == result); + } +} + +/* functions to test ThrottleState initialization/destroy methods */ +static void read_timer_cb(void *opaque) +{ +} + +static void write_timer_cb(void *opaque) +{ +} + +static void test_init(void) +{ + int i; + + tt = &tgm.throttle_timers; + + /* fill the structures with crap */ + memset(&ts, 1, sizeof(ts)); + memset(tt, 1, sizeof(*tt)); + + /* init structures */ + throttle_init(&ts); + throttle_timers_init(tt, ctx, QEMU_CLOCK_VIRTUAL, + read_timer_cb, write_timer_cb, &ts); + + /* check initialized fields */ + g_assert(tt->clock_type == QEMU_CLOCK_VIRTUAL); + g_assert(tt->timers[0]); + g_assert(tt->timers[1]); + + /* check other fields where cleared */ + g_assert(!ts.previous_leak); + g_assert(!ts.cfg.op_size); + for (i = 0; i < BUCKETS_COUNT; i++) { + g_assert(!ts.cfg.buckets[i].avg); + g_assert(!ts.cfg.buckets[i].max); + g_assert(!ts.cfg.buckets[i].level); + } + + throttle_timers_destroy(tt); +} + +static void test_destroy(void) +{ + int i; + throttle_init(&ts); + throttle_timers_init(tt, ctx, QEMU_CLOCK_VIRTUAL, + read_timer_cb, write_timer_cb, &ts); + throttle_timers_destroy(tt); + for (i = 0; i < 2; i++) { + g_assert(!tt->timers[i]); + } +} + +/* function to test throttle_config and throttle_get_config */ +static void test_config_functions(void) +{ + int i; + ThrottleConfig orig_cfg, final_cfg; + + orig_cfg.buckets[THROTTLE_BPS_TOTAL].avg = 153; + orig_cfg.buckets[THROTTLE_BPS_READ].avg = 56; + orig_cfg.buckets[THROTTLE_BPS_WRITE].avg = 1; + + orig_cfg.buckets[THROTTLE_OPS_TOTAL].avg = 150; + orig_cfg.buckets[THROTTLE_OPS_READ].avg = 69; + orig_cfg.buckets[THROTTLE_OPS_WRITE].avg = 23; + + orig_cfg.buckets[THROTTLE_BPS_TOTAL].max = 0; + orig_cfg.buckets[THROTTLE_BPS_READ].max = 56; + orig_cfg.buckets[THROTTLE_BPS_WRITE].max = 120; + + orig_cfg.buckets[THROTTLE_OPS_TOTAL].max = 150; + orig_cfg.buckets[THROTTLE_OPS_READ].max = 400; + orig_cfg.buckets[THROTTLE_OPS_WRITE].max = 500; + + orig_cfg.buckets[THROTTLE_BPS_TOTAL].level = 45; + orig_cfg.buckets[THROTTLE_BPS_READ].level = 65; + orig_cfg.buckets[THROTTLE_BPS_WRITE].level = 23; + + orig_cfg.buckets[THROTTLE_OPS_TOTAL].level = 1; + orig_cfg.buckets[THROTTLE_OPS_READ].level = 90; + orig_cfg.buckets[THROTTLE_OPS_WRITE].level = 75; + + orig_cfg.op_size = 1; + + throttle_init(&ts); + throttle_timers_init(tt, ctx, QEMU_CLOCK_VIRTUAL, + read_timer_cb, write_timer_cb, &ts); + /* structure reset by throttle_init previous_leak should be null */ + g_assert(!ts.previous_leak); + throttle_config(&ts, QEMU_CLOCK_VIRTUAL, &orig_cfg); + + /* has previous leak been initialized by throttle_config ? */ + g_assert(ts.previous_leak); + + /* get back the fixed configuration */ + throttle_get_config(&ts, &final_cfg); + + throttle_timers_destroy(tt); + + g_assert(final_cfg.buckets[THROTTLE_BPS_TOTAL].avg == 153); + g_assert(final_cfg.buckets[THROTTLE_BPS_READ].avg == 56); + g_assert(final_cfg.buckets[THROTTLE_BPS_WRITE].avg == 1); + + g_assert(final_cfg.buckets[THROTTLE_OPS_TOTAL].avg == 150); + g_assert(final_cfg.buckets[THROTTLE_OPS_READ].avg == 69); + g_assert(final_cfg.buckets[THROTTLE_OPS_WRITE].avg == 23); + + g_assert(final_cfg.buckets[THROTTLE_BPS_TOTAL].max == 0); + g_assert(final_cfg.buckets[THROTTLE_BPS_READ].max == 56); + g_assert(final_cfg.buckets[THROTTLE_BPS_WRITE].max == 120); + + g_assert(final_cfg.buckets[THROTTLE_OPS_TOTAL].max == 150); + g_assert(final_cfg.buckets[THROTTLE_OPS_READ].max == 400); + g_assert(final_cfg.buckets[THROTTLE_OPS_WRITE].max == 500); + + g_assert(final_cfg.op_size == 1); + + /* check bucket have been cleared */ + for (i = 0; i < BUCKETS_COUNT; i++) { + g_assert(!final_cfg.buckets[i].level); + } +} + +/* functions to test is throttle is enabled by a config */ +static void set_cfg_value(bool is_max, int index, int value) +{ + if (is_max) { + cfg.buckets[index].max = value; + /* If max is set, avg should never be 0 */ + cfg.buckets[index].avg = MAX(cfg.buckets[index].avg, 1); + } else { + cfg.buckets[index].avg = value; + } +} + +static void test_enabled(void) +{ + int i; + + throttle_config_init(&cfg); + g_assert(!throttle_enabled(&cfg)); + + for (i = 0; i < BUCKETS_COUNT; i++) { + throttle_config_init(&cfg); + set_cfg_value(false, i, 150); + g_assert(throttle_is_valid(&cfg, NULL)); + g_assert(throttle_enabled(&cfg)); + } + + for (i = 0; i < BUCKETS_COUNT; i++) { + throttle_config_init(&cfg); + set_cfg_value(false, i, -150); + g_assert(!throttle_is_valid(&cfg, NULL)); + } +} + +/* tests functions for throttle_conflicting */ + +static void test_conflicts_for_one_set(bool is_max, + int total, + int read, + int write) +{ + throttle_config_init(&cfg); + g_assert(throttle_is_valid(&cfg, NULL)); + + set_cfg_value(is_max, total, 1); + set_cfg_value(is_max, read, 1); + g_assert(!throttle_is_valid(&cfg, NULL)); + + throttle_config_init(&cfg); + set_cfg_value(is_max, total, 1); + set_cfg_value(is_max, write, 1); + g_assert(!throttle_is_valid(&cfg, NULL)); + + throttle_config_init(&cfg); + set_cfg_value(is_max, total, 1); + set_cfg_value(is_max, read, 1); + set_cfg_value(is_max, write, 1); + g_assert(!throttle_is_valid(&cfg, NULL)); + + throttle_config_init(&cfg); + set_cfg_value(is_max, total, 1); + g_assert(throttle_is_valid(&cfg, NULL)); + + throttle_config_init(&cfg); + set_cfg_value(is_max, read, 1); + set_cfg_value(is_max, write, 1); + g_assert(throttle_is_valid(&cfg, NULL)); +} + +static void test_conflicting_config(void) +{ + /* bps average conflicts */ + test_conflicts_for_one_set(false, + THROTTLE_BPS_TOTAL, + THROTTLE_BPS_READ, + THROTTLE_BPS_WRITE); + + /* ops average conflicts */ + test_conflicts_for_one_set(false, + THROTTLE_OPS_TOTAL, + THROTTLE_OPS_READ, + THROTTLE_OPS_WRITE); + + /* bps average conflicts */ + test_conflicts_for_one_set(true, + THROTTLE_BPS_TOTAL, + THROTTLE_BPS_READ, + THROTTLE_BPS_WRITE); + /* ops average conflicts */ + test_conflicts_for_one_set(true, + THROTTLE_OPS_TOTAL, + THROTTLE_OPS_READ, + THROTTLE_OPS_WRITE); +} +/* functions to test the throttle_is_valid function */ +static void test_is_valid_for_value(int value, bool should_be_valid) +{ + int is_max, index; + for (is_max = 0; is_max < 2; is_max++) { + for (index = 0; index < BUCKETS_COUNT; index++) { + throttle_config_init(&cfg); + set_cfg_value(is_max, index, value); + g_assert(throttle_is_valid(&cfg, NULL) == should_be_valid); + } + } +} + +static void test_is_valid(void) +{ + /* negative number are invalid */ + test_is_valid_for_value(-1, false); + /* zero are valids */ + test_is_valid_for_value(0, true); + /* positives numers are valids */ + test_is_valid_for_value(1, true); +} + +static void test_ranges(void) +{ + int i; + + for (i = 0; i < BUCKETS_COUNT; i++) { + LeakyBucket *b = &cfg.buckets[i]; + throttle_config_init(&cfg); + + /* avg = 0 means throttling is disabled, but the config is valid */ + b->avg = 0; + g_assert(throttle_is_valid(&cfg, NULL)); + g_assert(!throttle_enabled(&cfg)); + + /* These are valid configurations (values <= THROTTLE_VALUE_MAX) */ + b->avg = 1; + g_assert(throttle_is_valid(&cfg, NULL)); + + b->avg = THROTTLE_VALUE_MAX; + g_assert(throttle_is_valid(&cfg, NULL)); + + b->avg = THROTTLE_VALUE_MAX; + b->max = THROTTLE_VALUE_MAX; + g_assert(throttle_is_valid(&cfg, NULL)); + + /* Values over THROTTLE_VALUE_MAX are not allowed */ + b->avg = THROTTLE_VALUE_MAX + 1; + g_assert(!throttle_is_valid(&cfg, NULL)); + + b->avg = THROTTLE_VALUE_MAX; + b->max = THROTTLE_VALUE_MAX + 1; + g_assert(!throttle_is_valid(&cfg, NULL)); + + /* burst_length must be between 1 and THROTTLE_VALUE_MAX */ + b->avg = 1; + b->max = 1; + b->burst_length = 0; + g_assert(!throttle_is_valid(&cfg, NULL)); + + b->avg = 1; + b->max = 1; + b->burst_length = 1; + g_assert(throttle_is_valid(&cfg, NULL)); + + b->avg = 1; + b->max = 1; + b->burst_length = THROTTLE_VALUE_MAX; + g_assert(throttle_is_valid(&cfg, NULL)); + + b->avg = 1; + b->max = 1; + b->burst_length = THROTTLE_VALUE_MAX + 1; + g_assert(!throttle_is_valid(&cfg, NULL)); + + /* burst_length * max cannot exceed THROTTLE_VALUE_MAX */ + b->avg = 1; + b->max = 2; + b->burst_length = THROTTLE_VALUE_MAX / 2; + g_assert(throttle_is_valid(&cfg, NULL)); + + b->avg = 1; + b->max = 3; + b->burst_length = THROTTLE_VALUE_MAX / 2; + g_assert(!throttle_is_valid(&cfg, NULL)); + + b->avg = 1; + b->max = THROTTLE_VALUE_MAX; + b->burst_length = 1; + g_assert(throttle_is_valid(&cfg, NULL)); + + b->avg = 1; + b->max = THROTTLE_VALUE_MAX; + b->burst_length = 2; + g_assert(!throttle_is_valid(&cfg, NULL)); + } +} + +static void test_max_is_missing_limit(void) +{ + int i; + + for (i = 0; i < BUCKETS_COUNT; i++) { + throttle_config_init(&cfg); + cfg.buckets[i].max = 100; + cfg.buckets[i].avg = 0; + g_assert(!throttle_is_valid(&cfg, NULL)); + + cfg.buckets[i].max = 0; + cfg.buckets[i].avg = 0; + g_assert(throttle_is_valid(&cfg, NULL)); + + cfg.buckets[i].max = 0; + cfg.buckets[i].avg = 100; + g_assert(throttle_is_valid(&cfg, NULL)); + + cfg.buckets[i].max = 30; + cfg.buckets[i].avg = 100; + g_assert(!throttle_is_valid(&cfg, NULL)); + + cfg.buckets[i].max = 100; + cfg.buckets[i].avg = 100; + g_assert(throttle_is_valid(&cfg, NULL)); + } +} + +static void test_iops_size_is_missing_limit(void) +{ + /* A total/read/write iops limit is required */ + throttle_config_init(&cfg); + cfg.op_size = 4096; + g_assert(!throttle_is_valid(&cfg, NULL)); +} + +static void test_have_timer(void) +{ + /* zero structures */ + memset(&ts, 0, sizeof(ts)); + memset(tt, 0, sizeof(*tt)); + + /* no timer set should return false */ + g_assert(!throttle_timers_are_initialized(tt)); + + /* init structures */ + throttle_init(&ts); + throttle_timers_init(tt, ctx, QEMU_CLOCK_VIRTUAL, + read_timer_cb, write_timer_cb, &ts); + + /* timer set by init should return true */ + g_assert(throttle_timers_are_initialized(tt)); + + throttle_timers_destroy(tt); +} + +static void test_detach_attach(void) +{ + /* zero structures */ + memset(&ts, 0, sizeof(ts)); + memset(tt, 0, sizeof(*tt)); + + /* init the structure */ + throttle_init(&ts); + throttle_timers_init(tt, ctx, QEMU_CLOCK_VIRTUAL, + read_timer_cb, write_timer_cb, &ts); + + /* timer set by init should return true */ + g_assert(throttle_timers_are_initialized(tt)); + + /* timer should no longer exist after detaching */ + throttle_timers_detach_aio_context(tt); + g_assert(!throttle_timers_are_initialized(tt)); + + /* timer should exist again after attaching */ + throttle_timers_attach_aio_context(tt, ctx); + g_assert(throttle_timers_are_initialized(tt)); + + throttle_timers_destroy(tt); +} + +static bool do_test_accounting(bool is_ops, /* are we testing bps or ops */ + int size, /* size of the operation to do */ + double avg, /* io limit */ + uint64_t op_size, /* ideal size of an io */ + double total_result, + double read_result, + double write_result) +{ + BucketType to_test[2][3] = { { THROTTLE_BPS_TOTAL, + THROTTLE_BPS_READ, + THROTTLE_BPS_WRITE, }, + { THROTTLE_OPS_TOTAL, + THROTTLE_OPS_READ, + THROTTLE_OPS_WRITE, } }; + ThrottleConfig cfg; + BucketType index; + int i; + + throttle_config_init(&cfg); + + for (i = 0; i < 3; i++) { + BucketType index = to_test[is_ops][i]; + cfg.buckets[index].avg = avg; + } + + cfg.op_size = op_size; + + throttle_init(&ts); + throttle_timers_init(tt, ctx, QEMU_CLOCK_VIRTUAL, + read_timer_cb, write_timer_cb, &ts); + throttle_config(&ts, QEMU_CLOCK_VIRTUAL, &cfg); + + /* account a read */ + throttle_account(&ts, false, size); + /* account a write */ + throttle_account(&ts, true, size); + + /* check total result */ + index = to_test[is_ops][0]; + if (!double_cmp(ts.cfg.buckets[index].level, total_result)) { + return false; + } + + /* check read result */ + index = to_test[is_ops][1]; + if (!double_cmp(ts.cfg.buckets[index].level, read_result)) { + return false; + } + + /* check write result */ + index = to_test[is_ops][2]; + if (!double_cmp(ts.cfg.buckets[index].level, write_result)) { + return false; + } + + throttle_timers_destroy(tt); + + return true; +} + +static void test_accounting(void) +{ + /* tests for bps */ + + /* op of size 1 */ + g_assert(do_test_accounting(false, + 1 * 512, + 150, + 0, + 1024, + 512, + 512)); + + /* op of size 2 */ + g_assert(do_test_accounting(false, + 2 * 512, + 150, + 0, + 2048, + 1024, + 1024)); + + /* op of size 2 and orthogonal parameter change */ + g_assert(do_test_accounting(false, + 2 * 512, + 150, + 17, + 2048, + 1024, + 1024)); + + + /* tests for ops */ + + /* op of size 1 */ + g_assert(do_test_accounting(true, + 1 * 512, + 150, + 0, + 2, + 1, + 1)); + + /* op of size 2 */ + g_assert(do_test_accounting(true, + 2 * 512, + 150, + 0, + 2, + 1, + 1)); + + /* jumbo op accounting fragmentation : size 64 with op size of 13 units */ + g_assert(do_test_accounting(true, + 64 * 512, + 150, + 13 * 512, + (64.0 * 2) / 13, + (64.0 / 13), + (64.0 / 13))); + + /* same with orthogonal parameters changes */ + g_assert(do_test_accounting(true, + 64 * 512, + 300, + 13 * 512, + (64.0 * 2) / 13, + (64.0 / 13), + (64.0 / 13))); +} + +static void test_groups(void) +{ + ThrottleConfig cfg1, cfg2; + BlockBackend *blk1, *blk2, *blk3; + BlockBackendPublic *blkp1, *blkp2, *blkp3; + ThrottleGroupMember *tgm1, *tgm2, *tgm3; + + /* No actual I/O is performed on these devices */ + blk1 = blk_new(qemu_get_aio_context(), 0, BLK_PERM_ALL); + blk2 = blk_new(qemu_get_aio_context(), 0, BLK_PERM_ALL); + blk3 = blk_new(qemu_get_aio_context(), 0, BLK_PERM_ALL); + + blkp1 = blk_get_public(blk1); + blkp2 = blk_get_public(blk2); + blkp3 = blk_get_public(blk3); + + tgm1 = &blkp1->throttle_group_member; + tgm2 = &blkp2->throttle_group_member; + tgm3 = &blkp3->throttle_group_member; + + g_assert(tgm1->throttle_state == NULL); + g_assert(tgm2->throttle_state == NULL); + g_assert(tgm3->throttle_state == NULL); + + throttle_group_register_tgm(tgm1, "bar", blk_get_aio_context(blk1)); + throttle_group_register_tgm(tgm2, "foo", blk_get_aio_context(blk2)); + throttle_group_register_tgm(tgm3, "bar", blk_get_aio_context(blk3)); + + g_assert(tgm1->throttle_state != NULL); + g_assert(tgm2->throttle_state != NULL); + g_assert(tgm3->throttle_state != NULL); + + g_assert(!strcmp(throttle_group_get_name(tgm1), "bar")); + g_assert(!strcmp(throttle_group_get_name(tgm2), "foo")); + g_assert(tgm1->throttle_state == tgm3->throttle_state); + + /* Setting the config of a group member affects the whole group */ + throttle_config_init(&cfg1); + cfg1.buckets[THROTTLE_BPS_READ].avg = 500000; + cfg1.buckets[THROTTLE_BPS_WRITE].avg = 285000; + cfg1.buckets[THROTTLE_OPS_READ].avg = 20000; + cfg1.buckets[THROTTLE_OPS_WRITE].avg = 12000; + throttle_group_config(tgm1, &cfg1); + + throttle_group_get_config(tgm1, &cfg1); + throttle_group_get_config(tgm3, &cfg2); + g_assert(!memcmp(&cfg1, &cfg2, sizeof(cfg1))); + + cfg2.buckets[THROTTLE_BPS_READ].avg = 4547; + cfg2.buckets[THROTTLE_BPS_WRITE].avg = 1349; + cfg2.buckets[THROTTLE_OPS_READ].avg = 123; + cfg2.buckets[THROTTLE_OPS_WRITE].avg = 86; + throttle_group_config(tgm3, &cfg1); + + throttle_group_get_config(tgm1, &cfg1); + throttle_group_get_config(tgm3, &cfg2); + g_assert(!memcmp(&cfg1, &cfg2, sizeof(cfg1))); + + throttle_group_unregister_tgm(tgm1); + throttle_group_unregister_tgm(tgm2); + throttle_group_unregister_tgm(tgm3); + + g_assert(tgm1->throttle_state == NULL); + g_assert(tgm2->throttle_state == NULL); + g_assert(tgm3->throttle_state == NULL); +} + +int main(int argc, char **argv) +{ + qemu_init_main_loop(&error_fatal); + ctx = qemu_get_aio_context(); + bdrv_init(); + module_call_init(MODULE_INIT_QOM); + + do {} while (g_main_context_iteration(NULL, false)); + + /* tests in the same order as the header function declarations */ + g_test_init(&argc, &argv, NULL); + g_test_add_func("/throttle/leak_bucket", test_leak_bucket); + g_test_add_func("/throttle/compute_wait", test_compute_wait); + g_test_add_func("/throttle/init", test_init); + g_test_add_func("/throttle/destroy", test_destroy); + g_test_add_func("/throttle/have_timer", test_have_timer); + g_test_add_func("/throttle/detach_attach", test_detach_attach); + g_test_add_func("/throttle/config/enabled", test_enabled); + g_test_add_func("/throttle/config/conflicting", test_conflicting_config); + g_test_add_func("/throttle/config/is_valid", test_is_valid); + g_test_add_func("/throttle/config/ranges", test_ranges); + g_test_add_func("/throttle/config/max", test_max_is_missing_limit); + g_test_add_func("/throttle/config/iops_size", + test_iops_size_is_missing_limit); + g_test_add_func("/throttle/config_functions", test_config_functions); + g_test_add_func("/throttle/accounting", test_accounting); + g_test_add_func("/throttle/groups", test_groups); + return g_test_run(); +} + diff --git a/tests/unit/test-timed-average.c b/tests/unit/test-timed-average.c new file mode 100644 index 0000000000..82c92500df --- /dev/null +++ b/tests/unit/test-timed-average.c @@ -0,0 +1,89 @@ +/* + * Timed average computation tests + * + * Copyright Nodalink, EURL. 2014 + * + * Authors: + * Benoît Canet + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "sysemu/cpu-timers.h" +#include "qemu/timed-average.h" + +/* This is the clock for QEMU_CLOCK_VIRTUAL */ +static int64_t my_clock_value; + +int64_t cpu_get_clock(void) +{ + return my_clock_value; +} + +static void account(TimedAverage *ta) +{ + timed_average_account(ta, 1); + timed_average_account(ta, 5); + timed_average_account(ta, 2); + timed_average_account(ta, 4); + timed_average_account(ta, 3); +} + +static void test_average(void) +{ + TimedAverage ta; + uint64_t result; + int i; + + /* we will compute some average on a period of 1 second */ + timed_average_init(&ta, QEMU_CLOCK_VIRTUAL, NANOSECONDS_PER_SECOND); + + result = timed_average_min(&ta); + g_assert(result == 0); + result = timed_average_avg(&ta); + g_assert(result == 0); + result = timed_average_max(&ta); + g_assert(result == 0); + + for (i = 0; i < 100; i++) { + account(&ta); + result = timed_average_min(&ta); + g_assert(result == 1); + result = timed_average_avg(&ta); + g_assert(result == 3); + result = timed_average_max(&ta); + g_assert(result == 5); + my_clock_value += NANOSECONDS_PER_SECOND / 10; + } + + my_clock_value += NANOSECONDS_PER_SECOND * 100; + + result = timed_average_min(&ta); + g_assert(result == 0); + result = timed_average_avg(&ta); + g_assert(result == 0); + result = timed_average_max(&ta); + g_assert(result == 0); + + for (i = 0; i < 100; i++) { + account(&ta); + result = timed_average_min(&ta); + g_assert(result == 1); + result = timed_average_avg(&ta); + g_assert(result == 3); + result = timed_average_max(&ta); + g_assert(result == 5); + my_clock_value += NANOSECONDS_PER_SECOND / 10; + } +} + +int main(int argc, char **argv) +{ + /* tests in the same order as the header function declarations */ + g_test_init(&argc, &argv, NULL); + g_test_add_func("/timed-average/average", test_average); + return g_test_run(); +} + diff --git a/tests/unit/test-util-filemonitor.c b/tests/unit/test-util-filemonitor.c new file mode 100644 index 0000000000..b629e10857 --- /dev/null +++ b/tests/unit/test-util-filemonitor.c @@ -0,0 +1,720 @@ +/* + * Tests for util/filemonitor-*.c + * + * Copyright 2018 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this library; if not, see . + * + */ + +#include "qemu/osdep.h" +#include "qemu/main-loop.h" +#include "qapi/error.h" +#include "qemu/filemonitor.h" + +#include + +#include + +enum { + QFILE_MONITOR_TEST_OP_ADD_WATCH, + QFILE_MONITOR_TEST_OP_DEL_WATCH, + QFILE_MONITOR_TEST_OP_EVENT, + QFILE_MONITOR_TEST_OP_CREATE, + QFILE_MONITOR_TEST_OP_APPEND, + QFILE_MONITOR_TEST_OP_TRUNC, + QFILE_MONITOR_TEST_OP_RENAME, + QFILE_MONITOR_TEST_OP_TOUCH, + QFILE_MONITOR_TEST_OP_UNLINK, + QFILE_MONITOR_TEST_OP_MKDIR, + QFILE_MONITOR_TEST_OP_RMDIR, +}; + +typedef struct { + int type; + const char *filesrc; + const char *filedst; + int64_t *watchid; + int eventid; + /* + * Only valid with OP_EVENT - this event might be + * swapped with the next OP_EVENT + */ + bool swapnext; +} QFileMonitorTestOp; + +typedef struct { + int64_t id; + QFileMonitorEvent event; + char *filename; +} QFileMonitorTestRecord; + + +typedef struct { + QemuMutex lock; + GList *records; +} QFileMonitorTestData; + +static QemuMutex evlock; +static bool evstopping; +static bool evrunning; +static bool debug; + +/* + * Main function for a background thread that is + * running the event loop during the test + */ +static void * +qemu_file_monitor_test_event_loop(void *opaque G_GNUC_UNUSED) +{ + qemu_mutex_lock(&evlock); + + while (!evstopping) { + qemu_mutex_unlock(&evlock); + main_loop_wait(true); + qemu_mutex_lock(&evlock); + } + + evrunning = false; + qemu_mutex_unlock(&evlock); + return NULL; +} + + +/* + * File monitor event handler which simply maintains + * an ordered list of all events that it receives + */ +static void +qemu_file_monitor_test_handler(int64_t id, + QFileMonitorEvent event, + const char *filename, + void *opaque) +{ + QFileMonitorTestData *data = opaque; + QFileMonitorTestRecord *rec = g_new0(QFileMonitorTestRecord, 1); + + if (debug) { + g_printerr("Queue event id %" PRIx64 " event %d file %s\n", + id, event, filename); + } + rec->id = id; + rec->event = event; + rec->filename = g_strdup(filename); + + qemu_mutex_lock(&data->lock); + data->records = g_list_append(data->records, rec); + qemu_mutex_unlock(&data->lock); +} + + +static void +qemu_file_monitor_test_record_free(QFileMonitorTestRecord *rec) +{ + g_free(rec->filename); + g_free(rec); +} + + +/* + * Get the next event record that has been received by + * the file monitor event handler. Since events are + * emitted in the background thread running the event + * loop, we can't assume there is a record available + * immediately. Thus we will sleep for upto 5 seconds + * to wait for the event to be queued for us. + */ +static QFileMonitorTestRecord * +qemu_file_monitor_test_next_record(QFileMonitorTestData *data, + QFileMonitorTestRecord *pushback) +{ + GTimer *timer = g_timer_new(); + QFileMonitorTestRecord *record = NULL; + GList *tmp; + + qemu_mutex_lock(&data->lock); + while (!data->records && g_timer_elapsed(timer, NULL) < 5) { + qemu_mutex_unlock(&data->lock); + usleep(10 * 1000); + qemu_mutex_lock(&data->lock); + } + if (data->records) { + record = data->records->data; + if (pushback) { + data->records->data = pushback; + } else { + tmp = data->records; + data->records = g_list_remove_link(data->records, tmp); + g_list_free(tmp); + } + } else if (pushback) { + qemu_file_monitor_test_record_free(pushback); + } + qemu_mutex_unlock(&data->lock); + + g_timer_destroy(timer); + return record; +} + + +/* + * Check whether the event record we retrieved matches + * data we were expecting to see for the event + */ +static bool +qemu_file_monitor_test_expect(QFileMonitorTestData *data, + int64_t id, + QFileMonitorEvent event, + const char *filename, + bool swapnext) +{ + QFileMonitorTestRecord *rec; + bool ret = false; + + rec = qemu_file_monitor_test_next_record(data, NULL); + + retry: + if (!rec) { + g_printerr("Missing event watch id %" PRIx64 " event %d file %s\n", + id, event, filename); + return false; + } + + if (id != rec->id) { + if (swapnext) { + rec = qemu_file_monitor_test_next_record(data, rec); + swapnext = false; + goto retry; + } + g_printerr("Expected watch id %" PRIx64 " but got %" PRIx64 "\n", + id, rec->id); + goto cleanup; + } + + if (event != rec->event) { + g_printerr("Expected event %d but got %d\n", event, rec->event); + goto cleanup; + } + + if (!g_str_equal(filename, rec->filename)) { + g_printerr("Expected filename %s but got %s\n", + filename, rec->filename); + goto cleanup; + } + + ret = true; + + cleanup: + qemu_file_monitor_test_record_free(rec); + return ret; +} + + +static void +test_file_monitor_events(void) +{ + int64_t watch0 = 0; + int64_t watch1 = 0; + int64_t watch2 = 0; + int64_t watch3 = 0; + int64_t watch4 = 0; + int64_t watch5 = 0; + QFileMonitorTestOp ops[] = { + { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH, + .filesrc = NULL, .watchid = &watch0 }, + { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH, + .filesrc = "one.txt", .watchid = &watch1 }, + { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH, + .filesrc = "two.txt", .watchid = &watch2 }, + + + { .type = QFILE_MONITOR_TEST_OP_CREATE, + .filesrc = "one.txt", }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "one.txt", .watchid = &watch0, + .eventid = QFILE_MONITOR_EVENT_CREATED }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "one.txt", .watchid = &watch1, + .eventid = QFILE_MONITOR_EVENT_CREATED }, + + + { .type = QFILE_MONITOR_TEST_OP_CREATE, + .filesrc = "two.txt", }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "two.txt", .watchid = &watch0, + .eventid = QFILE_MONITOR_EVENT_CREATED }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "two.txt", .watchid = &watch2, + .eventid = QFILE_MONITOR_EVENT_CREATED }, + + + { .type = QFILE_MONITOR_TEST_OP_CREATE, + .filesrc = "three.txt", }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "three.txt", .watchid = &watch0, + .eventid = QFILE_MONITOR_EVENT_CREATED }, + + + { .type = QFILE_MONITOR_TEST_OP_UNLINK, + .filesrc = "three.txt", }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "three.txt", .watchid = &watch0, + .eventid = QFILE_MONITOR_EVENT_DELETED }, + + + { .type = QFILE_MONITOR_TEST_OP_RENAME, + .filesrc = "one.txt", .filedst = "two.txt" }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "one.txt", .watchid = &watch0, + .eventid = QFILE_MONITOR_EVENT_DELETED }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "one.txt", .watchid = &watch1, + .eventid = QFILE_MONITOR_EVENT_DELETED }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "two.txt", .watchid = &watch0, + .eventid = QFILE_MONITOR_EVENT_CREATED }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "two.txt", .watchid = &watch2, + .eventid = QFILE_MONITOR_EVENT_CREATED }, + + + { .type = QFILE_MONITOR_TEST_OP_APPEND, + .filesrc = "two.txt", }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "two.txt", .watchid = &watch0, + .eventid = QFILE_MONITOR_EVENT_MODIFIED }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "two.txt", .watchid = &watch2, + .eventid = QFILE_MONITOR_EVENT_MODIFIED }, + + + { .type = QFILE_MONITOR_TEST_OP_TOUCH, + .filesrc = "two.txt", }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "two.txt", .watchid = &watch0, + .eventid = QFILE_MONITOR_EVENT_ATTRIBUTES }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "two.txt", .watchid = &watch2, + .eventid = QFILE_MONITOR_EVENT_ATTRIBUTES }, + + + { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH, + .filesrc = "one.txt", .watchid = &watch1 }, + { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH, + .filesrc = "one.txt", .watchid = &watch3 }, + { .type = QFILE_MONITOR_TEST_OP_CREATE, + .filesrc = "one.txt", }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "one.txt", .watchid = &watch0, + .eventid = QFILE_MONITOR_EVENT_CREATED }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "one.txt", .watchid = &watch3, + .eventid = QFILE_MONITOR_EVENT_CREATED }, + + + { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH, + .filesrc = "one.txt", .watchid = &watch3 }, + { .type = QFILE_MONITOR_TEST_OP_UNLINK, + .filesrc = "one.txt", }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "one.txt", .watchid = &watch0, + .eventid = QFILE_MONITOR_EVENT_DELETED }, + + + { .type = QFILE_MONITOR_TEST_OP_MKDIR, + .filesrc = "fish", }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "fish", .watchid = &watch0, + .eventid = QFILE_MONITOR_EVENT_CREATED }, + + + { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH, + .filesrc = "fish/", .watchid = &watch4 }, + { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH, + .filesrc = "fish/one.txt", .watchid = &watch5 }, + { .type = QFILE_MONITOR_TEST_OP_CREATE, + .filesrc = "fish/one.txt", }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "one.txt", .watchid = &watch4, + .eventid = QFILE_MONITOR_EVENT_CREATED }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "one.txt", .watchid = &watch5, + .eventid = QFILE_MONITOR_EVENT_CREATED }, + + + { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH, + .filesrc = "fish/one.txt", .watchid = &watch5 }, + { .type = QFILE_MONITOR_TEST_OP_RENAME, + .filesrc = "fish/one.txt", .filedst = "two.txt", }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "one.txt", .watchid = &watch4, + .eventid = QFILE_MONITOR_EVENT_DELETED }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "two.txt", .watchid = &watch0, + .eventid = QFILE_MONITOR_EVENT_CREATED }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "two.txt", .watchid = &watch2, + .eventid = QFILE_MONITOR_EVENT_CREATED }, + + + { .type = QFILE_MONITOR_TEST_OP_RMDIR, + .filesrc = "fish", }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "", .watchid = &watch4, + .eventid = QFILE_MONITOR_EVENT_IGNORED, + .swapnext = true }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "fish", .watchid = &watch0, + .eventid = QFILE_MONITOR_EVENT_DELETED }, + { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH, + .filesrc = "fish", .watchid = &watch4 }, + + + { .type = QFILE_MONITOR_TEST_OP_UNLINK, + .filesrc = "two.txt", }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "two.txt", .watchid = &watch0, + .eventid = QFILE_MONITOR_EVENT_DELETED }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "two.txt", .watchid = &watch2, + .eventid = QFILE_MONITOR_EVENT_DELETED }, + + + { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH, + .filesrc = "two.txt", .watchid = &watch2 }, + { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH, + .filesrc = NULL, .watchid = &watch0 }, + }; + Error *local_err = NULL; + GError *gerr = NULL; + QFileMonitor *mon = qemu_file_monitor_new(&local_err); + QemuThread th; + GTimer *timer; + gchar *dir = NULL; + int err = -1; + gsize i; + char *pathsrc = NULL; + char *pathdst = NULL; + QFileMonitorTestData data; + GHashTable *ids = g_hash_table_new(g_int64_hash, g_int64_equal); + char *travis_arch; + + qemu_mutex_init(&data.lock); + data.records = NULL; + + /* + * This test does not work on Travis LXD containers since some + * syscalls are blocked in that environment. + */ + travis_arch = getenv("TRAVIS_ARCH"); + if (travis_arch && !g_str_equal(travis_arch, "x86_64")) { + g_test_skip("Test does not work on non-x86 Travis containers."); + return; + } + + /* + * The file monitor needs the main loop running in + * order to receive events from inotify. We must + * thus spawn a background thread to run an event + * loop impl, while this thread triggers the + * actual file operations we're testing + */ + evrunning = 1; + evstopping = 0; + qemu_thread_create(&th, "event-loop", + qemu_file_monitor_test_event_loop, NULL, + QEMU_THREAD_JOINABLE); + + if (local_err) { + g_printerr("File monitoring not available: %s", + error_get_pretty(local_err)); + error_free(local_err); + return; + } + + dir = g_dir_make_tmp("test-util-filemonitor-XXXXXX", + &gerr); + if (!dir) { + g_printerr("Unable to create tmp dir %s", + gerr->message); + g_error_free(gerr); + abort(); + } + + /* + * Run through the operation sequence validating events + * as we go + */ + for (i = 0; i < G_N_ELEMENTS(ops); i++) { + const QFileMonitorTestOp *op = &(ops[i]); + int fd; + struct utimbuf ubuf; + char *watchdir; + const char *watchfile; + + pathsrc = g_strdup_printf("%s/%s", dir, op->filesrc); + if (op->filedst) { + pathdst = g_strdup_printf("%s/%s", dir, op->filedst); + } + + switch (op->type) { + case QFILE_MONITOR_TEST_OP_ADD_WATCH: + if (debug) { + g_printerr("Add watch %s %s\n", + dir, op->filesrc); + } + if (op->filesrc && strchr(op->filesrc, '/')) { + watchdir = g_strdup_printf("%s/%s", dir, op->filesrc); + watchfile = strrchr(watchdir, '/'); + *(char *)watchfile = '\0'; + watchfile++; + if (*watchfile == '\0') { + watchfile = NULL; + } + } else { + watchdir = g_strdup(dir); + watchfile = op->filesrc; + } + *op->watchid = + qemu_file_monitor_add_watch(mon, + watchdir, + watchfile, + qemu_file_monitor_test_handler, + &data, + &local_err); + g_free(watchdir); + if (*op->watchid < 0) { + g_printerr("Unable to add watch %s", + error_get_pretty(local_err)); + error_free(local_err); + goto cleanup; + } + if (debug) { + g_printerr("Watch ID %" PRIx64 "\n", *op->watchid); + } + if (g_hash_table_contains(ids, op->watchid)) { + g_printerr("Watch ID %" PRIx64 "already exists", *op->watchid); + goto cleanup; + } + g_hash_table_add(ids, op->watchid); + break; + case QFILE_MONITOR_TEST_OP_DEL_WATCH: + if (debug) { + g_printerr("Del watch %s %" PRIx64 "\n", dir, *op->watchid); + } + if (op->filesrc && strchr(op->filesrc, '/')) { + watchdir = g_strdup_printf("%s/%s", dir, op->filesrc); + watchfile = strrchr(watchdir, '/'); + *(char *)watchfile = '\0'; + } else { + watchdir = g_strdup(dir); + } + g_hash_table_remove(ids, op->watchid); + qemu_file_monitor_remove_watch(mon, + watchdir, + *op->watchid); + g_free(watchdir); + break; + case QFILE_MONITOR_TEST_OP_EVENT: + if (debug) { + g_printerr("Event id=%" PRIx64 " event=%d file=%s\n", + *op->watchid, op->eventid, op->filesrc); + } + if (!qemu_file_monitor_test_expect(&data, *op->watchid, + op->eventid, op->filesrc, + op->swapnext)) + goto cleanup; + break; + case QFILE_MONITOR_TEST_OP_CREATE: + if (debug) { + g_printerr("Create %s\n", pathsrc); + } + fd = open(pathsrc, O_WRONLY | O_CREAT, 0700); + if (fd < 0) { + g_printerr("Unable to create %s: %s", + pathsrc, strerror(errno)); + goto cleanup; + } + close(fd); + break; + + case QFILE_MONITOR_TEST_OP_APPEND: + if (debug) { + g_printerr("Append %s\n", pathsrc); + } + fd = open(pathsrc, O_WRONLY | O_APPEND, 0700); + if (fd < 0) { + g_printerr("Unable to open %s: %s", + pathsrc, strerror(errno)); + goto cleanup; + } + + if (write(fd, "Hello World", 10) != 10) { + g_printerr("Unable to write %s: %s", + pathsrc, strerror(errno)); + close(fd); + goto cleanup; + } + close(fd); + break; + + case QFILE_MONITOR_TEST_OP_TRUNC: + if (debug) { + g_printerr("Truncate %s\n", pathsrc); + } + if (truncate(pathsrc, 4) < 0) { + g_printerr("Unable to truncate %s: %s", + pathsrc, strerror(errno)); + goto cleanup; + } + break; + + case QFILE_MONITOR_TEST_OP_RENAME: + if (debug) { + g_printerr("Rename %s -> %s\n", pathsrc, pathdst); + } + if (rename(pathsrc, pathdst) < 0) { + g_printerr("Unable to rename %s to %s: %s", + pathsrc, pathdst, strerror(errno)); + goto cleanup; + } + break; + + case QFILE_MONITOR_TEST_OP_UNLINK: + if (debug) { + g_printerr("Unlink %s\n", pathsrc); + } + if (unlink(pathsrc) < 0) { + g_printerr("Unable to unlink %s: %s", + pathsrc, strerror(errno)); + goto cleanup; + } + break; + + case QFILE_MONITOR_TEST_OP_TOUCH: + if (debug) { + g_printerr("Touch %s\n", pathsrc); + } + ubuf.actime = 1024; + ubuf.modtime = 1025; + if (utime(pathsrc, &ubuf) < 0) { + g_printerr("Unable to touch %s: %s", + pathsrc, strerror(errno)); + goto cleanup; + } + break; + + case QFILE_MONITOR_TEST_OP_MKDIR: + if (debug) { + g_printerr("Mkdir %s\n", pathsrc); + } + if (g_mkdir_with_parents(pathsrc, 0700) < 0) { + g_printerr("Unable to mkdir %s: %s", + pathsrc, strerror(errno)); + goto cleanup; + } + break; + + case QFILE_MONITOR_TEST_OP_RMDIR: + if (debug) { + g_printerr("Rmdir %s\n", pathsrc); + } + if (rmdir(pathsrc) < 0) { + g_printerr("Unable to rmdir %s: %s", + pathsrc, strerror(errno)); + goto cleanup; + } + break; + + default: + g_assert_not_reached(); + } + + g_free(pathsrc); + g_free(pathdst); + pathsrc = pathdst = NULL; + } + + g_assert_cmpint(g_hash_table_size(ids), ==, 0); + + err = 0; + + cleanup: + g_free(pathsrc); + g_free(pathdst); + + qemu_mutex_lock(&evlock); + evstopping = 1; + timer = g_timer_new(); + while (evrunning && g_timer_elapsed(timer, NULL) < 5) { + qemu_mutex_unlock(&evlock); + usleep(10 * 1000); + qemu_mutex_lock(&evlock); + } + qemu_mutex_unlock(&evlock); + + if (g_timer_elapsed(timer, NULL) >= 5) { + g_printerr("Event loop failed to quit after 5 seconds\n"); + } + g_timer_destroy(timer); + + qemu_file_monitor_free(mon); + g_list_foreach(data.records, + (GFunc)qemu_file_monitor_test_record_free, NULL); + g_list_free(data.records); + qemu_mutex_destroy(&data.lock); + if (dir) { + for (i = 0; i < G_N_ELEMENTS(ops); i++) { + const QFileMonitorTestOp *op = &(ops[i]); + char *path = g_strdup_printf("%s/%s", + dir, op->filesrc); + if (op->type == QFILE_MONITOR_TEST_OP_MKDIR) { + rmdir(path); + g_free(path); + } else { + unlink(path); + g_free(path); + if (op->filedst) { + path = g_strdup_printf("%s/%s", + dir, op->filedst); + unlink(path); + g_free(path); + } + } + } + if (rmdir(dir) < 0) { + g_printerr("Failed to remove %s: %s\n", + dir, strerror(errno)); + abort(); + } + } + g_hash_table_unref(ids); + g_free(dir); + g_assert(err == 0); +} + + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + qemu_init_main_loop(&error_abort); + + qemu_mutex_init(&evlock); + + debug = getenv("FILEMONITOR_DEBUG") != NULL; + g_test_add_func("/util/filemonitor", test_file_monitor_events); + + return g_test_run(); +} diff --git a/tests/unit/test-util-sockets.c b/tests/unit/test-util-sockets.c new file mode 100644 index 0000000000..67486055ed --- /dev/null +++ b/tests/unit/test-util-sockets.c @@ -0,0 +1,379 @@ +/* + * Tests for util/qemu-sockets.c + * + * Copyright 2018 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this library; if not, see . + * + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "qemu/sockets.h" +#include "qapi/error.h" +#include "socket-helpers.h" +#include "monitor/monitor.h" + +static void test_fd_is_socket_bad(void) +{ + char *tmp = g_strdup("qemu-test-util-sockets-XXXXXX"); + int fd = mkstemp(tmp); + if (fd != 0) { + unlink(tmp); + } + g_free(tmp); + + g_assert(fd >= 0); + + g_assert(!fd_is_socket(fd)); + close(fd); +} + +static void test_fd_is_socket_good(void) +{ + int fd = qemu_socket(PF_INET, SOCK_STREAM, 0); + + g_assert(fd >= 0); + + g_assert(fd_is_socket(fd)); + close(fd); +} + +static int mon_fd = -1; +static const char *mon_fdname; +__thread Monitor *cur_mon; + +int monitor_get_fd(Monitor *mon, const char *fdname, Error **errp) +{ + g_assert(cur_mon); + g_assert(mon == cur_mon); + if (mon_fd == -1 || !g_str_equal(mon_fdname, fdname)) { + error_setg(errp, "No fd named %s", fdname); + return -1; + } + return dup(mon_fd); +} + +/* + * Syms of stubs in libqemuutil.a are discarded at .o file + * granularity. To replace monitor_get_fd() and monitor_cur(), we + * must ensure that we also replace any other symbol that is used in + * the binary and would be taken from the same stub object file, + * otherwise we get duplicate syms at link time. + */ +Monitor *monitor_cur(void) { return cur_mon; } +int monitor_vprintf(Monitor *mon, const char *fmt, va_list ap) { abort(); } + +#ifndef _WIN32 +static void test_socket_fd_pass_name_good(void) +{ + SocketAddress addr; + int fd; + + cur_mon = g_malloc(1); /* Fake a monitor */ + mon_fdname = "myfd"; + mon_fd = qemu_socket(AF_INET, SOCK_STREAM, 0); + g_assert_cmpint(mon_fd, >, STDERR_FILENO); + + addr.type = SOCKET_ADDRESS_TYPE_FD; + addr.u.fd.str = g_strdup(mon_fdname); + + fd = socket_connect(&addr, &error_abort); + g_assert_cmpint(fd, !=, -1); + g_assert_cmpint(fd, !=, mon_fd); + close(fd); + + fd = socket_listen(&addr, 1, &error_abort); + g_assert_cmpint(fd, !=, -1); + g_assert_cmpint(fd, !=, mon_fd); + close(fd); + + g_free(addr.u.fd.str); + mon_fdname = NULL; + close(mon_fd); + mon_fd = -1; + g_free(cur_mon); + cur_mon = NULL; +} + +static void test_socket_fd_pass_name_bad(void) +{ + SocketAddress addr; + Error *err = NULL; + int fd; + + cur_mon = g_malloc(1); /* Fake a monitor */ + mon_fdname = "myfd"; + mon_fd = dup(STDOUT_FILENO); + g_assert_cmpint(mon_fd, >, STDERR_FILENO); + + addr.type = SOCKET_ADDRESS_TYPE_FD; + addr.u.fd.str = g_strdup(mon_fdname); + + fd = socket_connect(&addr, &err); + g_assert_cmpint(fd, ==, -1); + error_free_or_abort(&err); + + fd = socket_listen(&addr, 1, &err); + g_assert_cmpint(fd, ==, -1); + error_free_or_abort(&err); + + g_free(addr.u.fd.str); + mon_fdname = NULL; + close(mon_fd); + mon_fd = -1; + g_free(cur_mon); + cur_mon = NULL; +} + +static void test_socket_fd_pass_name_nomon(void) +{ + SocketAddress addr; + Error *err = NULL; + int fd; + + g_assert(cur_mon == NULL); + + addr.type = SOCKET_ADDRESS_TYPE_FD; + addr.u.fd.str = g_strdup("myfd"); + + fd = socket_connect(&addr, &err); + g_assert_cmpint(fd, ==, -1); + error_free_or_abort(&err); + + fd = socket_listen(&addr, 1, &err); + g_assert_cmpint(fd, ==, -1); + error_free_or_abort(&err); + + g_free(addr.u.fd.str); +} + + +static void test_socket_fd_pass_num_good(void) +{ + SocketAddress addr; + int fd, sfd; + + g_assert(cur_mon == NULL); + sfd = qemu_socket(AF_INET, SOCK_STREAM, 0); + g_assert_cmpint(sfd, >, STDERR_FILENO); + + addr.type = SOCKET_ADDRESS_TYPE_FD; + addr.u.fd.str = g_strdup_printf("%d", sfd); + + fd = socket_connect(&addr, &error_abort); + g_assert_cmpint(fd, ==, sfd); + + fd = socket_listen(&addr, 1, &error_abort); + g_assert_cmpint(fd, ==, sfd); + + g_free(addr.u.fd.str); + close(sfd); +} + +static void test_socket_fd_pass_num_bad(void) +{ + SocketAddress addr; + Error *err = NULL; + int fd, sfd; + + g_assert(cur_mon == NULL); + sfd = dup(STDOUT_FILENO); + + addr.type = SOCKET_ADDRESS_TYPE_FD; + addr.u.fd.str = g_strdup_printf("%d", sfd); + + fd = socket_connect(&addr, &err); + g_assert_cmpint(fd, ==, -1); + error_free_or_abort(&err); + + fd = socket_listen(&addr, 1, &err); + g_assert_cmpint(fd, ==, -1); + error_free_or_abort(&err); + + g_free(addr.u.fd.str); + close(sfd); +} + +static void test_socket_fd_pass_num_nocli(void) +{ + SocketAddress addr; + Error *err = NULL; + int fd; + + cur_mon = g_malloc(1); /* Fake a monitor */ + + addr.type = SOCKET_ADDRESS_TYPE_FD; + addr.u.fd.str = g_strdup_printf("%d", STDOUT_FILENO); + + fd = socket_connect(&addr, &err); + g_assert_cmpint(fd, ==, -1); + error_free_or_abort(&err); + + fd = socket_listen(&addr, 1, &err); + g_assert_cmpint(fd, ==, -1); + error_free_or_abort(&err); + + g_free(addr.u.fd.str); +} +#endif + +#ifdef CONFIG_LINUX + +#define ABSTRACT_SOCKET_VARIANTS 3 + +typedef struct { + SocketAddress *server, *client[ABSTRACT_SOCKET_VARIANTS]; + bool expect_connect[ABSTRACT_SOCKET_VARIANTS]; +} abstract_socket_matrix_row; + +static gpointer unix_client_thread_func(gpointer user_data) +{ + abstract_socket_matrix_row *row = user_data; + Error *err = NULL; + int i, fd; + + for (i = 0; i < ABSTRACT_SOCKET_VARIANTS; i++) { + if (row->expect_connect[i]) { + fd = socket_connect(row->client[i], &error_abort); + g_assert_cmpint(fd, >=, 0); + } else { + fd = socket_connect(row->client[i], &err); + g_assert_cmpint(fd, ==, -1); + error_free_or_abort(&err); + } + close(fd); + } + return NULL; +} + +static void test_socket_unix_abstract_row(abstract_socket_matrix_row *test) +{ + int fd, connfd, i; + GThread *cli; + struct sockaddr_un un; + socklen_t len = sizeof(un); + + /* Last one must connect, or else accept() below hangs */ + assert(test->expect_connect[ABSTRACT_SOCKET_VARIANTS - 1]); + + fd = socket_listen(test->server, 1, &error_abort); + g_assert_cmpint(fd, >=, 0); + g_assert(fd_is_socket(fd)); + + cli = g_thread_new("abstract_unix_client", + unix_client_thread_func, + test); + + for (i = 0; i < ABSTRACT_SOCKET_VARIANTS; i++) { + if (test->expect_connect[i]) { + connfd = accept(fd, (struct sockaddr *)&un, &len); + g_assert_cmpint(connfd, !=, -1); + close(connfd); + } + } + + close(fd); + g_thread_join(cli); +} + +static void test_socket_unix_abstract(void) +{ + SocketAddress addr, addr_tight, addr_padded; + abstract_socket_matrix_row matrix[ABSTRACT_SOCKET_VARIANTS] = { + { &addr, + { &addr_tight, &addr_padded, &addr }, + { true, false, true } }, + { &addr_tight, + { &addr_padded, &addr, &addr_tight }, + { false, true, true } }, + { &addr_padded, + { &addr, &addr_tight, &addr_padded }, + { false, false, true } } + }; + int i; + + addr.type = SOCKET_ADDRESS_TYPE_UNIX; + addr.u.q_unix.path = g_strdup_printf("unix-%d-%u", + getpid(), g_random_int()); + addr.u.q_unix.has_abstract = true; + addr.u.q_unix.abstract = true; + addr.u.q_unix.has_tight = false; + addr.u.q_unix.tight = false; + + addr_tight = addr; + addr_tight.u.q_unix.has_tight = true; + addr_tight.u.q_unix.tight = true; + + addr_padded = addr; + addr_padded.u.q_unix.has_tight = true; + addr_padded.u.q_unix.tight = false; + + for (i = 0; i < ABSTRACT_SOCKET_VARIANTS; i++) { + test_socket_unix_abstract_row(&matrix[i]); + } + + g_free(addr.u.q_unix.path); +} + +#endif /* CONFIG_LINUX */ + +int main(int argc, char **argv) +{ + bool has_ipv4, has_ipv6; + + qemu_init_main_loop(&error_abort); + socket_init(); + + g_test_init(&argc, &argv, NULL); + + /* We're creating actual IPv4/6 sockets, so we should + * check if the host running tests actually supports + * each protocol to avoid breaking tests on machines + * with either IPv4 or IPv6 disabled. + */ + if (socket_check_protocol_support(&has_ipv4, &has_ipv6) < 0) { + g_printerr("socket_check_protocol_support() failed\n"); + goto end; + } + + if (has_ipv4) { + g_test_add_func("/util/socket/is-socket/bad", + test_fd_is_socket_bad); + g_test_add_func("/util/socket/is-socket/good", + test_fd_is_socket_good); +#ifndef _WIN32 + g_test_add_func("/socket/fd-pass/name/good", + test_socket_fd_pass_name_good); + g_test_add_func("/socket/fd-pass/name/bad", + test_socket_fd_pass_name_bad); + g_test_add_func("/socket/fd-pass/name/nomon", + test_socket_fd_pass_name_nomon); + g_test_add_func("/socket/fd-pass/num/good", + test_socket_fd_pass_num_good); + g_test_add_func("/socket/fd-pass/num/bad", + test_socket_fd_pass_num_bad); + g_test_add_func("/socket/fd-pass/num/nocli", + test_socket_fd_pass_num_nocli); +#endif + } + +#ifdef CONFIG_LINUX + g_test_add_func("/util/socket/unix-abstract", + test_socket_unix_abstract); +#endif + +end: + return g_test_run(); +} diff --git a/tests/unit/test-uuid.c b/tests/unit/test-uuid.c new file mode 100644 index 0000000000..c111de5fc1 --- /dev/null +++ b/tests/unit/test-uuid.c @@ -0,0 +1,184 @@ +/* + * QEMU UUID Library + * + * Copyright (c) 2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + * + */ + +#include "qemu/osdep.h" +#include "qemu/uuid.h" + +struct { + const char *uuidstr; + QemuUUID uuid; + bool uuidstr_is_valid; + bool check_unparse; +} uuid_test_data[] = { + { /* Normal */ + "586ece27-7f09-41e0-9e74-e901317e9d42", + { { { + 0x58, 0x6e, 0xce, 0x27, 0x7f, 0x09, 0x41, 0xe0, + 0x9e, 0x74, 0xe9, 0x01, 0x31, 0x7e, 0x9d, 0x42, + } } }, + true, true, + }, { /* NULL */ + "00000000-0000-0000-0000-000000000000", + { }, + true, true, + }, { /* Upper case */ + "0CC6C752-3961-4028-A286-C05CC616D396", + { { { + 0x0c, 0xc6, 0xc7, 0x52, 0x39, 0x61, 0x40, 0x28, + 0xa2, 0x86, 0xc0, 0x5c, 0xc6, 0x16, 0xd3, 0x96, + } } }, + true, false, + }, { /* Mixed case */ + "0CC6C752-3961-4028-a286-c05cc616D396", + { { { + 0x0c, 0xc6, 0xc7, 0x52, 0x39, 0x61, 0x40, 0x28, + 0xa2, 0x86, 0xc0, 0x5c, 0xc6, 0x16, 0xd3, 0x96, + } } }, + true, false, + }, { /* Empty */ + "" + }, { /* Too short */ + "abc", + }, { /* Non-hex */ + "abcdefgh-0000-0000-0000-000000000000", + }, { /* No '-' */ + "0cc6c75239614028a286c05cc616d396", + }, { /* '-' in wrong position */ + "0cc6c-7523961-4028-a286-c05cc616d396", + }, { /* Double '-' */ + "0cc6c752--3961-4028-a286-c05cc616d396", + }, { /* Too long */ + "0000000000000000000000000000000000000000000000", + }, { /* Invalid char in the beginning */ + ")cc6c752-3961-4028-a286-c05cc616d396", + }, { /* Invalid char in the beginning, in extra */ + ")0cc6c752-3961-4028-a286-c05cc616d396", + }, { /* Invalid char in the middle */ + "0cc6c752-39*1-4028-a286-c05cc616d396", + }, { /* Invalid char in the middle, in extra */ + "0cc6c752-39*61-4028-a286-c05cc616d396", + }, { /* Invalid char in the end */ + "0cc6c752-3961-4028-a286-c05cc616d39&", + }, { /* Invalid char in the end, in extra */ + "0cc6c752-3961-4028-a286-c05cc616d396&", + }, { /* Short end and trailing space */ + "0cc6c752-3961-4028-a286-c05cc616d39 ", + }, { /* Leading space and short end */ + " 0cc6c752-3961-4028-a286-c05cc616d39", + }, +}; + +static inline bool uuid_is_valid(QemuUUID *uuid) +{ + return qemu_uuid_is_null(uuid) || + ((uuid->data[6] & 0xf0) == 0x40 && (uuid->data[8] & 0xc0) == 0x80); +} + +static void test_uuid_generate(void) +{ + QemuUUID uuid_not_null = { { { + 0x58, 0x6e, 0xce, 0x27, 0x7f, 0x09, 0x41, 0xe0, + 0x9e, 0x74, 0xe9, 0x01, 0x31, 0x7e, 0x9d, 0x42 + } } }; + QemuUUID uuid; + int i; + + for (i = 0; i < 100; ++i) { + qemu_uuid_generate(&uuid); + g_assert(uuid_is_valid(&uuid)); + g_assert_false(qemu_uuid_is_null(&uuid)); + g_assert_false(qemu_uuid_is_equal(&uuid_not_null, &uuid)); + } +} + +static void test_uuid_is_null(void) +{ + QemuUUID uuid_null = { }; + QemuUUID uuid_not_null = { { { + 0x58, 0x6e, 0xce, 0x27, 0x7f, 0x09, 0x41, 0xe0, + 0x9e, 0x74, 0xe9, 0x01, 0x31, 0x7e, 0x9d, 0x42 + } } }; + QemuUUID uuid_not_null_2 = { { { 1 } } }; + + g_assert(qemu_uuid_is_null(&uuid_null)); + g_assert_false(qemu_uuid_is_null(&uuid_not_null)); + g_assert_false(qemu_uuid_is_null(&uuid_not_null_2)); +} + +static void test_uuid_parse(void) +{ + int i, r; + + for (i = 0; i < ARRAY_SIZE(uuid_test_data); i++) { + QemuUUID uuid; + bool is_valid = uuid_test_data[i].uuidstr_is_valid; + + r = qemu_uuid_parse(uuid_test_data[i].uuidstr, &uuid); + g_assert_cmpint(!r, ==, is_valid); + if (is_valid) { + g_assert_cmpint(is_valid, ==, uuid_is_valid(&uuid)); + g_assert_cmpmem(&uuid_test_data[i].uuid, sizeof(uuid), + &uuid, sizeof(uuid)); + } + } +} + +static void test_uuid_unparse(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(uuid_test_data); i++) { + char out[37]; + + if (!uuid_test_data[i].check_unparse) { + continue; + } + qemu_uuid_unparse(&uuid_test_data[i].uuid, out); + g_assert_cmpstr(uuid_test_data[i].uuidstr, ==, out); + } +} + +static void test_uuid_unparse_strdup(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(uuid_test_data); i++) { + char *out; + + if (!uuid_test_data[i].check_unparse) { + continue; + } + out = qemu_uuid_unparse_strdup(&uuid_test_data[i].uuid); + g_assert_cmpstr(uuid_test_data[i].uuidstr, ==, out); + g_free(out); + } +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + g_test_add_func("/uuid/is_null", test_uuid_is_null); + g_test_add_func("/uuid/generate", test_uuid_generate); + g_test_add_func("/uuid/parse", test_uuid_parse); + g_test_add_func("/uuid/unparse", test_uuid_unparse); + g_test_add_func("/uuid/unparse_strdup", test_uuid_unparse_strdup); + + return g_test_run(); +} diff --git a/tests/unit/test-visitor-serialization.c b/tests/unit/test-visitor-serialization.c new file mode 100644 index 0000000000..4629958647 --- /dev/null +++ b/tests/unit/test-visitor-serialization.c @@ -0,0 +1,1116 @@ +/* + * Unit-tests for visitor-based serialization + * + * Copyright (C) 2014-2015 Red Hat, Inc. + * Copyright IBM, Corp. 2012 + * + * Authors: + * Michael Roth + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include + +#include "qemu-common.h" +#include "test-qapi-visit.h" +#include "qapi/error.h" +#include "qapi/qmp/qjson.h" +#include "qapi/qmp/qstring.h" +#include "qapi/qobject-input-visitor.h" +#include "qapi/qobject-output-visitor.h" +#include "qapi/string-input-visitor.h" +#include "qapi/string-output-visitor.h" +#include "qapi/dealloc-visitor.h" + +enum PrimitiveTypeKind { + PTYPE_STRING = 0, + PTYPE_BOOLEAN, + PTYPE_NUMBER, + PTYPE_INTEGER, + PTYPE_U8, + PTYPE_U16, + PTYPE_U32, + PTYPE_U64, + PTYPE_S8, + PTYPE_S16, + PTYPE_S32, + PTYPE_S64, + PTYPE_EOL, +}; + +typedef struct PrimitiveType { + union { + const char *string; + bool boolean; + double number; + int64_t integer; + uint8_t u8; + uint16_t u16; + uint32_t u32; + uint64_t u64; + int8_t s8; + int16_t s16; + int32_t s32; + int64_t s64; + } value; + enum PrimitiveTypeKind type; + const char *description; +} PrimitiveType; + +typedef struct PrimitiveList { + union { + strList *strings; + boolList *booleans; + numberList *numbers; + intList *integers; + int8List *s8_integers; + int16List *s16_integers; + int32List *s32_integers; + int64List *s64_integers; + uint8List *u8_integers; + uint16List *u16_integers; + uint32List *u32_integers; + uint64List *u64_integers; + } value; + enum PrimitiveTypeKind type; + const char *description; +} PrimitiveList; + +/* test helpers */ + +typedef void (*VisitorFunc)(Visitor *v, void **native, Error **errp); + +static void dealloc_helper(void *native_in, VisitorFunc visit, Error **errp) +{ + Visitor *v = qapi_dealloc_visitor_new(); + + visit(v, &native_in, errp); + + visit_free(v); +} + +static void visit_primitive_type(Visitor *v, void **native, Error **errp) +{ + PrimitiveType *pt = *native; + switch(pt->type) { + case PTYPE_STRING: + visit_type_str(v, NULL, (char **)&pt->value.string, errp); + break; + case PTYPE_BOOLEAN: + visit_type_bool(v, NULL, &pt->value.boolean, errp); + break; + case PTYPE_NUMBER: + visit_type_number(v, NULL, &pt->value.number, errp); + break; + case PTYPE_INTEGER: + visit_type_int(v, NULL, &pt->value.integer, errp); + break; + case PTYPE_U8: + visit_type_uint8(v, NULL, &pt->value.u8, errp); + break; + case PTYPE_U16: + visit_type_uint16(v, NULL, &pt->value.u16, errp); + break; + case PTYPE_U32: + visit_type_uint32(v, NULL, &pt->value.u32, errp); + break; + case PTYPE_U64: + visit_type_uint64(v, NULL, &pt->value.u64, errp); + break; + case PTYPE_S8: + visit_type_int8(v, NULL, &pt->value.s8, errp); + break; + case PTYPE_S16: + visit_type_int16(v, NULL, &pt->value.s16, errp); + break; + case PTYPE_S32: + visit_type_int32(v, NULL, &pt->value.s32, errp); + break; + case PTYPE_S64: + visit_type_int64(v, NULL, &pt->value.s64, errp); + break; + case PTYPE_EOL: + g_assert_not_reached(); + } +} + +static void visit_primitive_list(Visitor *v, void **native, Error **errp) +{ + PrimitiveList *pl = *native; + switch (pl->type) { + case PTYPE_STRING: + visit_type_strList(v, NULL, &pl->value.strings, errp); + break; + case PTYPE_BOOLEAN: + visit_type_boolList(v, NULL, &pl->value.booleans, errp); + break; + case PTYPE_NUMBER: + visit_type_numberList(v, NULL, &pl->value.numbers, errp); + break; + case PTYPE_INTEGER: + visit_type_intList(v, NULL, &pl->value.integers, errp); + break; + case PTYPE_S8: + visit_type_int8List(v, NULL, &pl->value.s8_integers, errp); + break; + case PTYPE_S16: + visit_type_int16List(v, NULL, &pl->value.s16_integers, errp); + break; + case PTYPE_S32: + visit_type_int32List(v, NULL, &pl->value.s32_integers, errp); + break; + case PTYPE_S64: + visit_type_int64List(v, NULL, &pl->value.s64_integers, errp); + break; + case PTYPE_U8: + visit_type_uint8List(v, NULL, &pl->value.u8_integers, errp); + break; + case PTYPE_U16: + visit_type_uint16List(v, NULL, &pl->value.u16_integers, errp); + break; + case PTYPE_U32: + visit_type_uint32List(v, NULL, &pl->value.u32_integers, errp); + break; + case PTYPE_U64: + visit_type_uint64List(v, NULL, &pl->value.u64_integers, errp); + break; + default: + g_assert_not_reached(); + } +} + + +static TestStruct *struct_create(void) +{ + TestStruct *ts = g_malloc0(sizeof(*ts)); + ts->integer = -42; + ts->boolean = true; + ts->string = strdup("test string"); + return ts; +} + +static void struct_compare(TestStruct *ts1, TestStruct *ts2) +{ + g_assert(ts1); + g_assert(ts2); + g_assert_cmpint(ts1->integer, ==, ts2->integer); + g_assert(ts1->boolean == ts2->boolean); + g_assert_cmpstr(ts1->string, ==, ts2->string); +} + +static void struct_cleanup(TestStruct *ts) +{ + g_free(ts->string); + g_free(ts); +} + +static void visit_struct(Visitor *v, void **native, Error **errp) +{ + visit_type_TestStruct(v, NULL, (TestStruct **)native, errp); +} + +static UserDefTwo *nested_struct_create(void) +{ + UserDefTwo *udnp = g_malloc0(sizeof(*udnp)); + udnp->string0 = strdup("test_string0"); + udnp->dict1 = g_malloc0(sizeof(*udnp->dict1)); + udnp->dict1->string1 = strdup("test_string1"); + udnp->dict1->dict2 = g_malloc0(sizeof(*udnp->dict1->dict2)); + udnp->dict1->dict2->userdef = g_new0(UserDefOne, 1); + udnp->dict1->dict2->userdef->integer = 42; + udnp->dict1->dict2->userdef->string = strdup("test_string"); + udnp->dict1->dict2->string = strdup("test_string2"); + udnp->dict1->dict3 = g_malloc0(sizeof(*udnp->dict1->dict3)); + udnp->dict1->has_dict3 = true; + udnp->dict1->dict3->userdef = g_new0(UserDefOne, 1); + udnp->dict1->dict3->userdef->integer = 43; + udnp->dict1->dict3->userdef->string = strdup("test_string"); + udnp->dict1->dict3->string = strdup("test_string3"); + return udnp; +} + +static void nested_struct_compare(UserDefTwo *udnp1, UserDefTwo *udnp2) +{ + g_assert(udnp1); + g_assert(udnp2); + g_assert_cmpstr(udnp1->string0, ==, udnp2->string0); + g_assert_cmpstr(udnp1->dict1->string1, ==, udnp2->dict1->string1); + g_assert_cmpint(udnp1->dict1->dict2->userdef->integer, ==, + udnp2->dict1->dict2->userdef->integer); + g_assert_cmpstr(udnp1->dict1->dict2->userdef->string, ==, + udnp2->dict1->dict2->userdef->string); + g_assert_cmpstr(udnp1->dict1->dict2->string, ==, + udnp2->dict1->dict2->string); + g_assert(udnp1->dict1->has_dict3 == udnp2->dict1->has_dict3); + g_assert_cmpint(udnp1->dict1->dict3->userdef->integer, ==, + udnp2->dict1->dict3->userdef->integer); + g_assert_cmpstr(udnp1->dict1->dict3->userdef->string, ==, + udnp2->dict1->dict3->userdef->string); + g_assert_cmpstr(udnp1->dict1->dict3->string, ==, + udnp2->dict1->dict3->string); +} + +static void nested_struct_cleanup(UserDefTwo *udnp) +{ + qapi_free_UserDefTwo(udnp); +} + +static void visit_nested_struct(Visitor *v, void **native, Error **errp) +{ + visit_type_UserDefTwo(v, NULL, (UserDefTwo **)native, errp); +} + +static void visit_nested_struct_list(Visitor *v, void **native, Error **errp) +{ + visit_type_UserDefTwoList(v, NULL, (UserDefTwoList **)native, errp); +} + +/* test cases */ + +typedef enum VisitorCapabilities { + VCAP_PRIMITIVES = 1, + VCAP_STRUCTURES = 2, + VCAP_LISTS = 4, + VCAP_PRIMITIVE_LISTS = 8, +} VisitorCapabilities; + +typedef struct SerializeOps { + void (*serialize)(void *native_in, void **datap, + VisitorFunc visit, Error **errp); + void (*deserialize)(void **native_out, void *datap, + VisitorFunc visit, Error **errp); + void (*cleanup)(void *datap); + const char *type; + VisitorCapabilities caps; +} SerializeOps; + +typedef struct TestArgs { + const SerializeOps *ops; + void *test_data; +} TestArgs; + +static void test_primitives(gconstpointer opaque) +{ + TestArgs *args = (TestArgs *) opaque; + const SerializeOps *ops = args->ops; + PrimitiveType *pt = args->test_data; + PrimitiveType *pt_copy = g_malloc0(sizeof(*pt_copy)); + void *serialize_data; + + pt_copy->type = pt->type; + ops->serialize(pt, &serialize_data, visit_primitive_type, &error_abort); + ops->deserialize((void **)&pt_copy, serialize_data, visit_primitive_type, + &error_abort); + + g_assert(pt_copy != NULL); + switch (pt->type) { + case PTYPE_STRING: + g_assert_cmpstr(pt->value.string, ==, pt_copy->value.string); + g_free((char *)pt_copy->value.string); + break; + case PTYPE_BOOLEAN: + g_assert_cmpint(pt->value.boolean, ==, pt->value.boolean); + break; + case PTYPE_NUMBER: + g_assert_cmpfloat(pt->value.number, ==, pt_copy->value.number); + break; + case PTYPE_INTEGER: + g_assert_cmpint(pt->value.integer, ==, pt_copy->value.integer); + break; + case PTYPE_U8: + g_assert_cmpuint(pt->value.u8, ==, pt_copy->value.u8); + break; + case PTYPE_U16: + g_assert_cmpuint(pt->value.u16, ==, pt_copy->value.u16); + break; + case PTYPE_U32: + g_assert_cmpuint(pt->value.u32, ==, pt_copy->value.u32); + break; + case PTYPE_U64: + g_assert_cmpuint(pt->value.u64, ==, pt_copy->value.u64); + break; + case PTYPE_S8: + g_assert_cmpint(pt->value.s8, ==, pt_copy->value.s8); + break; + case PTYPE_S16: + g_assert_cmpint(pt->value.s16, ==, pt_copy->value.s16); + break; + case PTYPE_S32: + g_assert_cmpint(pt->value.s32, ==, pt_copy->value.s32); + break; + case PTYPE_S64: + g_assert_cmpint(pt->value.s64, ==, pt_copy->value.s64); + break; + case PTYPE_EOL: + g_assert_not_reached(); + } + + ops->cleanup(serialize_data); + g_free(args); + g_free(pt_copy); +} + +static void test_primitive_lists(gconstpointer opaque) +{ + TestArgs *args = (TestArgs *) opaque; + const SerializeOps *ops = args->ops; + PrimitiveType *pt = args->test_data; + PrimitiveList pl = { .value = { NULL } }; + PrimitiveList pl_copy = { .value = { NULL } }; + PrimitiveList *pl_copy_ptr = &pl_copy; + void *serialize_data; + void *cur_head = NULL; + int i; + + pl.type = pl_copy.type = pt->type; + + /* build up our list of primitive types */ + for (i = 0; i < 32; i++) { + switch (pl.type) { + case PTYPE_STRING: { + QAPI_LIST_PREPEND(pl.value.strings, g_strdup(pt->value.string)); + break; + } + case PTYPE_INTEGER: { + QAPI_LIST_PREPEND(pl.value.integers, pt->value.integer); + break; + } + case PTYPE_S8: { + QAPI_LIST_PREPEND(pl.value.s8_integers, pt->value.s8); + break; + } + case PTYPE_S16: { + QAPI_LIST_PREPEND(pl.value.s16_integers, pt->value.s16); + break; + } + case PTYPE_S32: { + QAPI_LIST_PREPEND(pl.value.s32_integers, pt->value.s32); + break; + } + case PTYPE_S64: { + QAPI_LIST_PREPEND(pl.value.s64_integers, pt->value.s64); + break; + } + case PTYPE_U8: { + QAPI_LIST_PREPEND(pl.value.u8_integers, pt->value.u8); + break; + } + case PTYPE_U16: { + QAPI_LIST_PREPEND(pl.value.u16_integers, pt->value.u16); + break; + } + case PTYPE_U32: { + QAPI_LIST_PREPEND(pl.value.u32_integers, pt->value.u32); + break; + } + case PTYPE_U64: { + QAPI_LIST_PREPEND(pl.value.u64_integers, pt->value.u64); + break; + } + case PTYPE_NUMBER: { + QAPI_LIST_PREPEND(pl.value.numbers, pt->value.number); + break; + } + case PTYPE_BOOLEAN: { + QAPI_LIST_PREPEND(pl.value.booleans, pt->value.boolean); + break; + } + default: + g_assert_not_reached(); + } + } + + ops->serialize((void **)&pl, &serialize_data, visit_primitive_list, + &error_abort); + ops->deserialize((void **)&pl_copy_ptr, serialize_data, + visit_primitive_list, &error_abort); + + i = 0; + + /* compare our deserialized list of primitives to the original */ + do { + switch (pl_copy.type) { + case PTYPE_STRING: { + strList *ptr; + if (cur_head) { + ptr = cur_head; + cur_head = ptr->next; + } else { + cur_head = ptr = pl_copy.value.strings; + } + g_assert_cmpstr(pt->value.string, ==, ptr->value); + break; + } + case PTYPE_INTEGER: { + intList *ptr; + if (cur_head) { + ptr = cur_head; + cur_head = ptr->next; + } else { + cur_head = ptr = pl_copy.value.integers; + } + g_assert_cmpint(pt->value.integer, ==, ptr->value); + break; + } + case PTYPE_S8: { + int8List *ptr; + if (cur_head) { + ptr = cur_head; + cur_head = ptr->next; + } else { + cur_head = ptr = pl_copy.value.s8_integers; + } + g_assert_cmpint(pt->value.s8, ==, ptr->value); + break; + } + case PTYPE_S16: { + int16List *ptr; + if (cur_head) { + ptr = cur_head; + cur_head = ptr->next; + } else { + cur_head = ptr = pl_copy.value.s16_integers; + } + g_assert_cmpint(pt->value.s16, ==, ptr->value); + break; + } + case PTYPE_S32: { + int32List *ptr; + if (cur_head) { + ptr = cur_head; + cur_head = ptr->next; + } else { + cur_head = ptr = pl_copy.value.s32_integers; + } + g_assert_cmpint(pt->value.s32, ==, ptr->value); + break; + } + case PTYPE_S64: { + int64List *ptr; + if (cur_head) { + ptr = cur_head; + cur_head = ptr->next; + } else { + cur_head = ptr = pl_copy.value.s64_integers; + } + g_assert_cmpint(pt->value.s64, ==, ptr->value); + break; + } + case PTYPE_U8: { + uint8List *ptr; + if (cur_head) { + ptr = cur_head; + cur_head = ptr->next; + } else { + cur_head = ptr = pl_copy.value.u8_integers; + } + g_assert_cmpint(pt->value.u8, ==, ptr->value); + break; + } + case PTYPE_U16: { + uint16List *ptr; + if (cur_head) { + ptr = cur_head; + cur_head = ptr->next; + } else { + cur_head = ptr = pl_copy.value.u16_integers; + } + g_assert_cmpint(pt->value.u16, ==, ptr->value); + break; + } + case PTYPE_U32: { + uint32List *ptr; + if (cur_head) { + ptr = cur_head; + cur_head = ptr->next; + } else { + cur_head = ptr = pl_copy.value.u32_integers; + } + g_assert_cmpint(pt->value.u32, ==, ptr->value); + break; + } + case PTYPE_U64: { + uint64List *ptr; + if (cur_head) { + ptr = cur_head; + cur_head = ptr->next; + } else { + cur_head = ptr = pl_copy.value.u64_integers; + } + g_assert_cmpint(pt->value.u64, ==, ptr->value); + break; + } + case PTYPE_NUMBER: { + numberList *ptr; + GString *double_expected = g_string_new(""); + GString *double_actual = g_string_new(""); + if (cur_head) { + ptr = cur_head; + cur_head = ptr->next; + } else { + cur_head = ptr = pl_copy.value.numbers; + } + /* we serialize with %f for our reference visitors, so rather than + * fuzzy floating math to test "equality", just compare the + * formatted values + */ + g_string_printf(double_expected, "%.6f", pt->value.number); + g_string_printf(double_actual, "%.6f", ptr->value); + g_assert_cmpstr(double_actual->str, ==, double_expected->str); + g_string_free(double_expected, true); + g_string_free(double_actual, true); + break; + } + case PTYPE_BOOLEAN: { + boolList *ptr; + if (cur_head) { + ptr = cur_head; + cur_head = ptr->next; + } else { + cur_head = ptr = pl_copy.value.booleans; + } + g_assert_cmpint(!!pt->value.boolean, ==, !!ptr->value); + break; + } + default: + g_assert_not_reached(); + } + i++; + } while (cur_head); + + g_assert_cmpint(i, ==, 33); + + ops->cleanup(serialize_data); + dealloc_helper(&pl, visit_primitive_list, &error_abort); + dealloc_helper(&pl_copy, visit_primitive_list, &error_abort); + g_free(args); +} + +static void test_struct(gconstpointer opaque) +{ + TestArgs *args = (TestArgs *) opaque; + const SerializeOps *ops = args->ops; + TestStruct *ts = struct_create(); + TestStruct *ts_copy = NULL; + void *serialize_data; + + ops->serialize(ts, &serialize_data, visit_struct, &error_abort); + ops->deserialize((void **)&ts_copy, serialize_data, visit_struct, + &error_abort); + + struct_compare(ts, ts_copy); + + struct_cleanup(ts); + struct_cleanup(ts_copy); + + ops->cleanup(serialize_data); + g_free(args); +} + +static void test_nested_struct(gconstpointer opaque) +{ + TestArgs *args = (TestArgs *) opaque; + const SerializeOps *ops = args->ops; + UserDefTwo *udnp = nested_struct_create(); + UserDefTwo *udnp_copy = NULL; + void *serialize_data; + + ops->serialize(udnp, &serialize_data, visit_nested_struct, &error_abort); + ops->deserialize((void **)&udnp_copy, serialize_data, visit_nested_struct, + &error_abort); + + nested_struct_compare(udnp, udnp_copy); + + nested_struct_cleanup(udnp); + nested_struct_cleanup(udnp_copy); + + ops->cleanup(serialize_data); + g_free(args); +} + +static void test_nested_struct_list(gconstpointer opaque) +{ + TestArgs *args = (TestArgs *) opaque; + const SerializeOps *ops = args->ops; + UserDefTwoList *listp = NULL, *tmp, *tmp_copy, *listp_copy = NULL; + void *serialize_data; + int i = 0; + + for (i = 0; i < 8; i++) { + QAPI_LIST_PREPEND(listp, nested_struct_create()); + } + + ops->serialize(listp, &serialize_data, visit_nested_struct_list, + &error_abort); + ops->deserialize((void **)&listp_copy, serialize_data, + visit_nested_struct_list, &error_abort); + + tmp = listp; + tmp_copy = listp_copy; + while (listp_copy) { + g_assert(listp); + nested_struct_compare(listp->value, listp_copy->value); + listp = listp->next; + listp_copy = listp_copy->next; + } + + qapi_free_UserDefTwoList(tmp); + qapi_free_UserDefTwoList(tmp_copy); + + ops->cleanup(serialize_data); + g_free(args); +} + +static PrimitiveType pt_values[] = { + /* string tests */ + { + .description = "string_empty", + .type = PTYPE_STRING, + .value.string = "", + }, + { + .description = "string_whitespace", + .type = PTYPE_STRING, + .value.string = "a b c\td", + }, + { + .description = "string_newlines", + .type = PTYPE_STRING, + .value.string = "a\nb\n", + }, + { + .description = "string_commas", + .type = PTYPE_STRING, + .value.string = "a,b, c,d", + }, + { + .description = "string_single_quoted", + .type = PTYPE_STRING, + .value.string = "'a b',cd", + }, + { + .description = "string_double_quoted", + .type = PTYPE_STRING, + .value.string = "\"a b\",cd", + }, + /* boolean tests */ + { + .description = "boolean_true1", + .type = PTYPE_BOOLEAN, + .value.boolean = true, + }, + { + .description = "boolean_true2", + .type = PTYPE_BOOLEAN, + .value.boolean = 8, + }, + { + .description = "boolean_true3", + .type = PTYPE_BOOLEAN, + .value.boolean = -1, + }, + { + .description = "boolean_false1", + .type = PTYPE_BOOLEAN, + .value.boolean = false, + }, + { + .description = "boolean_false2", + .type = PTYPE_BOOLEAN, + .value.boolean = 0, + }, + /* number tests (double) */ + { + .description = "number_sanity1", + .type = PTYPE_NUMBER, + .value.number = -1, + }, + { + .description = "number_sanity2", + .type = PTYPE_NUMBER, + .value.number = 3.141593, + }, + { + .description = "number_min", + .type = PTYPE_NUMBER, + .value.number = DBL_MIN, + }, + { + .description = "number_max", + .type = PTYPE_NUMBER, + .value.number = DBL_MAX, + }, + /* integer tests (int64) */ + { + .description = "integer_sanity1", + .type = PTYPE_INTEGER, + .value.integer = -1, + }, + { + .description = "integer_sanity2", + .type = PTYPE_INTEGER, + .value.integer = INT64_MAX / 2 + 1, + }, + { + .description = "integer_min", + .type = PTYPE_INTEGER, + .value.integer = INT64_MIN, + }, + { + .description = "integer_max", + .type = PTYPE_INTEGER, + .value.integer = INT64_MAX, + }, + /* uint8 tests */ + { + .description = "uint8_sanity1", + .type = PTYPE_U8, + .value.u8 = 1, + }, + { + .description = "uint8_sanity2", + .type = PTYPE_U8, + .value.u8 = UINT8_MAX / 2 + 1, + }, + { + .description = "uint8_min", + .type = PTYPE_U8, + .value.u8 = 0, + }, + { + .description = "uint8_max", + .type = PTYPE_U8, + .value.u8 = UINT8_MAX, + }, + /* uint16 tests */ + { + .description = "uint16_sanity1", + .type = PTYPE_U16, + .value.u16 = 1, + }, + { + .description = "uint16_sanity2", + .type = PTYPE_U16, + .value.u16 = UINT16_MAX / 2 + 1, + }, + { + .description = "uint16_min", + .type = PTYPE_U16, + .value.u16 = 0, + }, + { + .description = "uint16_max", + .type = PTYPE_U16, + .value.u16 = UINT16_MAX, + }, + /* uint32 tests */ + { + .description = "uint32_sanity1", + .type = PTYPE_U32, + .value.u32 = 1, + }, + { + .description = "uint32_sanity2", + .type = PTYPE_U32, + .value.u32 = UINT32_MAX / 2 + 1, + }, + { + .description = "uint32_min", + .type = PTYPE_U32, + .value.u32 = 0, + }, + { + .description = "uint32_max", + .type = PTYPE_U32, + .value.u32 = UINT32_MAX, + }, + /* uint64 tests */ + { + .description = "uint64_sanity1", + .type = PTYPE_U64, + .value.u64 = 1, + }, + { + .description = "uint64_sanity2", + .type = PTYPE_U64, + .value.u64 = UINT64_MAX / 2 + 1, + }, + { + .description = "uint64_min", + .type = PTYPE_U64, + .value.u64 = 0, + }, + { + .description = "uint64_max", + .type = PTYPE_U64, + .value.u64 = UINT64_MAX, + }, + /* int8 tests */ + { + .description = "int8_sanity1", + .type = PTYPE_S8, + .value.s8 = -1, + }, + { + .description = "int8_sanity2", + .type = PTYPE_S8, + .value.s8 = INT8_MAX / 2 + 1, + }, + { + .description = "int8_min", + .type = PTYPE_S8, + .value.s8 = INT8_MIN, + }, + { + .description = "int8_max", + .type = PTYPE_S8, + .value.s8 = INT8_MAX, + }, + /* int16 tests */ + { + .description = "int16_sanity1", + .type = PTYPE_S16, + .value.s16 = -1, + }, + { + .description = "int16_sanity2", + .type = PTYPE_S16, + .value.s16 = INT16_MAX / 2 + 1, + }, + { + .description = "int16_min", + .type = PTYPE_S16, + .value.s16 = INT16_MIN, + }, + { + .description = "int16_max", + .type = PTYPE_S16, + .value.s16 = INT16_MAX, + }, + /* int32 tests */ + { + .description = "int32_sanity1", + .type = PTYPE_S32, + .value.s32 = -1, + }, + { + .description = "int32_sanity2", + .type = PTYPE_S32, + .value.s32 = INT32_MAX / 2 + 1, + }, + { + .description = "int32_min", + .type = PTYPE_S32, + .value.s32 = INT32_MIN, + }, + { + .description = "int32_max", + .type = PTYPE_S32, + .value.s32 = INT32_MAX, + }, + /* int64 tests */ + { + .description = "int64_sanity1", + .type = PTYPE_S64, + .value.s64 = -1, + }, + { + .description = "int64_sanity2", + .type = PTYPE_S64, + .value.s64 = INT64_MAX / 2 + 1, + }, + { + .description = "int64_min", + .type = PTYPE_S64, + .value.s64 = INT64_MIN, + }, + { + .description = "int64_max", + .type = PTYPE_S64, + .value.s64 = INT64_MAX, + }, + { .type = PTYPE_EOL } +}; + +/* visitor-specific op implementations */ + +typedef struct QmpSerializeData { + Visitor *qov; + QObject *obj; + Visitor *qiv; +} QmpSerializeData; + +static void qmp_serialize(void *native_in, void **datap, + VisitorFunc visit, Error **errp) +{ + QmpSerializeData *d = g_malloc0(sizeof(*d)); + + d->qov = qobject_output_visitor_new(&d->obj); + visit(d->qov, &native_in, errp); + *datap = d; +} + +static void qmp_deserialize(void **native_out, void *datap, + VisitorFunc visit, Error **errp) +{ + QmpSerializeData *d = datap; + GString *output_json; + QObject *obj_orig, *obj; + + visit_complete(d->qov, &d->obj); + obj_orig = d->obj; + output_json = qobject_to_json(obj_orig); + obj = qobject_from_json(output_json->str, &error_abort); + + g_string_free(output_json, true); + d->qiv = qobject_input_visitor_new(obj); + qobject_unref(obj_orig); + qobject_unref(obj); + visit(d->qiv, native_out, errp); +} + +static void qmp_cleanup(void *datap) +{ + QmpSerializeData *d = datap; + visit_free(d->qov); + visit_free(d->qiv); + + g_free(d); +} + +typedef struct StringSerializeData { + char *string; + Visitor *sov; + Visitor *siv; +} StringSerializeData; + +static void string_serialize(void *native_in, void **datap, + VisitorFunc visit, Error **errp) +{ + StringSerializeData *d = g_malloc0(sizeof(*d)); + + d->sov = string_output_visitor_new(false, &d->string); + visit(d->sov, &native_in, errp); + *datap = d; +} + +static void string_deserialize(void **native_out, void *datap, + VisitorFunc visit, Error **errp) +{ + StringSerializeData *d = datap; + + visit_complete(d->sov, &d->string); + d->siv = string_input_visitor_new(d->string); + visit(d->siv, native_out, errp); +} + +static void string_cleanup(void *datap) +{ + StringSerializeData *d = datap; + + visit_free(d->sov); + visit_free(d->siv); + g_free(d->string); + g_free(d); +} + +/* visitor registration, test harness */ + +/* note: to function interchangeably as a serialization mechanism your + * visitor test implementation should pass the test cases for all visitor + * capabilities: primitives, structures, and lists + */ +static const SerializeOps visitors[] = { + { + .type = "QMP", + .serialize = qmp_serialize, + .deserialize = qmp_deserialize, + .cleanup = qmp_cleanup, + .caps = VCAP_PRIMITIVES | VCAP_STRUCTURES | VCAP_LISTS | + VCAP_PRIMITIVE_LISTS + }, + { + .type = "String", + .serialize = string_serialize, + .deserialize = string_deserialize, + .cleanup = string_cleanup, + .caps = VCAP_PRIMITIVES + }, + { NULL } +}; + +static void add_visitor_type(const SerializeOps *ops) +{ + char testname_prefix[32]; + char testname[128]; + TestArgs *args; + int i = 0; + + sprintf(testname_prefix, "/visitor/serialization/%s", ops->type); + + if (ops->caps & VCAP_PRIMITIVES) { + while (pt_values[i].type != PTYPE_EOL) { + sprintf(testname, "%s/primitives/%s", testname_prefix, + pt_values[i].description); + args = g_malloc0(sizeof(*args)); + args->ops = ops; + args->test_data = &pt_values[i]; + g_test_add_data_func(testname, args, test_primitives); + i++; + } + } + + if (ops->caps & VCAP_STRUCTURES) { + sprintf(testname, "%s/struct", testname_prefix); + args = g_malloc0(sizeof(*args)); + args->ops = ops; + args->test_data = NULL; + g_test_add_data_func(testname, args, test_struct); + + sprintf(testname, "%s/nested_struct", testname_prefix); + args = g_malloc0(sizeof(*args)); + args->ops = ops; + args->test_data = NULL; + g_test_add_data_func(testname, args, test_nested_struct); + } + + if (ops->caps & VCAP_LISTS) { + sprintf(testname, "%s/nested_struct_list", testname_prefix); + args = g_malloc0(sizeof(*args)); + args->ops = ops; + args->test_data = NULL; + g_test_add_data_func(testname, args, test_nested_struct_list); + } + + if (ops->caps & VCAP_PRIMITIVE_LISTS) { + i = 0; + while (pt_values[i].type != PTYPE_EOL) { + sprintf(testname, "%s/primitive_list/%s", testname_prefix, + pt_values[i].description); + args = g_malloc0(sizeof(*args)); + args->ops = ops; + args->test_data = &pt_values[i]; + g_test_add_data_func(testname, args, test_primitive_lists); + i++; + } + } +} + +int main(int argc, char **argv) +{ + int i = 0; + + g_test_init(&argc, &argv, NULL); + + while (visitors[i].type != NULL) { + add_visitor_type(&visitors[i]); + i++; + } + + g_test_run(); + + return 0; +} diff --git a/tests/unit/test-vmstate.c b/tests/unit/test-vmstate.c new file mode 100644 index 0000000000..a001879585 --- /dev/null +++ b/tests/unit/test-vmstate.c @@ -0,0 +1,1529 @@ +/* + * Test code for VMState + * + * Copyright (c) 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" + +#include "../migration/migration.h" +#include "migration/vmstate.h" +#include "migration/qemu-file-types.h" +#include "../migration/qemu-file.h" +#include "../migration/qemu-file-channel.h" +#include "../migration/savevm.h" +#include "qemu/coroutine.h" +#include "qemu/module.h" +#include "io/channel-file.h" + +static int temp_fd; + + +/* Duplicate temp_fd and seek to the beginning of the file */ +static QEMUFile *open_test_file(bool write) +{ + int fd = dup(temp_fd); + QIOChannel *ioc; + QEMUFile *f; + + lseek(fd, 0, SEEK_SET); + if (write) { + g_assert_cmpint(ftruncate(fd, 0), ==, 0); + } + ioc = QIO_CHANNEL(qio_channel_file_new_fd(fd)); + if (write) { + f = qemu_fopen_channel_output(ioc); + } else { + f = qemu_fopen_channel_input(ioc); + } + object_unref(OBJECT(ioc)); + return f; +} + +#define SUCCESS(val) \ + g_assert_cmpint((val), ==, 0) + +#define FAILURE(val) \ + g_assert_cmpint((val), !=, 0) + +static void save_vmstate(const VMStateDescription *desc, void *obj) +{ + QEMUFile *f = open_test_file(true); + + /* Save file with vmstate */ + int ret = vmstate_save_state(f, desc, obj, NULL); + g_assert(!ret); + qemu_put_byte(f, QEMU_VM_EOF); + g_assert(!qemu_file_get_error(f)); + qemu_fclose(f); +} + +static void save_buffer(const uint8_t *buf, size_t buf_size) +{ + QEMUFile *fsave = open_test_file(true); + qemu_put_buffer(fsave, buf, buf_size); + qemu_fclose(fsave); +} + +static void compare_vmstate(const uint8_t *wire, size_t size) +{ + QEMUFile *f = open_test_file(false); + uint8_t result[size]; + + /* read back as binary */ + + g_assert_cmpint(qemu_get_buffer(f, result, sizeof(result)), ==, + sizeof(result)); + g_assert(!qemu_file_get_error(f)); + + /* Compare that what is on the file is the same that what we + expected to be there */ + SUCCESS(memcmp(result, wire, sizeof(result))); + + /* Must reach EOF */ + qemu_get_byte(f); + g_assert_cmpint(qemu_file_get_error(f), ==, -EIO); + + qemu_fclose(f); +} + +static int load_vmstate_one(const VMStateDescription *desc, void *obj, + int version, const uint8_t *wire, size_t size) +{ + QEMUFile *f; + int ret; + + f = open_test_file(true); + qemu_put_buffer(f, wire, size); + qemu_fclose(f); + + f = open_test_file(false); + ret = vmstate_load_state(f, desc, obj, version); + if (ret) { + g_assert(qemu_file_get_error(f)); + } else{ + g_assert(!qemu_file_get_error(f)); + } + qemu_fclose(f); + return ret; +} + + +static int load_vmstate(const VMStateDescription *desc, + void *obj, void *obj_clone, + void (*obj_copy)(void *, void*), + int version, const uint8_t *wire, size_t size) +{ + /* We test with zero size */ + obj_copy(obj_clone, obj); + FAILURE(load_vmstate_one(desc, obj, version, wire, 0)); + + /* Stream ends with QEMU_EOF, so we need at least 3 bytes to be + * able to test in the middle */ + + if (size > 3) { + + /* We test with size - 2. We can't test size - 1 due to EOF tricks */ + obj_copy(obj, obj_clone); + FAILURE(load_vmstate_one(desc, obj, version, wire, size - 2)); + + /* Test with size/2, first half of real state */ + obj_copy(obj, obj_clone); + FAILURE(load_vmstate_one(desc, obj, version, wire, size/2)); + + /* Test with size/2, second half of real state */ + obj_copy(obj, obj_clone); + FAILURE(load_vmstate_one(desc, obj, version, wire + (size/2), size/2)); + + } + obj_copy(obj, obj_clone); + return load_vmstate_one(desc, obj, version, wire, size); +} + +/* Test struct that we are going to use for our tests */ + +typedef struct TestSimple { + bool b_1, b_2; + uint8_t u8_1; + uint16_t u16_1; + uint32_t u32_1; + uint64_t u64_1; + int8_t i8_1, i8_2; + int16_t i16_1, i16_2; + int32_t i32_1, i32_2; + int64_t i64_1, i64_2; +} TestSimple; + +/* Object instantiation, we are going to use it in more than one test */ + +TestSimple obj_simple = { + .b_1 = true, + .b_2 = false, + .u8_1 = 130, + .u16_1 = 512, + .u32_1 = 70000, + .u64_1 = 12121212, + .i8_1 = 65, + .i8_2 = -65, + .i16_1 = 512, + .i16_2 = -512, + .i32_1 = 70000, + .i32_2 = -70000, + .i64_1 = 12121212, + .i64_2 = -12121212, +}; + +/* Description of the values. If you add a primitive type + you are expected to add a test here */ + +static const VMStateDescription vmstate_simple_primitive = { + .name = "simple/primitive", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_BOOL(b_1, TestSimple), + VMSTATE_BOOL(b_2, TestSimple), + VMSTATE_UINT8(u8_1, TestSimple), + VMSTATE_UINT16(u16_1, TestSimple), + VMSTATE_UINT32(u32_1, TestSimple), + VMSTATE_UINT64(u64_1, TestSimple), + VMSTATE_INT8(i8_1, TestSimple), + VMSTATE_INT8(i8_2, TestSimple), + VMSTATE_INT16(i16_1, TestSimple), + VMSTATE_INT16(i16_2, TestSimple), + VMSTATE_INT32(i32_1, TestSimple), + VMSTATE_INT32(i32_2, TestSimple), + VMSTATE_INT64(i64_1, TestSimple), + VMSTATE_INT64(i64_2, TestSimple), + VMSTATE_END_OF_LIST() + } +}; + +/* It describes what goes through the wire. Our tests are basically: + + * save test + - save a struct a vmstate to a file + - read that file back (binary read, no vmstate) + - compare it with what we expect to be on the wire + * load test + - save to the file what we expect to be on the wire + - read struct back with vmstate in a different + - compare back with the original struct +*/ + +uint8_t wire_simple_primitive[] = { + /* b_1 */ 0x01, + /* b_2 */ 0x00, + /* u8_1 */ 0x82, + /* u16_1 */ 0x02, 0x00, + /* u32_1 */ 0x00, 0x01, 0x11, 0x70, + /* u64_1 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, 0xf4, 0x7c, + /* i8_1 */ 0x41, + /* i8_2 */ 0xbf, + /* i16_1 */ 0x02, 0x00, + /* i16_2 */ 0xfe, 0x0, + /* i32_1 */ 0x00, 0x01, 0x11, 0x70, + /* i32_2 */ 0xff, 0xfe, 0xee, 0x90, + /* i64_1 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, 0xf4, 0x7c, + /* i64_2 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0x47, 0x0b, 0x84, + QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */ +}; + +static void obj_simple_copy(void *target, void *source) +{ + memcpy(target, source, sizeof(TestSimple)); +} + +static void test_simple_primitive(void) +{ + TestSimple obj, obj_clone; + + memset(&obj, 0, sizeof(obj)); + save_vmstate(&vmstate_simple_primitive, &obj_simple); + + compare_vmstate(wire_simple_primitive, sizeof(wire_simple_primitive)); + + SUCCESS(load_vmstate(&vmstate_simple_primitive, &obj, &obj_clone, + obj_simple_copy, 1, wire_simple_primitive, + sizeof(wire_simple_primitive))); + +#define FIELD_EQUAL(name) g_assert_cmpint(obj.name, ==, obj_simple.name) + + FIELD_EQUAL(b_1); + FIELD_EQUAL(b_2); + FIELD_EQUAL(u8_1); + FIELD_EQUAL(u16_1); + FIELD_EQUAL(u32_1); + FIELD_EQUAL(u64_1); + FIELD_EQUAL(i8_1); + FIELD_EQUAL(i8_2); + FIELD_EQUAL(i16_1); + FIELD_EQUAL(i16_2); + FIELD_EQUAL(i32_1); + FIELD_EQUAL(i32_2); + FIELD_EQUAL(i64_1); + FIELD_EQUAL(i64_2); +} + +typedef struct TestSimpleArray { + uint16_t u16_1[3]; +} TestSimpleArray; + +/* Object instantiation, we are going to use it in more than one test */ + +TestSimpleArray obj_simple_arr = { + .u16_1 = { 0x42, 0x43, 0x44 }, +}; + +/* Description of the values. If you add a primitive type + you are expected to add a test here */ + +static const VMStateDescription vmstate_simple_arr = { + .name = "simple/array", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT16_ARRAY(u16_1, TestSimpleArray, 3), + VMSTATE_END_OF_LIST() + } +}; + +uint8_t wire_simple_arr[] = { + /* u16_1 */ 0x00, 0x42, + /* u16_1 */ 0x00, 0x43, + /* u16_1 */ 0x00, 0x44, + QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */ +}; + +static void obj_simple_arr_copy(void *target, void *source) +{ + memcpy(target, source, sizeof(TestSimpleArray)); +} + +static void test_simple_array(void) +{ + TestSimpleArray obj, obj_clone; + + memset(&obj, 0, sizeof(obj)); + save_vmstate(&vmstate_simple_arr, &obj_simple_arr); + + compare_vmstate(wire_simple_arr, sizeof(wire_simple_arr)); + + SUCCESS(load_vmstate(&vmstate_simple_arr, &obj, &obj_clone, + obj_simple_arr_copy, 1, wire_simple_arr, + sizeof(wire_simple_arr))); +} + +typedef struct TestStruct { + uint32_t a, b, c, e; + uint64_t d, f; + bool skip_c_e; +} TestStruct; + +static const VMStateDescription vmstate_versioned = { + .name = "test/versioned", + .version_id = 2, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(a, TestStruct), + VMSTATE_UINT32_V(b, TestStruct, 2), /* Versioned field in the middle, so + * we catch bugs more easily. + */ + VMSTATE_UINT32(c, TestStruct), + VMSTATE_UINT64(d, TestStruct), + VMSTATE_UINT32_V(e, TestStruct, 2), + VMSTATE_UINT64_V(f, TestStruct, 2), + VMSTATE_END_OF_LIST() + } +}; + +static void test_load_v1(void) +{ + uint8_t buf[] = { + 0, 0, 0, 10, /* a */ + 0, 0, 0, 30, /* c */ + 0, 0, 0, 0, 0, 0, 0, 40, /* d */ + QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */ + }; + save_buffer(buf, sizeof(buf)); + + QEMUFile *loading = open_test_file(false); + TestStruct obj = { .b = 200, .e = 500, .f = 600 }; + vmstate_load_state(loading, &vmstate_versioned, &obj, 1); + g_assert(!qemu_file_get_error(loading)); + g_assert_cmpint(obj.a, ==, 10); + g_assert_cmpint(obj.b, ==, 200); + g_assert_cmpint(obj.c, ==, 30); + g_assert_cmpint(obj.d, ==, 40); + g_assert_cmpint(obj.e, ==, 500); + g_assert_cmpint(obj.f, ==, 600); + qemu_fclose(loading); +} + +static void test_load_v2(void) +{ + uint8_t buf[] = { + 0, 0, 0, 10, /* a */ + 0, 0, 0, 20, /* b */ + 0, 0, 0, 30, /* c */ + 0, 0, 0, 0, 0, 0, 0, 40, /* d */ + 0, 0, 0, 50, /* e */ + 0, 0, 0, 0, 0, 0, 0, 60, /* f */ + QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */ + }; + save_buffer(buf, sizeof(buf)); + + QEMUFile *loading = open_test_file(false); + TestStruct obj; + vmstate_load_state(loading, &vmstate_versioned, &obj, 2); + g_assert_cmpint(obj.a, ==, 10); + g_assert_cmpint(obj.b, ==, 20); + g_assert_cmpint(obj.c, ==, 30); + g_assert_cmpint(obj.d, ==, 40); + g_assert_cmpint(obj.e, ==, 50); + g_assert_cmpint(obj.f, ==, 60); + qemu_fclose(loading); +} + +static bool test_skip(void *opaque, int version_id) +{ + TestStruct *t = (TestStruct *)opaque; + return !t->skip_c_e; +} + +static const VMStateDescription vmstate_skipping = { + .name = "test/skip", + .version_id = 2, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(a, TestStruct), + VMSTATE_UINT32(b, TestStruct), + VMSTATE_UINT32_TEST(c, TestStruct, test_skip), + VMSTATE_UINT64(d, TestStruct), + VMSTATE_UINT32_TEST(e, TestStruct, test_skip), + VMSTATE_UINT64_V(f, TestStruct, 2), + VMSTATE_END_OF_LIST() + } +}; + + +static void test_save_noskip(void) +{ + QEMUFile *fsave = open_test_file(true); + TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6, + .skip_c_e = false }; + int ret = vmstate_save_state(fsave, &vmstate_skipping, &obj, NULL); + g_assert(!ret); + g_assert(!qemu_file_get_error(fsave)); + + uint8_t expected[] = { + 0, 0, 0, 1, /* a */ + 0, 0, 0, 2, /* b */ + 0, 0, 0, 3, /* c */ + 0, 0, 0, 0, 0, 0, 0, 4, /* d */ + 0, 0, 0, 5, /* e */ + 0, 0, 0, 0, 0, 0, 0, 6, /* f */ + }; + + qemu_fclose(fsave); + compare_vmstate(expected, sizeof(expected)); +} + +static void test_save_skip(void) +{ + QEMUFile *fsave = open_test_file(true); + TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6, + .skip_c_e = true }; + int ret = vmstate_save_state(fsave, &vmstate_skipping, &obj, NULL); + g_assert(!ret); + g_assert(!qemu_file_get_error(fsave)); + + uint8_t expected[] = { + 0, 0, 0, 1, /* a */ + 0, 0, 0, 2, /* b */ + 0, 0, 0, 0, 0, 0, 0, 4, /* d */ + 0, 0, 0, 0, 0, 0, 0, 6, /* f */ + }; + + qemu_fclose(fsave); + compare_vmstate(expected, sizeof(expected)); +} + +static void test_load_noskip(void) +{ + uint8_t buf[] = { + 0, 0, 0, 10, /* a */ + 0, 0, 0, 20, /* b */ + 0, 0, 0, 30, /* c */ + 0, 0, 0, 0, 0, 0, 0, 40, /* d */ + 0, 0, 0, 50, /* e */ + 0, 0, 0, 0, 0, 0, 0, 60, /* f */ + QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */ + }; + save_buffer(buf, sizeof(buf)); + + QEMUFile *loading = open_test_file(false); + TestStruct obj = { .skip_c_e = false }; + vmstate_load_state(loading, &vmstate_skipping, &obj, 2); + g_assert(!qemu_file_get_error(loading)); + g_assert_cmpint(obj.a, ==, 10); + g_assert_cmpint(obj.b, ==, 20); + g_assert_cmpint(obj.c, ==, 30); + g_assert_cmpint(obj.d, ==, 40); + g_assert_cmpint(obj.e, ==, 50); + g_assert_cmpint(obj.f, ==, 60); + qemu_fclose(loading); +} + +static void test_load_skip(void) +{ + uint8_t buf[] = { + 0, 0, 0, 10, /* a */ + 0, 0, 0, 20, /* b */ + 0, 0, 0, 0, 0, 0, 0, 40, /* d */ + 0, 0, 0, 0, 0, 0, 0, 60, /* f */ + QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */ + }; + save_buffer(buf, sizeof(buf)); + + QEMUFile *loading = open_test_file(false); + TestStruct obj = { .skip_c_e = true, .c = 300, .e = 500 }; + vmstate_load_state(loading, &vmstate_skipping, &obj, 2); + g_assert(!qemu_file_get_error(loading)); + g_assert_cmpint(obj.a, ==, 10); + g_assert_cmpint(obj.b, ==, 20); + g_assert_cmpint(obj.c, ==, 300); + g_assert_cmpint(obj.d, ==, 40); + g_assert_cmpint(obj.e, ==, 500); + g_assert_cmpint(obj.f, ==, 60); + qemu_fclose(loading); +} + +typedef struct { + int32_t i; +} TestStructTriv; + +const VMStateDescription vmsd_tst = { + .name = "test/tst", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_INT32(i, TestStructTriv), + VMSTATE_END_OF_LIST() + } +}; + +/* test array migration */ + +#define AR_SIZE 4 + +typedef struct { + TestStructTriv *ar[AR_SIZE]; +} TestArrayOfPtrToStuct; + +const VMStateDescription vmsd_arps = { + .name = "test/arps", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_ARRAY_OF_POINTER_TO_STRUCT(ar, TestArrayOfPtrToStuct, + AR_SIZE, 0, vmsd_tst, TestStructTriv), + VMSTATE_END_OF_LIST() + } +}; + +static uint8_t wire_arr_ptr_no0[] = { + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x03, + QEMU_VM_EOF +}; + +static void test_arr_ptr_str_no0_save(void) +{ + TestStructTriv ar[AR_SIZE] = {{.i = 0}, {.i = 1}, {.i = 2}, {.i = 3} }; + TestArrayOfPtrToStuct sample = {.ar = {&ar[0], &ar[1], &ar[2], &ar[3]} }; + + save_vmstate(&vmsd_arps, &sample); + compare_vmstate(wire_arr_ptr_no0, sizeof(wire_arr_ptr_no0)); +} + +static void test_arr_ptr_str_no0_load(void) +{ + TestStructTriv ar_gt[AR_SIZE] = {{.i = 0}, {.i = 1}, {.i = 2}, {.i = 3} }; + TestStructTriv ar[AR_SIZE] = {}; + TestArrayOfPtrToStuct obj = {.ar = {&ar[0], &ar[1], &ar[2], &ar[3]} }; + int idx; + + save_buffer(wire_arr_ptr_no0, sizeof(wire_arr_ptr_no0)); + SUCCESS(load_vmstate_one(&vmsd_arps, &obj, 1, + wire_arr_ptr_no0, sizeof(wire_arr_ptr_no0))); + for (idx = 0; idx < AR_SIZE; ++idx) { + /* compare the target array ar with the ground truth array ar_gt */ + g_assert_cmpint(ar_gt[idx].i, ==, ar[idx].i); + } +} + +static uint8_t wire_arr_ptr_0[] = { + 0x00, 0x00, 0x00, 0x00, + VMS_NULLPTR_MARKER, + 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x03, + QEMU_VM_EOF +}; + +static void test_arr_ptr_str_0_save(void) +{ + TestStructTriv ar[AR_SIZE] = {{.i = 0}, {.i = 1}, {.i = 2}, {.i = 3} }; + TestArrayOfPtrToStuct sample = {.ar = {&ar[0], NULL, &ar[2], &ar[3]} }; + + save_vmstate(&vmsd_arps, &sample); + compare_vmstate(wire_arr_ptr_0, sizeof(wire_arr_ptr_0)); +} + +static void test_arr_ptr_str_0_load(void) +{ + TestStructTriv ar_gt[AR_SIZE] = {{.i = 0}, {.i = 0}, {.i = 2}, {.i = 3} }; + TestStructTriv ar[AR_SIZE] = {}; + TestArrayOfPtrToStuct obj = {.ar = {&ar[0], NULL, &ar[2], &ar[3]} }; + int idx; + + save_buffer(wire_arr_ptr_0, sizeof(wire_arr_ptr_0)); + SUCCESS(load_vmstate_one(&vmsd_arps, &obj, 1, + wire_arr_ptr_0, sizeof(wire_arr_ptr_0))); + for (idx = 0; idx < AR_SIZE; ++idx) { + /* compare the target array ar with the ground truth array ar_gt */ + g_assert_cmpint(ar_gt[idx].i, ==, ar[idx].i); + } + for (idx = 0; idx < AR_SIZE; ++idx) { + if (idx == 1) { + g_assert_cmpint((uintptr_t)(obj.ar[idx]), ==, 0); + } else { + g_assert_cmpint((uintptr_t)(obj.ar[idx]), !=, 0); + } + } +} + +typedef struct TestArrayOfPtrToInt { + int32_t *ar[AR_SIZE]; +} TestArrayOfPtrToInt; + +const VMStateDescription vmsd_arpp = { + .name = "test/arps", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_ARRAY_OF_POINTER(ar, TestArrayOfPtrToInt, + AR_SIZE, 0, vmstate_info_int32, int32_t*), + VMSTATE_END_OF_LIST() + } +}; + +static void test_arr_ptr_prim_0_save(void) +{ + int32_t ar[AR_SIZE] = {0 , 1, 2, 3}; + TestArrayOfPtrToInt sample = {.ar = {&ar[0], NULL, &ar[2], &ar[3]} }; + + save_vmstate(&vmsd_arpp, &sample); + compare_vmstate(wire_arr_ptr_0, sizeof(wire_arr_ptr_0)); +} + +static void test_arr_ptr_prim_0_load(void) +{ + int32_t ar_gt[AR_SIZE] = {0, 1, 2, 3}; + int32_t ar[AR_SIZE] = {3 , 42, 1, 0}; + TestArrayOfPtrToInt obj = {.ar = {&ar[0], NULL, &ar[2], &ar[3]} }; + int idx; + + save_buffer(wire_arr_ptr_0, sizeof(wire_arr_ptr_0)); + SUCCESS(load_vmstate_one(&vmsd_arpp, &obj, 1, + wire_arr_ptr_0, sizeof(wire_arr_ptr_0))); + for (idx = 0; idx < AR_SIZE; ++idx) { + /* compare the target array ar with the ground truth array ar_gt */ + if (idx == 1) { + g_assert_cmpint(42, ==, ar[idx]); + } else { + g_assert_cmpint(ar_gt[idx], ==, ar[idx]); + } + } +} + +/* test QTAILQ migration */ +typedef struct TestQtailqElement TestQtailqElement; + +struct TestQtailqElement { + bool b; + uint8_t u8; + QTAILQ_ENTRY(TestQtailqElement) next; +}; + +typedef struct TestQtailq { + int16_t i16; + QTAILQ_HEAD(, TestQtailqElement) q; + int32_t i32; +} TestQtailq; + +static const VMStateDescription vmstate_q_element = { + .name = "test/queue-element", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_BOOL(b, TestQtailqElement), + VMSTATE_UINT8(u8, TestQtailqElement), + VMSTATE_END_OF_LIST() + }, +}; + +static const VMStateDescription vmstate_q = { + .name = "test/queue", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_INT16(i16, TestQtailq), + VMSTATE_QTAILQ_V(q, TestQtailq, 1, vmstate_q_element, TestQtailqElement, + next), + VMSTATE_INT32(i32, TestQtailq), + VMSTATE_END_OF_LIST() + } +}; + +uint8_t wire_q[] = { + /* i16 */ 0xfe, 0x0, + /* start of element 0 of q */ 0x01, + /* .b */ 0x01, + /* .u8 */ 0x82, + /* start of element 1 of q */ 0x01, + /* b */ 0x00, + /* u8 */ 0x41, + /* end of q */ 0x00, + /* i32 */ 0x00, 0x01, 0x11, 0x70, + QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */ +}; + +static void test_save_q(void) +{ + TestQtailq obj_q = { + .i16 = -512, + .i32 = 70000, + }; + + TestQtailqElement obj_qe1 = { + .b = true, + .u8 = 130, + }; + + TestQtailqElement obj_qe2 = { + .b = false, + .u8 = 65, + }; + + QTAILQ_INIT(&obj_q.q); + QTAILQ_INSERT_TAIL(&obj_q.q, &obj_qe1, next); + QTAILQ_INSERT_TAIL(&obj_q.q, &obj_qe2, next); + + save_vmstate(&vmstate_q, &obj_q); + compare_vmstate(wire_q, sizeof(wire_q)); +} + +static void test_load_q(void) +{ + TestQtailq obj_q = { + .i16 = -512, + .i32 = 70000, + }; + + TestQtailqElement obj_qe1 = { + .b = true, + .u8 = 130, + }; + + TestQtailqElement obj_qe2 = { + .b = false, + .u8 = 65, + }; + + QTAILQ_INIT(&obj_q.q); + QTAILQ_INSERT_TAIL(&obj_q.q, &obj_qe1, next); + QTAILQ_INSERT_TAIL(&obj_q.q, &obj_qe2, next); + + QEMUFile *fsave = open_test_file(true); + + qemu_put_buffer(fsave, wire_q, sizeof(wire_q)); + g_assert(!qemu_file_get_error(fsave)); + qemu_fclose(fsave); + + QEMUFile *fload = open_test_file(false); + TestQtailq tgt; + + QTAILQ_INIT(&tgt.q); + vmstate_load_state(fload, &vmstate_q, &tgt, 1); + char eof = qemu_get_byte(fload); + g_assert(!qemu_file_get_error(fload)); + g_assert_cmpint(tgt.i16, ==, obj_q.i16); + g_assert_cmpint(tgt.i32, ==, obj_q.i32); + g_assert_cmpint(eof, ==, QEMU_VM_EOF); + + TestQtailqElement *qele_from = QTAILQ_FIRST(&obj_q.q); + TestQtailqElement *qlast_from = QTAILQ_LAST(&obj_q.q); + TestQtailqElement *qele_to = QTAILQ_FIRST(&tgt.q); + TestQtailqElement *qlast_to = QTAILQ_LAST(&tgt.q); + + while (1) { + g_assert_cmpint(qele_to->b, ==, qele_from->b); + g_assert_cmpint(qele_to->u8, ==, qele_from->u8); + if ((qele_from == qlast_from) || (qele_to == qlast_to)) { + break; + } + qele_from = QTAILQ_NEXT(qele_from, next); + qele_to = QTAILQ_NEXT(qele_to, next); + } + + g_assert_cmpint((uintptr_t) qele_from, ==, (uintptr_t) qlast_from); + g_assert_cmpint((uintptr_t) qele_to, ==, (uintptr_t) qlast_to); + + /* clean up */ + TestQtailqElement *qele; + while (!QTAILQ_EMPTY(&tgt.q)) { + qele = QTAILQ_LAST(&tgt.q); + QTAILQ_REMOVE(&tgt.q, qele, next); + free(qele); + qele = NULL; + } + qemu_fclose(fload); +} + +/* interval (key) */ +typedef struct TestGTreeInterval { + uint64_t low; + uint64_t high; +} TestGTreeInterval; + +#define VMSTATE_INTERVAL \ +{ \ + .name = "interval", \ + .version_id = 1, \ + .minimum_version_id = 1, \ + .fields = (VMStateField[]) { \ + VMSTATE_UINT64(low, TestGTreeInterval), \ + VMSTATE_UINT64(high, TestGTreeInterval), \ + VMSTATE_END_OF_LIST() \ + } \ +} + +/* mapping (value) */ +typedef struct TestGTreeMapping { + uint64_t phys_addr; + uint32_t flags; +} TestGTreeMapping; + +#define VMSTATE_MAPPING \ +{ \ + .name = "mapping", \ + .version_id = 1, \ + .minimum_version_id = 1, \ + .fields = (VMStateField[]) { \ + VMSTATE_UINT64(phys_addr, TestGTreeMapping), \ + VMSTATE_UINT32(flags, TestGTreeMapping), \ + VMSTATE_END_OF_LIST() \ + }, \ +} + +static const VMStateDescription vmstate_interval_mapping[2] = { + VMSTATE_MAPPING, /* value */ + VMSTATE_INTERVAL /* key */ +}; + +typedef struct TestGTreeDomain { + int32_t id; + GTree *mappings; +} TestGTreeDomain; + +typedef struct TestGTreeIOMMU { + int32_t id; + GTree *domains; +} TestGTreeIOMMU; + +/* Interval comparison function */ +static gint interval_cmp(gconstpointer a, gconstpointer b, gpointer user_data) +{ + TestGTreeInterval *inta = (TestGTreeInterval *)a; + TestGTreeInterval *intb = (TestGTreeInterval *)b; + + if (inta->high < intb->low) { + return -1; + } else if (intb->high < inta->low) { + return 1; + } else { + return 0; + } +} + +/* ID comparison function */ +static gint int_cmp(gconstpointer a, gconstpointer b, gpointer user_data) +{ + guint ua = GPOINTER_TO_UINT(a); + guint ub = GPOINTER_TO_UINT(b); + return (ua > ub) - (ua < ub); +} + +static void destroy_domain(gpointer data) +{ + TestGTreeDomain *domain = (TestGTreeDomain *)data; + + g_tree_destroy(domain->mappings); + g_free(domain); +} + +static int domain_preload(void *opaque) +{ + TestGTreeDomain *domain = opaque; + + domain->mappings = g_tree_new_full((GCompareDataFunc)interval_cmp, + NULL, g_free, g_free); + return 0; +} + +static int iommu_preload(void *opaque) +{ + TestGTreeIOMMU *iommu = opaque; + + iommu->domains = g_tree_new_full((GCompareDataFunc)int_cmp, + NULL, NULL, destroy_domain); + return 0; +} + +static const VMStateDescription vmstate_domain = { + .name = "domain", + .version_id = 1, + .minimum_version_id = 1, + .pre_load = domain_preload, + .fields = (VMStateField[]) { + VMSTATE_INT32(id, TestGTreeDomain), + VMSTATE_GTREE_V(mappings, TestGTreeDomain, 1, + vmstate_interval_mapping, + TestGTreeInterval, TestGTreeMapping), + VMSTATE_END_OF_LIST() + } +}; + +/* test QLIST Migration */ + +typedef struct TestQListElement { + uint32_t id; + QLIST_ENTRY(TestQListElement) next; +} TestQListElement; + +typedef struct TestQListContainer { + uint32_t id; + QLIST_HEAD(, TestQListElement) list; +} TestQListContainer; + +static const VMStateDescription vmstate_qlist_element = { + .name = "test/queue list", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(id, TestQListElement), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_iommu = { + .name = "iommu", + .version_id = 1, + .minimum_version_id = 1, + .pre_load = iommu_preload, + .fields = (VMStateField[]) { + VMSTATE_INT32(id, TestGTreeIOMMU), + VMSTATE_GTREE_DIRECT_KEY_V(domains, TestGTreeIOMMU, 1, + &vmstate_domain, TestGTreeDomain), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_container = { + .name = "test/container/qlist", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(id, TestQListContainer), + VMSTATE_QLIST_V(list, TestQListContainer, 1, vmstate_qlist_element, + TestQListElement, next), + VMSTATE_END_OF_LIST() + } +}; + +uint8_t first_domain_dump[] = { + /* id */ + 0x00, 0x0, 0x0, 0x6, + 0x00, 0x0, 0x0, 0x2, /* 2 mappings */ + 0x1, /* start of a */ + /* a */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, + /* map_a */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x00, + 0x00, 0x00, 0x00, 0x01, + 0x1, /* start of b */ + /* b */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0xFF, + /* map_b */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, + 0x0, /* end of gtree */ + QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */ +}; + +static TestGTreeDomain *create_first_domain(void) +{ + TestGTreeDomain *domain; + TestGTreeMapping *map_a, *map_b; + TestGTreeInterval *a, *b; + + domain = g_malloc0(sizeof(TestGTreeDomain)); + domain->id = 6; + + a = g_malloc0(sizeof(TestGTreeInterval)); + a->low = 0x1000; + a->high = 0x1FFF; + + b = g_malloc0(sizeof(TestGTreeInterval)); + b->low = 0x4000; + b->high = 0x4FFF; + + map_a = g_malloc0(sizeof(TestGTreeMapping)); + map_a->phys_addr = 0xa000; + map_a->flags = 1; + + map_b = g_malloc0(sizeof(TestGTreeMapping)); + map_b->phys_addr = 0xe0000; + map_b->flags = 2; + + domain->mappings = g_tree_new_full((GCompareDataFunc)interval_cmp, NULL, + (GDestroyNotify)g_free, + (GDestroyNotify)g_free); + g_tree_insert(domain->mappings, a, map_a); + g_tree_insert(domain->mappings, b, map_b); + return domain; +} + +static void test_gtree_save_domain(void) +{ + TestGTreeDomain *first_domain = create_first_domain(); + + save_vmstate(&vmstate_domain, first_domain); + compare_vmstate(first_domain_dump, sizeof(first_domain_dump)); + destroy_domain(first_domain); +} + +struct match_node_data { + GTree *tree; + gpointer key; + gpointer value; +}; + +struct tree_cmp_data { + GTree *tree1; + GTree *tree2; + GTraverseFunc match_node; +}; + +static gboolean match_interval_mapping_node(gpointer key, + gpointer value, gpointer data) +{ + TestGTreeMapping *map_a, *map_b; + TestGTreeInterval *a, *b; + struct match_node_data *d = (struct match_node_data *)data; + a = (TestGTreeInterval *)key; + b = (TestGTreeInterval *)d->key; + + map_a = (TestGTreeMapping *)value; + map_b = (TestGTreeMapping *)d->value; + + assert(a->low == b->low); + assert(a->high == b->high); + assert(map_a->phys_addr == map_b->phys_addr); + assert(map_a->flags == map_b->flags); + g_tree_remove(d->tree, key); + return true; +} + +static gboolean diff_tree(gpointer key, gpointer value, gpointer data) +{ + struct tree_cmp_data *tp = (struct tree_cmp_data *)data; + struct match_node_data d = {tp->tree2, key, value}; + + g_tree_foreach(tp->tree2, tp->match_node, &d); + g_tree_remove(tp->tree1, key); + return false; +} + +static void compare_trees(GTree *tree1, GTree *tree2, + GTraverseFunc function) +{ + struct tree_cmp_data tp = {tree1, tree2, function}; + + g_tree_foreach(tree1, diff_tree, &tp); + assert(g_tree_nnodes(tree1) == 0); + assert(g_tree_nnodes(tree2) == 0); +} + +static void diff_domain(TestGTreeDomain *d1, TestGTreeDomain *d2) +{ + assert(d1->id == d2->id); + compare_trees(d1->mappings, d2->mappings, match_interval_mapping_node); +} + +static gboolean match_domain_node(gpointer key, gpointer value, gpointer data) +{ + uint64_t id1, id2; + TestGTreeDomain *d1, *d2; + struct match_node_data *d = (struct match_node_data *)data; + + id1 = (uint64_t)(uintptr_t)key; + id2 = (uint64_t)(uintptr_t)d->key; + d1 = (TestGTreeDomain *)value; + d2 = (TestGTreeDomain *)d->value; + assert(id1 == id2); + diff_domain(d1, d2); + g_tree_remove(d->tree, key); + return true; +} + +static void diff_iommu(TestGTreeIOMMU *iommu1, TestGTreeIOMMU *iommu2) +{ + assert(iommu1->id == iommu2->id); + compare_trees(iommu1->domains, iommu2->domains, match_domain_node); +} + +static void test_gtree_load_domain(void) +{ + TestGTreeDomain *dest_domain = g_malloc0(sizeof(TestGTreeDomain)); + TestGTreeDomain *orig_domain = create_first_domain(); + QEMUFile *fload, *fsave; + char eof; + + fsave = open_test_file(true); + qemu_put_buffer(fsave, first_domain_dump, sizeof(first_domain_dump)); + g_assert(!qemu_file_get_error(fsave)); + qemu_fclose(fsave); + + fload = open_test_file(false); + + vmstate_load_state(fload, &vmstate_domain, dest_domain, 1); + eof = qemu_get_byte(fload); + g_assert(!qemu_file_get_error(fload)); + g_assert_cmpint(orig_domain->id, ==, dest_domain->id); + g_assert_cmpint(eof, ==, QEMU_VM_EOF); + + diff_domain(orig_domain, dest_domain); + destroy_domain(orig_domain); + destroy_domain(dest_domain); + qemu_fclose(fload); +} + +uint8_t iommu_dump[] = { + /* iommu id */ + 0x00, 0x0, 0x0, 0x7, + 0x00, 0x0, 0x0, 0x2, /* 2 domains */ + 0x1,/* start of domain 5 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0, 0x0, 0x5, /* key = 5 */ + 0x00, 0x0, 0x0, 0x5, /* domain1 id */ + 0x00, 0x0, 0x0, 0x1, /* 1 mapping */ + 0x1, /* start of mappings */ + /* c */ + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, + /* map_c */ + 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, + 0x00, 0x0, 0x0, 0x3, + 0x0, /* end of domain1 mappings*/ + 0x1,/* start of domain 6 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0, 0x0, 0x6, /* key = 6 */ + 0x00, 0x0, 0x0, 0x6, /* domain6 id */ + 0x00, 0x0, 0x0, 0x2, /* 2 mappings */ + 0x1, /* start of a */ + /* a */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, + /* map_a */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x00, + 0x00, 0x00, 0x00, 0x01, + 0x1, /* start of b */ + /* b */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0xFF, + /* map_b */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, + 0x0, /* end of domain6 mappings*/ + 0x0, /* end of domains */ + QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */ +}; + +static TestGTreeIOMMU *create_iommu(void) +{ + TestGTreeIOMMU *iommu = g_malloc0(sizeof(TestGTreeIOMMU)); + TestGTreeDomain *first_domain = create_first_domain(); + TestGTreeDomain *second_domain; + TestGTreeMapping *map_c; + TestGTreeInterval *c; + + iommu->id = 7; + iommu->domains = g_tree_new_full((GCompareDataFunc)int_cmp, NULL, + NULL, + destroy_domain); + + second_domain = g_malloc0(sizeof(TestGTreeDomain)); + second_domain->id = 5; + second_domain->mappings = g_tree_new_full((GCompareDataFunc)interval_cmp, + NULL, + (GDestroyNotify)g_free, + (GDestroyNotify)g_free); + + g_tree_insert(iommu->domains, GUINT_TO_POINTER(6), first_domain); + g_tree_insert(iommu->domains, (gpointer)0x0000000000000005, second_domain); + + c = g_malloc0(sizeof(TestGTreeInterval)); + c->low = 0x1000000; + c->high = 0x1FFFFFF; + + map_c = g_malloc0(sizeof(TestGTreeMapping)); + map_c->phys_addr = 0xF000000; + map_c->flags = 0x3; + + g_tree_insert(second_domain->mappings, c, map_c); + return iommu; +} + +static void destroy_iommu(TestGTreeIOMMU *iommu) +{ + g_tree_destroy(iommu->domains); + g_free(iommu); +} + +static void test_gtree_save_iommu(void) +{ + TestGTreeIOMMU *iommu = create_iommu(); + + save_vmstate(&vmstate_iommu, iommu); + compare_vmstate(iommu_dump, sizeof(iommu_dump)); + destroy_iommu(iommu); +} + +static void test_gtree_load_iommu(void) +{ + TestGTreeIOMMU *dest_iommu = g_malloc0(sizeof(TestGTreeIOMMU)); + TestGTreeIOMMU *orig_iommu = create_iommu(); + QEMUFile *fsave, *fload; + char eof; + + fsave = open_test_file(true); + qemu_put_buffer(fsave, iommu_dump, sizeof(iommu_dump)); + g_assert(!qemu_file_get_error(fsave)); + qemu_fclose(fsave); + + fload = open_test_file(false); + vmstate_load_state(fload, &vmstate_iommu, dest_iommu, 1); + eof = qemu_get_byte(fload); + g_assert(!qemu_file_get_error(fload)); + g_assert_cmpint(orig_iommu->id, ==, dest_iommu->id); + g_assert_cmpint(eof, ==, QEMU_VM_EOF); + + diff_iommu(orig_iommu, dest_iommu); + destroy_iommu(orig_iommu); + destroy_iommu(dest_iommu); + qemu_fclose(fload); +} + +static uint8_t qlist_dump[] = { + 0x00, 0x00, 0x00, 0x01, /* container id */ + 0x1, /* start of a */ + 0x00, 0x00, 0x00, 0x0a, + 0x1, /* start of b */ + 0x00, 0x00, 0x0b, 0x00, + 0x1, /* start of c */ + 0x00, 0x0c, 0x00, 0x00, + 0x1, /* start of d */ + 0x0d, 0x00, 0x00, 0x00, + 0x0, /* end of list */ + QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */ +}; + +static TestQListContainer *alloc_container(void) +{ + TestQListElement *a = g_malloc(sizeof(TestQListElement)); + TestQListElement *b = g_malloc(sizeof(TestQListElement)); + TestQListElement *c = g_malloc(sizeof(TestQListElement)); + TestQListElement *d = g_malloc(sizeof(TestQListElement)); + TestQListContainer *container = g_malloc(sizeof(TestQListContainer)); + + a->id = 0x0a; + b->id = 0x0b00; + c->id = 0xc0000; + d->id = 0xd000000; + container->id = 1; + + QLIST_INIT(&container->list); + QLIST_INSERT_HEAD(&container->list, d, next); + QLIST_INSERT_HEAD(&container->list, c, next); + QLIST_INSERT_HEAD(&container->list, b, next); + QLIST_INSERT_HEAD(&container->list, a, next); + return container; +} + +static void free_container(TestQListContainer *container) +{ + TestQListElement *iter, *tmp; + + QLIST_FOREACH_SAFE(iter, &container->list, next, tmp) { + QLIST_REMOVE(iter, next); + g_free(iter); + } + g_free(container); +} + +static void compare_containers(TestQListContainer *c1, TestQListContainer *c2) +{ + TestQListElement *first_item_c1, *first_item_c2; + + while (!QLIST_EMPTY(&c1->list)) { + first_item_c1 = QLIST_FIRST(&c1->list); + first_item_c2 = QLIST_FIRST(&c2->list); + assert(first_item_c2); + assert(first_item_c1->id == first_item_c2->id); + QLIST_REMOVE(first_item_c1, next); + QLIST_REMOVE(first_item_c2, next); + g_free(first_item_c1); + g_free(first_item_c2); + } + assert(QLIST_EMPTY(&c2->list)); +} + +/* + * Check the prev & next fields are correct by doing list + * manipulations on the container. We will do that for both + * the source and the destination containers + */ +static void manipulate_container(TestQListContainer *c) +{ + TestQListElement *prev = NULL, *iter = QLIST_FIRST(&c->list); + TestQListElement *elem; + + elem = g_malloc(sizeof(TestQListElement)); + elem->id = 0x12; + QLIST_INSERT_AFTER(iter, elem, next); + + elem = g_malloc(sizeof(TestQListElement)); + elem->id = 0x13; + QLIST_INSERT_HEAD(&c->list, elem, next); + + while (iter) { + prev = iter; + iter = QLIST_NEXT(iter, next); + } + + elem = g_malloc(sizeof(TestQListElement)); + elem->id = 0x14; + QLIST_INSERT_BEFORE(prev, elem, next); + + elem = g_malloc(sizeof(TestQListElement)); + elem->id = 0x15; + QLIST_INSERT_AFTER(prev, elem, next); + + QLIST_REMOVE(prev, next); + g_free(prev); +} + +static void test_save_qlist(void) +{ + TestQListContainer *container = alloc_container(); + + save_vmstate(&vmstate_container, container); + compare_vmstate(qlist_dump, sizeof(qlist_dump)); + free_container(container); +} + +static void test_load_qlist(void) +{ + QEMUFile *fsave, *fload; + TestQListContainer *orig_container = alloc_container(); + TestQListContainer *dest_container = g_malloc0(sizeof(TestQListContainer)); + char eof; + + QLIST_INIT(&dest_container->list); + + fsave = open_test_file(true); + qemu_put_buffer(fsave, qlist_dump, sizeof(qlist_dump)); + g_assert(!qemu_file_get_error(fsave)); + qemu_fclose(fsave); + + fload = open_test_file(false); + vmstate_load_state(fload, &vmstate_container, dest_container, 1); + eof = qemu_get_byte(fload); + g_assert(!qemu_file_get_error(fload)); + g_assert_cmpint(eof, ==, QEMU_VM_EOF); + manipulate_container(orig_container); + manipulate_container(dest_container); + compare_containers(orig_container, dest_container); + free_container(orig_container); + free_container(dest_container); + qemu_fclose(fload); +} + +typedef struct TmpTestStruct { + TestStruct *parent; + int64_t diff; +} TmpTestStruct; + +static int tmp_child_pre_save(void *opaque) +{ + struct TmpTestStruct *tts = opaque; + + tts->diff = tts->parent->b - tts->parent->a; + + return 0; +} + +static int tmp_child_post_load(void *opaque, int version_id) +{ + struct TmpTestStruct *tts = opaque; + + tts->parent->b = tts->parent->a + tts->diff; + + return 0; +} + +static const VMStateDescription vmstate_tmp_back_to_parent = { + .name = "test/tmp_child_parent", + .fields = (VMStateField[]) { + VMSTATE_UINT64(f, TestStruct), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_tmp_child = { + .name = "test/tmp_child", + .pre_save = tmp_child_pre_save, + .post_load = tmp_child_post_load, + .fields = (VMStateField[]) { + VMSTATE_INT64(diff, TmpTestStruct), + VMSTATE_STRUCT_POINTER(parent, TmpTestStruct, + vmstate_tmp_back_to_parent, TestStruct), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_with_tmp = { + .name = "test/with_tmp", + .version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(a, TestStruct), + VMSTATE_UINT64(d, TestStruct), + VMSTATE_WITH_TMP(TestStruct, TmpTestStruct, vmstate_tmp_child), + VMSTATE_END_OF_LIST() + } +}; + +static void obj_tmp_copy(void *target, void *source) +{ + memcpy(target, source, sizeof(TestStruct)); +} + +static void test_tmp_struct(void) +{ + TestStruct obj, obj_clone; + + uint8_t const wire_with_tmp[] = { + /* u32 a */ 0x00, 0x00, 0x00, 0x02, + /* u64 d */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + /* diff */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + /* u64 f */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, + QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */ + }; + + memset(&obj, 0, sizeof(obj)); + obj.a = 2; + obj.b = 4; + obj.d = 1; + obj.f = 8; + save_vmstate(&vmstate_with_tmp, &obj); + + compare_vmstate(wire_with_tmp, sizeof(wire_with_tmp)); + + memset(&obj, 0, sizeof(obj)); + SUCCESS(load_vmstate(&vmstate_with_tmp, &obj, &obj_clone, + obj_tmp_copy, 1, wire_with_tmp, + sizeof(wire_with_tmp))); + g_assert_cmpint(obj.a, ==, 2); /* From top level vmsd */ + g_assert_cmpint(obj.b, ==, 4); /* from the post_load */ + g_assert_cmpint(obj.d, ==, 1); /* From top level vmsd */ + g_assert_cmpint(obj.f, ==, 8); /* From the child->parent */ +} + +int main(int argc, char **argv) +{ + g_autofree char *temp_file = g_strdup_printf("%s/vmst.test.XXXXXX", + g_get_tmp_dir()); + temp_fd = mkstemp(temp_file); + + module_call_init(MODULE_INIT_QOM); + + g_setenv("QTEST_SILENT_ERRORS", "1", 1); + + g_test_init(&argc, &argv, NULL); + g_test_add_func("/vmstate/simple/primitive", test_simple_primitive); + g_test_add_func("/vmstate/simple/array", test_simple_array); + g_test_add_func("/vmstate/versioned/load/v1", test_load_v1); + g_test_add_func("/vmstate/versioned/load/v2", test_load_v2); + g_test_add_func("/vmstate/field_exists/load/noskip", test_load_noskip); + g_test_add_func("/vmstate/field_exists/load/skip", test_load_skip); + g_test_add_func("/vmstate/field_exists/save/noskip", test_save_noskip); + g_test_add_func("/vmstate/field_exists/save/skip", test_save_skip); + g_test_add_func("/vmstate/array/ptr/str/no0/save", + test_arr_ptr_str_no0_save); + g_test_add_func("/vmstate/array/ptr/str/no0/load", + test_arr_ptr_str_no0_load); + g_test_add_func("/vmstate/array/ptr/str/0/save", test_arr_ptr_str_0_save); + g_test_add_func("/vmstate/array/ptr/str/0/load", + test_arr_ptr_str_0_load); + g_test_add_func("/vmstate/array/ptr/prim/0/save", + test_arr_ptr_prim_0_save); + g_test_add_func("/vmstate/array/ptr/prim/0/load", + test_arr_ptr_prim_0_load); + g_test_add_func("/vmstate/qtailq/save/saveq", test_save_q); + g_test_add_func("/vmstate/qtailq/load/loadq", test_load_q); + g_test_add_func("/vmstate/gtree/save/savedomain", test_gtree_save_domain); + g_test_add_func("/vmstate/gtree/load/loaddomain", test_gtree_load_domain); + g_test_add_func("/vmstate/gtree/save/saveiommu", test_gtree_save_iommu); + g_test_add_func("/vmstate/gtree/load/loadiommu", test_gtree_load_iommu); + g_test_add_func("/vmstate/qlist/save/saveqlist", test_save_qlist); + g_test_add_func("/vmstate/qlist/load/loadqlist", test_load_qlist); + g_test_add_func("/vmstate/tmp_struct", test_tmp_struct); + g_test_run(); + + close(temp_fd); + unlink(temp_file); + + return 0; +} diff --git a/tests/unit/test-write-threshold.c b/tests/unit/test-write-threshold.c new file mode 100644 index 0000000000..fc1c45a2eb --- /dev/null +++ b/tests/unit/test-write-threshold.c @@ -0,0 +1,123 @@ +/* + * Test block device write threshold + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "block/block_int.h" +#include "block/write-threshold.h" + + +static void test_threshold_not_set_on_init(void) +{ + uint64_t res; + BlockDriverState bs; + memset(&bs, 0, sizeof(bs)); + + g_assert(!bdrv_write_threshold_is_set(&bs)); + + res = bdrv_write_threshold_get(&bs); + g_assert_cmpint(res, ==, 0); +} + +static void test_threshold_set_get(void) +{ + uint64_t threshold = 4 * 1024 * 1024; + uint64_t res; + BlockDriverState bs; + memset(&bs, 0, sizeof(bs)); + + bdrv_write_threshold_set(&bs, threshold); + + g_assert(bdrv_write_threshold_is_set(&bs)); + + res = bdrv_write_threshold_get(&bs); + g_assert_cmpint(res, ==, threshold); +} + +static void test_threshold_multi_set_get(void) +{ + uint64_t threshold1 = 4 * 1024 * 1024; + uint64_t threshold2 = 15 * 1024 * 1024; + uint64_t res; + BlockDriverState bs; + memset(&bs, 0, sizeof(bs)); + + bdrv_write_threshold_set(&bs, threshold1); + bdrv_write_threshold_set(&bs, threshold2); + res = bdrv_write_threshold_get(&bs); + g_assert_cmpint(res, ==, threshold2); +} + +static void test_threshold_not_trigger(void) +{ + uint64_t amount = 0; + uint64_t threshold = 4 * 1024 * 1024; + BlockDriverState bs; + BdrvTrackedRequest req; + + memset(&bs, 0, sizeof(bs)); + memset(&req, 0, sizeof(req)); + req.offset = 1024; + req.bytes = 1024; + + bdrv_check_request(req.offset, req.bytes, &error_abort); + + bdrv_write_threshold_set(&bs, threshold); + amount = bdrv_write_threshold_exceeded(&bs, &req); + g_assert_cmpuint(amount, ==, 0); +} + + +static void test_threshold_trigger(void) +{ + uint64_t amount = 0; + uint64_t threshold = 4 * 1024 * 1024; + BlockDriverState bs; + BdrvTrackedRequest req; + + memset(&bs, 0, sizeof(bs)); + memset(&req, 0, sizeof(req)); + req.offset = (4 * 1024 * 1024) - 1024; + req.bytes = 2 * 1024; + + bdrv_check_request(req.offset, req.bytes, &error_abort); + + bdrv_write_threshold_set(&bs, threshold); + amount = bdrv_write_threshold_exceeded(&bs, &req); + g_assert_cmpuint(amount, >=, 1024); +} + +typedef struct TestStruct { + const char *name; + void (*func)(void); +} TestStruct; + + +int main(int argc, char **argv) +{ + size_t i; + TestStruct tests[] = { + { "/write-threshold/not-set-on-init", + test_threshold_not_set_on_init }, + { "/write-threshold/set-get", + test_threshold_set_get }, + { "/write-threshold/multi-set-get", + test_threshold_multi_set_get }, + { "/write-threshold/not-trigger", + test_threshold_not_trigger }, + { "/write-threshold/trigger", + test_threshold_trigger }, + { NULL, NULL } + }; + + g_test_init(&argc, &argv, NULL); + for (i = 0; tests[i].name != NULL; i++) { + g_test_add_func(tests[i].name, tests[i].func); + } + return g_test_run(); +} diff --git a/tests/unit/test-x86-cpuid.c b/tests/unit/test-x86-cpuid.c new file mode 100644 index 0000000000..bfabc0403a --- /dev/null +++ b/tests/unit/test-x86-cpuid.c @@ -0,0 +1,138 @@ +/* + * Test code for x86 CPUID and Topology functions + * + * Copyright (c) 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" + +#include "hw/i386/topology.h" + +static void test_topo_bits(void) +{ + X86CPUTopoInfo topo_info = {0}; + + /* simple tests for 1 thread per core, 1 core per die, 1 die per package */ + topo_info = (X86CPUTopoInfo) {1, 1, 1}; + g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 0); + g_assert_cmpuint(apicid_core_width(&topo_info), ==, 0); + g_assert_cmpuint(apicid_die_width(&topo_info), ==, 0); + + topo_info = (X86CPUTopoInfo) {1, 1, 1}; + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 0), ==, 0); + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 1), ==, 1); + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 2), ==, 2); + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 3), ==, 3); + + + /* Test field width calculation for multiple values + */ + topo_info = (X86CPUTopoInfo) {1, 1, 2}; + g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 1); + topo_info = (X86CPUTopoInfo) {1, 1, 3}; + g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 2); + topo_info = (X86CPUTopoInfo) {1, 1, 4}; + g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 2); + + topo_info = (X86CPUTopoInfo) {1, 1, 14}; + g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 4); + topo_info = (X86CPUTopoInfo) {1, 1, 15}; + g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 4); + topo_info = (X86CPUTopoInfo) {1, 1, 16}; + g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 4); + topo_info = (X86CPUTopoInfo) {1, 1, 17}; + g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 5); + + + topo_info = (X86CPUTopoInfo) {1, 30, 2}; + g_assert_cmpuint(apicid_core_width(&topo_info), ==, 5); + topo_info = (X86CPUTopoInfo) {1, 31, 2}; + g_assert_cmpuint(apicid_core_width(&topo_info), ==, 5); + topo_info = (X86CPUTopoInfo) {1, 32, 2}; + g_assert_cmpuint(apicid_core_width(&topo_info), ==, 5); + topo_info = (X86CPUTopoInfo) {1, 33, 2}; + g_assert_cmpuint(apicid_core_width(&topo_info), ==, 6); + + topo_info = (X86CPUTopoInfo) {1, 30, 2}; + g_assert_cmpuint(apicid_die_width(&topo_info), ==, 0); + topo_info = (X86CPUTopoInfo) {2, 30, 2}; + g_assert_cmpuint(apicid_die_width(&topo_info), ==, 1); + topo_info = (X86CPUTopoInfo) {3, 30, 2}; + g_assert_cmpuint(apicid_die_width(&topo_info), ==, 2); + topo_info = (X86CPUTopoInfo) {4, 30, 2}; + g_assert_cmpuint(apicid_die_width(&topo_info), ==, 2); + + /* build a weird topology and see if IDs are calculated correctly + */ + + /* This will use 2 bits for thread ID and 3 bits for core ID + */ + topo_info = (X86CPUTopoInfo) {1, 6, 3}; + g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 2); + g_assert_cmpuint(apicid_core_offset(&topo_info), ==, 2); + g_assert_cmpuint(apicid_die_offset(&topo_info), ==, 5); + g_assert_cmpuint(apicid_pkg_offset(&topo_info), ==, 5); + + topo_info = (X86CPUTopoInfo) {1, 6, 3}; + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 0), ==, 0); + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 1), ==, 1); + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 2), ==, 2); + + topo_info = (X86CPUTopoInfo) {1, 6, 3}; + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 1 * 3 + 0), ==, + (1 << 2) | 0); + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 1 * 3 + 1), ==, + (1 << 2) | 1); + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 1 * 3 + 2), ==, + (1 << 2) | 2); + + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 2 * 3 + 0), ==, + (2 << 2) | 0); + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 2 * 3 + 1), ==, + (2 << 2) | 1); + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 2 * 3 + 2), ==, + (2 << 2) | 2); + + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 5 * 3 + 0), ==, + (5 << 2) | 0); + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 5 * 3 + 1), ==, + (5 << 2) | 1); + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 5 * 3 + 2), ==, + (5 << 2) | 2); + + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, + 1 * 6 * 3 + 0 * 3 + 0), ==, (1 << 5)); + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, + 1 * 6 * 3 + 1 * 3 + 1), ==, (1 << 5) | (1 << 2) | 1); + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, + 3 * 6 * 3 + 5 * 3 + 2), ==, (3 << 5) | (5 << 2) | 2); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/cpuid/topology/basic", test_topo_bits); + + g_test_run(); + + return 0; +} diff --git a/tests/unit/test-xbzrle.c b/tests/unit/test-xbzrle.c new file mode 100644 index 0000000000..795d6f1cba --- /dev/null +++ b/tests/unit/test-xbzrle.c @@ -0,0 +1,191 @@ +/* + * Xor Based Zero Run Length Encoding unit tests. + * + * Copyright 2013 Red Hat, Inc. and/or its affiliates + * + * Authors: + * Orit Wasserman + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "qemu/cutils.h" +#include "../migration/xbzrle.h" + +#define XBZRLE_PAGE_SIZE 4096 + +static void test_uleb(void) +{ + uint32_t i, val; + uint8_t buf[2]; + int encode_ret, decode_ret; + + for (i = 0; i <= 0x3fff; i++) { + encode_ret = uleb128_encode_small(&buf[0], i); + decode_ret = uleb128_decode_small(&buf[0], &val); + g_assert(encode_ret == decode_ret); + g_assert(i == val); + } + + /* decode invalid value */ + buf[0] = 0x80; + buf[1] = 0x80; + + decode_ret = uleb128_decode_small(&buf[0], &val); + g_assert(decode_ret == -1); + g_assert(val == 0); +} + +static void test_encode_decode_zero(void) +{ + uint8_t *buffer = g_malloc0(XBZRLE_PAGE_SIZE); + uint8_t *compressed = g_malloc0(XBZRLE_PAGE_SIZE); + int i = 0; + int dlen = 0; + int diff_len = g_test_rand_int_range(0, XBZRLE_PAGE_SIZE - 1006); + + for (i = diff_len; i > 0; i--) { + buffer[1000 + i] = i; + } + + buffer[1000 + diff_len + 3] = 103; + buffer[1000 + diff_len + 5] = 105; + + /* encode zero page */ + dlen = xbzrle_encode_buffer(buffer, buffer, XBZRLE_PAGE_SIZE, compressed, + XBZRLE_PAGE_SIZE); + g_assert(dlen == 0); + + g_free(buffer); + g_free(compressed); +} + +static void test_encode_decode_unchanged(void) +{ + uint8_t *compressed = g_malloc0(XBZRLE_PAGE_SIZE); + uint8_t *test = g_malloc0(XBZRLE_PAGE_SIZE); + int i = 0; + int dlen = 0; + int diff_len = g_test_rand_int_range(0, XBZRLE_PAGE_SIZE - 1006); + + for (i = diff_len; i > 0; i--) { + test[1000 + i] = i + 4; + } + + test[1000 + diff_len + 3] = 107; + test[1000 + diff_len + 5] = 109; + + /* test unchanged buffer */ + dlen = xbzrle_encode_buffer(test, test, XBZRLE_PAGE_SIZE, compressed, + XBZRLE_PAGE_SIZE); + g_assert(dlen == 0); + + g_free(test); + g_free(compressed); +} + +static void test_encode_decode_1_byte(void) +{ + uint8_t *buffer = g_malloc0(XBZRLE_PAGE_SIZE); + uint8_t *test = g_malloc0(XBZRLE_PAGE_SIZE); + uint8_t *compressed = g_malloc(XBZRLE_PAGE_SIZE); + int dlen = 0, rc = 0; + uint8_t buf[2]; + + test[XBZRLE_PAGE_SIZE - 1] = 1; + + dlen = xbzrle_encode_buffer(buffer, test, XBZRLE_PAGE_SIZE, compressed, + XBZRLE_PAGE_SIZE); + g_assert(dlen == (uleb128_encode_small(&buf[0], 4095) + 2)); + + rc = xbzrle_decode_buffer(compressed, dlen, buffer, XBZRLE_PAGE_SIZE); + g_assert(rc == XBZRLE_PAGE_SIZE); + g_assert(memcmp(test, buffer, XBZRLE_PAGE_SIZE) == 0); + + g_free(buffer); + g_free(compressed); + g_free(test); +} + +static void test_encode_decode_overflow(void) +{ + uint8_t *compressed = g_malloc0(XBZRLE_PAGE_SIZE); + uint8_t *test = g_malloc0(XBZRLE_PAGE_SIZE); + uint8_t *buffer = g_malloc0(XBZRLE_PAGE_SIZE); + int i = 0, rc = 0; + + for (i = 0; i < XBZRLE_PAGE_SIZE / 2 - 1; i++) { + test[i * 2] = 1; + } + + /* encode overflow */ + rc = xbzrle_encode_buffer(buffer, test, XBZRLE_PAGE_SIZE, compressed, + XBZRLE_PAGE_SIZE); + g_assert(rc == -1); + + g_free(buffer); + g_free(compressed); + g_free(test); +} + +static void encode_decode_range(void) +{ + uint8_t *buffer = g_malloc0(XBZRLE_PAGE_SIZE); + uint8_t *compressed = g_malloc(XBZRLE_PAGE_SIZE); + uint8_t *test = g_malloc0(XBZRLE_PAGE_SIZE); + int i = 0, rc = 0; + int dlen = 0; + + int diff_len = g_test_rand_int_range(0, XBZRLE_PAGE_SIZE - 1006); + + for (i = diff_len; i > 0; i--) { + buffer[1000 + i] = i; + test[1000 + i] = i + 4; + } + + buffer[1000 + diff_len + 3] = 103; + test[1000 + diff_len + 3] = 107; + + buffer[1000 + diff_len + 5] = 105; + test[1000 + diff_len + 5] = 109; + + /* test encode/decode */ + dlen = xbzrle_encode_buffer(test, buffer, XBZRLE_PAGE_SIZE, compressed, + XBZRLE_PAGE_SIZE); + + rc = xbzrle_decode_buffer(compressed, dlen, test, XBZRLE_PAGE_SIZE); + g_assert(rc < XBZRLE_PAGE_SIZE); + g_assert(memcmp(test, buffer, XBZRLE_PAGE_SIZE) == 0); + + g_free(buffer); + g_free(compressed); + g_free(test); +} + +static void test_encode_decode(void) +{ + int i; + + for (i = 0; i < 10000; i++) { + encode_decode_range(); + } +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + g_test_rand_int(); + g_test_add_func("/xbzrle/uleb", test_uleb); + g_test_add_func("/xbzrle/encode_decode_zero", test_encode_decode_zero); + g_test_add_func("/xbzrle/encode_decode_unchanged", + test_encode_decode_unchanged); + g_test_add_func("/xbzrle/encode_decode_1_byte", test_encode_decode_1_byte); + g_test_add_func("/xbzrle/encode_decode_overflow", + test_encode_decode_overflow); + g_test_add_func("/xbzrle/encode_decode", test_encode_decode); + + return g_test_run(); +} -- cgit v1.2.3-55-g7522 From 3b472e71d50fe33f2e0dfdd447dde5910ddf0761 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 12 Mar 2021 10:22:38 +0100 Subject: tests: Move benchmarks into a separate folder Make it clear that these files are related to benchmarks by moving them into a new folder called "bench". Message-Id: <20210312092238.79509-1-thuth@redhat.com> Acked-by: Paolo Bonzini Signed-off-by: Thomas Huth --- MAINTAINERS | 2 +- tests/atomic64-bench.c | 169 ----------- tests/atomic_add-bench.c | 180 ------------ tests/bench/atomic64-bench.c | 169 +++++++++++ tests/bench/atomic_add-bench.c | 180 ++++++++++++ tests/bench/benchmark-crypto-cipher.c | 208 ++++++++++++++ tests/bench/benchmark-crypto-hash.c | 116 ++++++++ tests/bench/benchmark-crypto-hmac.c | 81 ++++++ tests/bench/meson.build | 34 +++ tests/bench/qht-bench.c | 523 ++++++++++++++++++++++++++++++++++ tests/benchmark-crypto-cipher.c | 208 -------------- tests/benchmark-crypto-hash.c | 116 -------- tests/benchmark-crypto-hmac.c | 81 ------ tests/meson.build | 34 +-- tests/qht-bench.c | 523 ---------------------------------- 15 files changed, 1313 insertions(+), 1311 deletions(-) delete mode 100644 tests/atomic64-bench.c delete mode 100644 tests/atomic_add-bench.c create mode 100644 tests/bench/atomic64-bench.c create mode 100644 tests/bench/atomic_add-bench.c create mode 100644 tests/bench/benchmark-crypto-cipher.c create mode 100644 tests/bench/benchmark-crypto-hash.c create mode 100644 tests/bench/benchmark-crypto-hmac.c create mode 100644 tests/bench/meson.build create mode 100644 tests/bench/qht-bench.c delete mode 100644 tests/benchmark-crypto-cipher.c delete mode 100644 tests/benchmark-crypto-hash.c delete mode 100644 tests/benchmark-crypto-hmac.c delete mode 100644 tests/qht-bench.c (limited to 'tests') diff --git a/MAINTAINERS b/MAINTAINERS index e6c43c6833..9a68f09b25 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2722,7 +2722,7 @@ F: crypto/ F: include/crypto/ F: qapi/crypto.json F: tests/unit/test-crypto-* -F: tests/benchmark-crypto-* +F: tests/bench/benchmark-crypto-* F: tests/unit/crypto-tls-* F: tests/unit/pkix_asn1_tab.c F: qemu.sasl diff --git a/tests/atomic64-bench.c b/tests/atomic64-bench.c deleted file mode 100644 index e474753d34..0000000000 --- a/tests/atomic64-bench.c +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright (C) 2018, Emilio G. Cota - * - * License: GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ -#include "qemu/osdep.h" -#include "qemu/thread.h" -#include "qemu/host-utils.h" -#include "qemu/processor.h" - -struct thread_info { - uint64_t r; - uint64_t accesses; -} QEMU_ALIGNED(64); - -struct count { - int64_t i64; -} QEMU_ALIGNED(64); - -static QemuThread *threads; -static struct thread_info *th_info; -static unsigned int n_threads = 1; -static unsigned int n_ready_threads; -static struct count *counts; -static unsigned int duration = 1; -static unsigned int range = 1024; -static bool test_start; -static bool test_stop; - -static const char commands_string[] = - " -d = duration in seconds\n" - " -n = number of threads\n" - " -r = range (will be rounded up to pow2)"; - -static void usage_complete(char *argv[]) -{ - fprintf(stderr, "Usage: %s [options]\n", argv[0]); - fprintf(stderr, "options:\n%s\n", commands_string); -} - -/* - * From: https://en.wikipedia.org/wiki/Xorshift - * This is faster than rand_r(), and gives us a wider range (RAND_MAX is only - * guaranteed to be >= INT_MAX). - */ -static uint64_t xorshift64star(uint64_t x) -{ - x ^= x >> 12; /* a */ - x ^= x << 25; /* b */ - x ^= x >> 27; /* c */ - return x * UINT64_C(2685821657736338717); -} - -static void *thread_func(void *arg) -{ - struct thread_info *info = arg; - - qatomic_inc(&n_ready_threads); - while (!qatomic_read(&test_start)) { - cpu_relax(); - } - - while (!qatomic_read(&test_stop)) { - unsigned int index; - - info->r = xorshift64star(info->r); - index = info->r & (range - 1); - qatomic_read_i64(&counts[index].i64); - info->accesses++; - } - return NULL; -} - -static void run_test(void) -{ - unsigned int i; - - while (qatomic_read(&n_ready_threads) != n_threads) { - cpu_relax(); - } - - qatomic_set(&test_start, true); - g_usleep(duration * G_USEC_PER_SEC); - qatomic_set(&test_stop, true); - - for (i = 0; i < n_threads; i++) { - qemu_thread_join(&threads[i]); - } -} - -static void create_threads(void) -{ - unsigned int i; - - threads = g_new(QemuThread, n_threads); - th_info = g_new(struct thread_info, n_threads); - counts = g_malloc0_n(range, sizeof(*counts)); - - for (i = 0; i < n_threads; i++) { - struct thread_info *info = &th_info[i]; - - info->r = (i + 1) ^ time(NULL); - info->accesses = 0; - qemu_thread_create(&threads[i], NULL, thread_func, info, - QEMU_THREAD_JOINABLE); - } -} - -static void pr_params(void) -{ - printf("Parameters:\n"); - printf(" # of threads: %u\n", n_threads); - printf(" duration: %u\n", duration); - printf(" ops' range: %u\n", range); -} - -static void pr_stats(void) -{ - unsigned long long val = 0; - double tx; - int i; - - for (i = 0; i < n_threads; i++) { - val += th_info[i].accesses; - } - tx = val / duration / 1e6; - - printf("Results:\n"); - printf("Duration: %u s\n", duration); - printf(" Throughput: %.2f Mops/s\n", tx); - printf(" Throughput/thread: %.2f Mops/s/thread\n", tx / n_threads); -} - -static void parse_args(int argc, char *argv[]) -{ - int c; - - for (;;) { - c = getopt(argc, argv, "hd:n:r:"); - if (c < 0) { - break; - } - switch (c) { - case 'h': - usage_complete(argv); - exit(0); - case 'd': - duration = atoi(optarg); - break; - case 'n': - n_threads = atoi(optarg); - break; - case 'r': - range = pow2ceil(atoi(optarg)); - break; - } - } -} - -int main(int argc, char *argv[]) -{ - parse_args(argc, argv); - pr_params(); - create_threads(); - run_test(); - pr_stats(); - return 0; -} diff --git a/tests/atomic_add-bench.c b/tests/atomic_add-bench.c deleted file mode 100644 index f05471ab45..0000000000 --- a/tests/atomic_add-bench.c +++ /dev/null @@ -1,180 +0,0 @@ -#include "qemu/osdep.h" -#include "qemu/thread.h" -#include "qemu/host-utils.h" -#include "qemu/processor.h" - -struct thread_info { - uint64_t r; -} QEMU_ALIGNED(64); - -struct count { - QemuMutex lock; - unsigned long val; -} QEMU_ALIGNED(64); - -static QemuThread *threads; -static struct thread_info *th_info; -static unsigned int n_threads = 1; -static unsigned int n_ready_threads; -static struct count *counts; -static unsigned int duration = 1; -static unsigned int range = 1024; -static bool use_mutex; -static bool test_start; -static bool test_stop; - -static const char commands_string[] = - " -n = number of threads\n" - " -m = use mutexes instead of atomic increments\n" - " -p = enable sync profiler\n" - " -d = duration in seconds\n" - " -r = range (will be rounded up to pow2)"; - -static void usage_complete(char *argv[]) -{ - fprintf(stderr, "Usage: %s [options]\n", argv[0]); - fprintf(stderr, "options:\n%s\n", commands_string); -} - -/* - * From: https://en.wikipedia.org/wiki/Xorshift - * This is faster than rand_r(), and gives us a wider range (RAND_MAX is only - * guaranteed to be >= INT_MAX). - */ -static uint64_t xorshift64star(uint64_t x) -{ - x ^= x >> 12; /* a */ - x ^= x << 25; /* b */ - x ^= x >> 27; /* c */ - return x * UINT64_C(2685821657736338717); -} - -static void *thread_func(void *arg) -{ - struct thread_info *info = arg; - - qatomic_inc(&n_ready_threads); - while (!qatomic_read(&test_start)) { - cpu_relax(); - } - - while (!qatomic_read(&test_stop)) { - unsigned int index; - - info->r = xorshift64star(info->r); - index = info->r & (range - 1); - if (use_mutex) { - qemu_mutex_lock(&counts[index].lock); - counts[index].val += 1; - qemu_mutex_unlock(&counts[index].lock); - } else { - qatomic_inc(&counts[index].val); - } - } - return NULL; -} - -static void run_test(void) -{ - unsigned int i; - - while (qatomic_read(&n_ready_threads) != n_threads) { - cpu_relax(); - } - - qatomic_set(&test_start, true); - g_usleep(duration * G_USEC_PER_SEC); - qatomic_set(&test_stop, true); - - for (i = 0; i < n_threads; i++) { - qemu_thread_join(&threads[i]); - } -} - -static void create_threads(void) -{ - unsigned int i; - - threads = g_new(QemuThread, n_threads); - th_info = g_new(struct thread_info, n_threads); - counts = qemu_memalign(64, sizeof(*counts) * range); - memset(counts, 0, sizeof(*counts) * range); - for (i = 0; i < range; i++) { - qemu_mutex_init(&counts[i].lock); - } - - for (i = 0; i < n_threads; i++) { - struct thread_info *info = &th_info[i]; - - info->r = (i + 1) ^ time(NULL); - qemu_thread_create(&threads[i], NULL, thread_func, info, - QEMU_THREAD_JOINABLE); - } -} - -static void pr_params(void) -{ - printf("Parameters:\n"); - printf(" # of threads: %u\n", n_threads); - printf(" duration: %u\n", duration); - printf(" ops' range: %u\n", range); -} - -static void pr_stats(void) -{ - unsigned long long val = 0; - unsigned int i; - double tx; - - for (i = 0; i < range; i++) { - val += counts[i].val; - } - tx = val / duration / 1e6; - - printf("Results:\n"); - printf("Duration: %u s\n", duration); - printf(" Throughput: %.2f Mops/s\n", tx); - printf(" Throughput/thread: %.2f Mops/s/thread\n", tx / n_threads); -} - -static void parse_args(int argc, char *argv[]) -{ - int c; - - for (;;) { - c = getopt(argc, argv, "hd:n:mpr:"); - if (c < 0) { - break; - } - switch (c) { - case 'h': - usage_complete(argv); - exit(0); - case 'd': - duration = atoi(optarg); - break; - case 'n': - n_threads = atoi(optarg); - break; - case 'm': - use_mutex = true; - break; - case 'p': - qsp_enable(); - break; - case 'r': - range = pow2ceil(atoi(optarg)); - break; - } - } -} - -int main(int argc, char *argv[]) -{ - parse_args(argc, argv); - pr_params(); - create_threads(); - run_test(); - pr_stats(); - return 0; -} diff --git a/tests/bench/atomic64-bench.c b/tests/bench/atomic64-bench.c new file mode 100644 index 0000000000..e474753d34 --- /dev/null +++ b/tests/bench/atomic64-bench.c @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2018, Emilio G. Cota + * + * License: GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#include "qemu/osdep.h" +#include "qemu/thread.h" +#include "qemu/host-utils.h" +#include "qemu/processor.h" + +struct thread_info { + uint64_t r; + uint64_t accesses; +} QEMU_ALIGNED(64); + +struct count { + int64_t i64; +} QEMU_ALIGNED(64); + +static QemuThread *threads; +static struct thread_info *th_info; +static unsigned int n_threads = 1; +static unsigned int n_ready_threads; +static struct count *counts; +static unsigned int duration = 1; +static unsigned int range = 1024; +static bool test_start; +static bool test_stop; + +static const char commands_string[] = + " -d = duration in seconds\n" + " -n = number of threads\n" + " -r = range (will be rounded up to pow2)"; + +static void usage_complete(char *argv[]) +{ + fprintf(stderr, "Usage: %s [options]\n", argv[0]); + fprintf(stderr, "options:\n%s\n", commands_string); +} + +/* + * From: https://en.wikipedia.org/wiki/Xorshift + * This is faster than rand_r(), and gives us a wider range (RAND_MAX is only + * guaranteed to be >= INT_MAX). + */ +static uint64_t xorshift64star(uint64_t x) +{ + x ^= x >> 12; /* a */ + x ^= x << 25; /* b */ + x ^= x >> 27; /* c */ + return x * UINT64_C(2685821657736338717); +} + +static void *thread_func(void *arg) +{ + struct thread_info *info = arg; + + qatomic_inc(&n_ready_threads); + while (!qatomic_read(&test_start)) { + cpu_relax(); + } + + while (!qatomic_read(&test_stop)) { + unsigned int index; + + info->r = xorshift64star(info->r); + index = info->r & (range - 1); + qatomic_read_i64(&counts[index].i64); + info->accesses++; + } + return NULL; +} + +static void run_test(void) +{ + unsigned int i; + + while (qatomic_read(&n_ready_threads) != n_threads) { + cpu_relax(); + } + + qatomic_set(&test_start, true); + g_usleep(duration * G_USEC_PER_SEC); + qatomic_set(&test_stop, true); + + for (i = 0; i < n_threads; i++) { + qemu_thread_join(&threads[i]); + } +} + +static void create_threads(void) +{ + unsigned int i; + + threads = g_new(QemuThread, n_threads); + th_info = g_new(struct thread_info, n_threads); + counts = g_malloc0_n(range, sizeof(*counts)); + + for (i = 0; i < n_threads; i++) { + struct thread_info *info = &th_info[i]; + + info->r = (i + 1) ^ time(NULL); + info->accesses = 0; + qemu_thread_create(&threads[i], NULL, thread_func, info, + QEMU_THREAD_JOINABLE); + } +} + +static void pr_params(void) +{ + printf("Parameters:\n"); + printf(" # of threads: %u\n", n_threads); + printf(" duration: %u\n", duration); + printf(" ops' range: %u\n", range); +} + +static void pr_stats(void) +{ + unsigned long long val = 0; + double tx; + int i; + + for (i = 0; i < n_threads; i++) { + val += th_info[i].accesses; + } + tx = val / duration / 1e6; + + printf("Results:\n"); + printf("Duration: %u s\n", duration); + printf(" Throughput: %.2f Mops/s\n", tx); + printf(" Throughput/thread: %.2f Mops/s/thread\n", tx / n_threads); +} + +static void parse_args(int argc, char *argv[]) +{ + int c; + + for (;;) { + c = getopt(argc, argv, "hd:n:r:"); + if (c < 0) { + break; + } + switch (c) { + case 'h': + usage_complete(argv); + exit(0); + case 'd': + duration = atoi(optarg); + break; + case 'n': + n_threads = atoi(optarg); + break; + case 'r': + range = pow2ceil(atoi(optarg)); + break; + } + } +} + +int main(int argc, char *argv[]) +{ + parse_args(argc, argv); + pr_params(); + create_threads(); + run_test(); + pr_stats(); + return 0; +} diff --git a/tests/bench/atomic_add-bench.c b/tests/bench/atomic_add-bench.c new file mode 100644 index 0000000000..f05471ab45 --- /dev/null +++ b/tests/bench/atomic_add-bench.c @@ -0,0 +1,180 @@ +#include "qemu/osdep.h" +#include "qemu/thread.h" +#include "qemu/host-utils.h" +#include "qemu/processor.h" + +struct thread_info { + uint64_t r; +} QEMU_ALIGNED(64); + +struct count { + QemuMutex lock; + unsigned long val; +} QEMU_ALIGNED(64); + +static QemuThread *threads; +static struct thread_info *th_info; +static unsigned int n_threads = 1; +static unsigned int n_ready_threads; +static struct count *counts; +static unsigned int duration = 1; +static unsigned int range = 1024; +static bool use_mutex; +static bool test_start; +static bool test_stop; + +static const char commands_string[] = + " -n = number of threads\n" + " -m = use mutexes instead of atomic increments\n" + " -p = enable sync profiler\n" + " -d = duration in seconds\n" + " -r = range (will be rounded up to pow2)"; + +static void usage_complete(char *argv[]) +{ + fprintf(stderr, "Usage: %s [options]\n", argv[0]); + fprintf(stderr, "options:\n%s\n", commands_string); +} + +/* + * From: https://en.wikipedia.org/wiki/Xorshift + * This is faster than rand_r(), and gives us a wider range (RAND_MAX is only + * guaranteed to be >= INT_MAX). + */ +static uint64_t xorshift64star(uint64_t x) +{ + x ^= x >> 12; /* a */ + x ^= x << 25; /* b */ + x ^= x >> 27; /* c */ + return x * UINT64_C(2685821657736338717); +} + +static void *thread_func(void *arg) +{ + struct thread_info *info = arg; + + qatomic_inc(&n_ready_threads); + while (!qatomic_read(&test_start)) { + cpu_relax(); + } + + while (!qatomic_read(&test_stop)) { + unsigned int index; + + info->r = xorshift64star(info->r); + index = info->r & (range - 1); + if (use_mutex) { + qemu_mutex_lock(&counts[index].lock); + counts[index].val += 1; + qemu_mutex_unlock(&counts[index].lock); + } else { + qatomic_inc(&counts[index].val); + } + } + return NULL; +} + +static void run_test(void) +{ + unsigned int i; + + while (qatomic_read(&n_ready_threads) != n_threads) { + cpu_relax(); + } + + qatomic_set(&test_start, true); + g_usleep(duration * G_USEC_PER_SEC); + qatomic_set(&test_stop, true); + + for (i = 0; i < n_threads; i++) { + qemu_thread_join(&threads[i]); + } +} + +static void create_threads(void) +{ + unsigned int i; + + threads = g_new(QemuThread, n_threads); + th_info = g_new(struct thread_info, n_threads); + counts = qemu_memalign(64, sizeof(*counts) * range); + memset(counts, 0, sizeof(*counts) * range); + for (i = 0; i < range; i++) { + qemu_mutex_init(&counts[i].lock); + } + + for (i = 0; i < n_threads; i++) { + struct thread_info *info = &th_info[i]; + + info->r = (i + 1) ^ time(NULL); + qemu_thread_create(&threads[i], NULL, thread_func, info, + QEMU_THREAD_JOINABLE); + } +} + +static void pr_params(void) +{ + printf("Parameters:\n"); + printf(" # of threads: %u\n", n_threads); + printf(" duration: %u\n", duration); + printf(" ops' range: %u\n", range); +} + +static void pr_stats(void) +{ + unsigned long long val = 0; + unsigned int i; + double tx; + + for (i = 0; i < range; i++) { + val += counts[i].val; + } + tx = val / duration / 1e6; + + printf("Results:\n"); + printf("Duration: %u s\n", duration); + printf(" Throughput: %.2f Mops/s\n", tx); + printf(" Throughput/thread: %.2f Mops/s/thread\n", tx / n_threads); +} + +static void parse_args(int argc, char *argv[]) +{ + int c; + + for (;;) { + c = getopt(argc, argv, "hd:n:mpr:"); + if (c < 0) { + break; + } + switch (c) { + case 'h': + usage_complete(argv); + exit(0); + case 'd': + duration = atoi(optarg); + break; + case 'n': + n_threads = atoi(optarg); + break; + case 'm': + use_mutex = true; + break; + case 'p': + qsp_enable(); + break; + case 'r': + range = pow2ceil(atoi(optarg)); + break; + } + } +} + +int main(int argc, char *argv[]) +{ + parse_args(argc, argv); + pr_params(); + create_threads(); + run_test(); + pr_stats(); + return 0; +} diff --git a/tests/bench/benchmark-crypto-cipher.c b/tests/bench/benchmark-crypto-cipher.c new file mode 100644 index 0000000000..c04f0a0fba --- /dev/null +++ b/tests/bench/benchmark-crypto-cipher.c @@ -0,0 +1,208 @@ +/* + * QEMU Crypto cipher speed benchmark + * + * Copyright (c) 2017 HUAWEI TECHNOLOGIES CO., LTD. + * + * Authors: + * Longpeng(Mike) + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. See the COPYING file in the + * top-level directory. + */ +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "crypto/init.h" +#include "crypto/cipher.h" + +static void test_cipher_speed(size_t chunk_size, + QCryptoCipherMode mode, + QCryptoCipherAlgorithm alg) +{ + QCryptoCipher *cipher; + Error *err = NULL; + uint8_t *key = NULL, *iv = NULL; + uint8_t *plaintext = NULL, *ciphertext = NULL; + size_t nkey; + size_t niv; + const size_t total = 2 * GiB; + size_t remain; + + if (!qcrypto_cipher_supports(alg, mode)) { + return; + } + + nkey = qcrypto_cipher_get_key_len(alg); + niv = qcrypto_cipher_get_iv_len(alg, mode); + if (mode == QCRYPTO_CIPHER_MODE_XTS) { + nkey *= 2; + } + + key = g_new0(uint8_t, nkey); + memset(key, g_test_rand_int(), nkey); + + iv = g_new0(uint8_t, niv); + memset(iv, g_test_rand_int(), niv); + + ciphertext = g_new0(uint8_t, chunk_size); + + plaintext = g_new0(uint8_t, chunk_size); + memset(plaintext, g_test_rand_int(), chunk_size); + + cipher = qcrypto_cipher_new(alg, mode, + key, nkey, &err); + g_assert(cipher != NULL); + + if (mode != QCRYPTO_CIPHER_MODE_ECB) + g_assert(qcrypto_cipher_setiv(cipher, + iv, niv, + &err) == 0); + + g_test_timer_start(); + remain = total; + while (remain) { + g_assert(qcrypto_cipher_encrypt(cipher, + plaintext, + ciphertext, + chunk_size, + &err) == 0); + remain -= chunk_size; + } + g_test_timer_elapsed(); + + g_test_message("enc(%s-%s) chunk %zu bytes %.2f MB/sec ", + QCryptoCipherAlgorithm_str(alg), + QCryptoCipherMode_str(mode), + chunk_size, (double)total / MiB / g_test_timer_last()); + + g_test_timer_start(); + remain = total; + while (remain) { + g_assert(qcrypto_cipher_decrypt(cipher, + plaintext, + ciphertext, + chunk_size, + &err) == 0); + remain -= chunk_size; + } + g_test_timer_elapsed(); + + g_test_message("dec(%s-%s) chunk %zu bytes %.2f MB/sec ", + QCryptoCipherAlgorithm_str(alg), + QCryptoCipherMode_str(mode), + chunk_size, (double)total / MiB / g_test_timer_last()); + + qcrypto_cipher_free(cipher); + g_free(plaintext); + g_free(ciphertext); + g_free(iv); + g_free(key); +} + + +static void test_cipher_speed_ecb_aes_128(const void *opaque) +{ + size_t chunk_size = (size_t)opaque; + test_cipher_speed(chunk_size, + QCRYPTO_CIPHER_MODE_ECB, + QCRYPTO_CIPHER_ALG_AES_128); +} + +static void test_cipher_speed_ecb_aes_256(const void *opaque) +{ + size_t chunk_size = (size_t)opaque; + test_cipher_speed(chunk_size, + QCRYPTO_CIPHER_MODE_ECB, + QCRYPTO_CIPHER_ALG_AES_256); +} + +static void test_cipher_speed_cbc_aes_128(const void *opaque) +{ + size_t chunk_size = (size_t)opaque; + test_cipher_speed(chunk_size, + QCRYPTO_CIPHER_MODE_CBC, + QCRYPTO_CIPHER_ALG_AES_128); +} + +static void test_cipher_speed_cbc_aes_256(const void *opaque) +{ + size_t chunk_size = (size_t)opaque; + test_cipher_speed(chunk_size, + QCRYPTO_CIPHER_MODE_CBC, + QCRYPTO_CIPHER_ALG_AES_256); +} + +static void test_cipher_speed_ctr_aes_128(const void *opaque) +{ + size_t chunk_size = (size_t)opaque; + test_cipher_speed(chunk_size, + QCRYPTO_CIPHER_MODE_CTR, + QCRYPTO_CIPHER_ALG_AES_128); +} + +static void test_cipher_speed_ctr_aes_256(const void *opaque) +{ + size_t chunk_size = (size_t)opaque; + test_cipher_speed(chunk_size, + QCRYPTO_CIPHER_MODE_CTR, + QCRYPTO_CIPHER_ALG_AES_256); +} + +static void test_cipher_speed_xts_aes_128(const void *opaque) +{ + size_t chunk_size = (size_t)opaque; + test_cipher_speed(chunk_size, + QCRYPTO_CIPHER_MODE_XTS, + QCRYPTO_CIPHER_ALG_AES_128); +} + +static void test_cipher_speed_xts_aes_256(const void *opaque) +{ + size_t chunk_size = (size_t)opaque; + test_cipher_speed(chunk_size, + QCRYPTO_CIPHER_MODE_XTS, + QCRYPTO_CIPHER_ALG_AES_256); +} + + +int main(int argc, char **argv) +{ + char *alg = NULL; + char *size = NULL; + g_test_init(&argc, &argv, NULL); + g_assert(qcrypto_init(NULL) == 0); + +#define ADD_TEST(mode, cipher, keysize, chunk) \ + if ((!alg || g_str_equal(alg, #mode)) && \ + (!size || g_str_equal(size, #chunk))) \ + g_test_add_data_func( \ + "/crypto/cipher/" #mode "-" #cipher "-" #keysize "/chunk-" #chunk, \ + (void *)chunk, \ + test_cipher_speed_ ## mode ## _ ## cipher ## _ ## keysize) + + if (argc >= 2) { + alg = argv[1]; + } + if (argc >= 3) { + size = argv[2]; + } + +#define ADD_TESTS(chunk) \ + do { \ + ADD_TEST(ecb, aes, 128, chunk); \ + ADD_TEST(ecb, aes, 256, chunk); \ + ADD_TEST(cbc, aes, 128, chunk); \ + ADD_TEST(cbc, aes, 256, chunk); \ + ADD_TEST(ctr, aes, 128, chunk); \ + ADD_TEST(ctr, aes, 256, chunk); \ + ADD_TEST(xts, aes, 128, chunk); \ + ADD_TEST(xts, aes, 256, chunk); \ + } while (0) + + ADD_TESTS(512); + ADD_TESTS(4096); + ADD_TESTS(16384); + ADD_TESTS(65536); + + return g_test_run(); +} diff --git a/tests/bench/benchmark-crypto-hash.c b/tests/bench/benchmark-crypto-hash.c new file mode 100644 index 0000000000..927b00bb4d --- /dev/null +++ b/tests/bench/benchmark-crypto-hash.c @@ -0,0 +1,116 @@ +/* + * QEMU Crypto hash speed benchmark + * + * Copyright (c) 2017 HUAWEI TECHNOLOGIES CO., LTD. + * + * Authors: + * Longpeng(Mike) + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. See the COPYING file in the + * top-level directory. + */ +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "crypto/init.h" +#include "crypto/hash.h" + +typedef struct QCryptoHashOpts { + size_t chunk_size; + QCryptoHashAlgorithm alg; +} QCryptoHashOpts; + +static void test_hash_speed(const void *opaque) +{ + const QCryptoHashOpts *opts = opaque; + uint8_t *in = NULL, *out = NULL; + size_t out_len = 0; + const size_t total = 2 * GiB; + size_t remain; + struct iovec iov; + int ret; + + in = g_new0(uint8_t, opts->chunk_size); + memset(in, g_test_rand_int(), opts->chunk_size); + + iov.iov_base = (char *)in; + iov.iov_len = opts->chunk_size; + + g_test_timer_start(); + remain = total; + while (remain) { + ret = qcrypto_hash_bytesv(opts->alg, + &iov, 1, &out, &out_len, + NULL); + g_assert(ret == 0); + + remain -= opts->chunk_size; + } + g_test_timer_elapsed(); + + g_test_message("hash(%s): chunk %zu bytes %.2f MB/sec", + QCryptoHashAlgorithm_str(opts->alg), + opts->chunk_size, total / g_test_timer_last()); + + g_free(out); + g_free(in); +} + +int main(int argc, char **argv) +{ + char name[64]; + + g_test_init(&argc, &argv, NULL); + g_assert(qcrypto_init(NULL) == 0); + +#define TEST_ONE(a, c) \ + QCryptoHashOpts opts ## a ## c = { \ + .alg = QCRYPTO_HASH_ALG_ ## a, .chunk_size = c, \ + }; \ + memset(name, 0 , sizeof(name)); \ + snprintf(name, sizeof(name), \ + "/crypto/benchmark/hash/%s/bufsize-%d", \ + QCryptoHashAlgorithm_str(QCRYPTO_HASH_ALG_ ## a), \ + c); \ + if (qcrypto_hash_supports(QCRYPTO_HASH_ALG_ ## a)) \ + g_test_add_data_func(name, \ + &opts ## a ## c, \ + test_hash_speed); + + TEST_ONE(MD5, 512); + TEST_ONE(MD5, 1024); + TEST_ONE(MD5, 4096); + TEST_ONE(MD5, 16384); + + TEST_ONE(SHA1, 512); + TEST_ONE(SHA1, 1024); + TEST_ONE(SHA1, 4096); + TEST_ONE(SHA1, 16384); + + TEST_ONE(SHA224, 512); + TEST_ONE(SHA224, 1024); + TEST_ONE(SHA224, 4096); + TEST_ONE(SHA224, 16384); + + TEST_ONE(SHA384, 512); + TEST_ONE(SHA384, 1024); + TEST_ONE(SHA384, 4096); + TEST_ONE(SHA384, 16384); + + TEST_ONE(SHA256, 512); + TEST_ONE(SHA256, 1024); + TEST_ONE(SHA256, 4096); + TEST_ONE(SHA256, 16384); + + TEST_ONE(SHA512, 512); + TEST_ONE(SHA512, 1024); + TEST_ONE(SHA512, 4096); + TEST_ONE(SHA512, 16384); + + TEST_ONE(RIPEMD160, 512); + TEST_ONE(RIPEMD160, 1024); + TEST_ONE(RIPEMD160, 4096); + TEST_ONE(RIPEMD160, 16384); + + return g_test_run(); +} diff --git a/tests/bench/benchmark-crypto-hmac.c b/tests/bench/benchmark-crypto-hmac.c new file mode 100644 index 0000000000..5cca636789 --- /dev/null +++ b/tests/bench/benchmark-crypto-hmac.c @@ -0,0 +1,81 @@ +/* + * QEMU Crypto hmac speed benchmark + * + * Copyright (c) 2017 HUAWEI TECHNOLOGIES CO., LTD. + * + * Authors: + * Longpeng(Mike) + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. See the COPYING file in the + * top-level directory. + */ +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "crypto/init.h" +#include "crypto/hmac.h" + +#define KEY "monkey monkey monkey monkey" + +static void test_hmac_speed(const void *opaque) +{ + size_t chunk_size = (size_t)opaque; + QCryptoHmac *hmac = NULL; + uint8_t *in = NULL, *out = NULL; + size_t out_len = 0; + double total = 0.0; + struct iovec iov; + Error *err = NULL; + int ret; + + if (!qcrypto_hmac_supports(QCRYPTO_HASH_ALG_SHA256)) { + return; + } + + in = g_new0(uint8_t, chunk_size); + memset(in, g_test_rand_int(), chunk_size); + + iov.iov_base = (char *)in; + iov.iov_len = chunk_size; + + g_test_timer_start(); + do { + hmac = qcrypto_hmac_new(QCRYPTO_HASH_ALG_SHA256, + (const uint8_t *)KEY, strlen(KEY), &err); + g_assert(err == NULL); + g_assert(hmac != NULL); + + ret = qcrypto_hmac_bytesv(hmac, &iov, 1, &out, &out_len, &err); + g_assert(ret == 0); + g_assert(err == NULL); + + qcrypto_hmac_free(hmac); + + total += chunk_size; + } while (g_test_timer_elapsed() < 5.0); + + total /= MiB; + g_test_message("hmac(%s): chunk %zu bytes %.2f MB/sec", + QCryptoHashAlgorithm_str(QCRYPTO_HASH_ALG_SHA256), + chunk_size, total / g_test_timer_last()); + + g_free(out); + g_free(in); +} + +int main(int argc, char **argv) +{ + size_t i; + char name[64]; + + g_test_init(&argc, &argv, NULL); + g_assert(qcrypto_init(NULL) == 0); + + for (i = 512; i <= 64 * KiB; i *= 2) { + memset(name, 0 , sizeof(name)); + snprintf(name, sizeof(name), "/crypto/hmac/speed-%zu", i); + g_test_add_data_func(name, (void *)i, test_hmac_speed); + } + + return g_test_run(); +} diff --git a/tests/bench/meson.build b/tests/bench/meson.build new file mode 100644 index 0000000000..00b3c209dc --- /dev/null +++ b/tests/bench/meson.build @@ -0,0 +1,34 @@ + +qht_bench = executable('qht-bench', + sources: 'qht-bench.c', + dependencies: [qemuutil]) + +executable('atomic_add-bench', + sources: files('atomic_add-bench.c'), + dependencies: [qemuutil], + build_by_default: false) + +executable('atomic64-bench', + sources: files('atomic64-bench.c'), + dependencies: [qemuutil], + build_by_default: false) + +benchs = {} + +if have_block + benchs += { + 'benchmark-crypto-hash': [crypto], + 'benchmark-crypto-hmac': [crypto], + 'benchmark-crypto-cipher': [crypto], + } +endif + +foreach bench_name, deps: benchs + exe = executable(bench_name, bench_name + '.c', + dependencies: [qemuutil] + deps) + benchmark(bench_name, exe, + args: ['--tap', '-k'], + protocol: 'tap', + timeout: 0, + suite: ['speed']) +endforeach diff --git a/tests/bench/qht-bench.c b/tests/bench/qht-bench.c new file mode 100644 index 0000000000..2e5b70ccd0 --- /dev/null +++ b/tests/bench/qht-bench.c @@ -0,0 +1,523 @@ +/* + * Copyright (C) 2016, Emilio G. Cota + * + * License: GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#include "qemu/osdep.h" +#include "qemu/processor.h" +#include "qemu/atomic.h" +#include "qemu/qht.h" +#include "qemu/rcu.h" +#include "qemu/xxhash.h" + +struct thread_stats { + size_t rd; + size_t not_rd; + size_t in; + size_t not_in; + size_t rm; + size_t not_rm; + size_t rz; + size_t not_rz; +}; + +struct thread_info { + void (*func)(struct thread_info *); + struct thread_stats stats; + /* + * Seed is in the range [1..UINT64_MAX], because the RNG requires + * a non-zero seed. To use, subtract 1 and compare against the + * threshold with =. This lets threshold = 0 never match (0% hit), + * and threshold = UINT64_MAX always match (100% hit). + */ + uint64_t seed; + bool write_op; /* writes alternate between insertions and removals */ + bool resize_down; +} QEMU_ALIGNED(64); /* avoid false sharing among threads */ + +static struct qht ht; +static QemuThread *rw_threads; + +#define DEFAULT_RANGE (4096) +#define DEFAULT_QHT_N_ELEMS DEFAULT_RANGE + +static unsigned int duration = 1; +static unsigned int n_rw_threads = 1; +static unsigned long lookup_range = DEFAULT_RANGE; +static unsigned long update_range = DEFAULT_RANGE; +static size_t init_range = DEFAULT_RANGE; +static size_t init_size = DEFAULT_RANGE; +static size_t n_ready_threads; +static long populate_offset; +static long *keys; + +static size_t resize_min; +static size_t resize_max; +static struct thread_info *rz_info; +static unsigned long resize_delay = 1000; +static double resize_rate; /* 0.0 to 1.0 */ +static unsigned int n_rz_threads = 1; +static QemuThread *rz_threads; +static bool precompute_hash; + +static double update_rate; /* 0.0 to 1.0 */ +static uint64_t update_threshold; +static uint64_t resize_threshold; + +static size_t qht_n_elems = DEFAULT_QHT_N_ELEMS; +static int qht_mode; + +static bool test_start; +static bool test_stop; + +static struct thread_info *rw_info; + +static const char commands_string[] = + " -d = duration, in seconds\n" + " -n = number of threads\n" + "\n" + " -o = offset at which keys start\n" + " -p = precompute hashes\n" + "\n" + " -g = set -s,-k,-K,-l,-r to the same value\n" + " -s = initial size hint\n" + " -k = initial number of keys\n" + " -K = initial range of keys (will be rounded up to pow2)\n" + " -l = lookup range of keys (will be rounded up to pow2)\n" + " -r = update range of keys (will be rounded up to pow2)\n" + "\n" + " -u = update rate (0.0 to 100.0), 50/50 split of insertions/removals\n" + "\n" + " -R = enable auto-resize\n" + " -S = resize rate (0.0 to 100.0)\n" + " -D = delay (in us) between potential resizes\n" + " -N = number of resize threads"; + +static void usage_complete(int argc, char *argv[]) +{ + fprintf(stderr, "Usage: %s [options]\n", argv[0]); + fprintf(stderr, "options:\n%s\n", commands_string); + exit(-1); +} + +static bool is_equal(const void *ap, const void *bp) +{ + const long *a = ap; + const long *b = bp; + + return *a == *b; +} + +static uint32_t h(unsigned long v) +{ + return qemu_xxhash2(v); +} + +static uint32_t hval(unsigned long v) +{ + return v; +} + +static uint32_t (*hfunc)(unsigned long v) = h; + +/* + * From: https://en.wikipedia.org/wiki/Xorshift + * This is faster than rand_r(), and gives us a wider range (RAND_MAX is only + * guaranteed to be >= INT_MAX). + */ +static uint64_t xorshift64star(uint64_t x) +{ + x ^= x >> 12; /* a */ + x ^= x << 25; /* b */ + x ^= x >> 27; /* c */ + return x * UINT64_C(2685821657736338717); +} + +static void do_rz(struct thread_info *info) +{ + struct thread_stats *stats = &info->stats; + uint64_t r = info->seed - 1; + + if (r < resize_threshold) { + size_t size = info->resize_down ? resize_min : resize_max; + bool resized; + + resized = qht_resize(&ht, size); + info->resize_down = !info->resize_down; + + if (resized) { + stats->rz++; + } else { + stats->not_rz++; + } + } + g_usleep(resize_delay); +} + +static void do_rw(struct thread_info *info) +{ + struct thread_stats *stats = &info->stats; + uint64_t r = info->seed - 1; + uint32_t hash; + long *p; + + if (r >= update_threshold) { + bool read; + + p = &keys[r & (lookup_range - 1)]; + hash = hfunc(*p); + read = qht_lookup(&ht, p, hash); + if (read) { + stats->rd++; + } else { + stats->not_rd++; + } + } else { + p = &keys[r & (update_range - 1)]; + hash = hfunc(*p); + if (info->write_op) { + bool written = false; + + if (qht_lookup(&ht, p, hash) == NULL) { + written = qht_insert(&ht, p, hash, NULL); + } + if (written) { + stats->in++; + } else { + stats->not_in++; + } + } else { + bool removed = false; + + if (qht_lookup(&ht, p, hash)) { + removed = qht_remove(&ht, p, hash); + } + if (removed) { + stats->rm++; + } else { + stats->not_rm++; + } + } + info->write_op = !info->write_op; + } +} + +static void *thread_func(void *p) +{ + struct thread_info *info = p; + + rcu_register_thread(); + + qatomic_inc(&n_ready_threads); + while (!qatomic_read(&test_start)) { + cpu_relax(); + } + + rcu_read_lock(); + while (!qatomic_read(&test_stop)) { + info->seed = xorshift64star(info->seed); + info->func(info); + } + rcu_read_unlock(); + + rcu_unregister_thread(); + return NULL; +} + +/* sets everything except info->func */ +static void prepare_thread_info(struct thread_info *info, int i) +{ + /* seed for the RNG; each thread should have a different one */ + info->seed = (i + 1) ^ time(NULL); + /* the first update will be a write */ + info->write_op = true; + /* the first resize will be down */ + info->resize_down = true; + + memset(&info->stats, 0, sizeof(info->stats)); +} + +static void +th_create_n(QemuThread **threads, struct thread_info **infos, const char *name, + void (*func)(struct thread_info *), int offset, int n) +{ + struct thread_info *info; + QemuThread *th; + int i; + + th = g_malloc(sizeof(*th) * n); + *threads = th; + + info = qemu_memalign(64, sizeof(*info) * n); + *infos = info; + + for (i = 0; i < n; i++) { + prepare_thread_info(&info[i], offset + i); + info[i].func = func; + qemu_thread_create(&th[i], name, thread_func, &info[i], + QEMU_THREAD_JOINABLE); + } +} + +static void create_threads(void) +{ + th_create_n(&rw_threads, &rw_info, "rw", do_rw, 0, n_rw_threads); + th_create_n(&rz_threads, &rz_info, "rz", do_rz, n_rw_threads, n_rz_threads); +} + +static void pr_params(void) +{ + printf("Parameters:\n"); + printf(" duration: %d s\n", duration); + printf(" # of threads: %u\n", n_rw_threads); + printf(" initial # of keys: %zu\n", init_size); + printf(" initial size hint: %zu\n", qht_n_elems); + printf(" auto-resize: %s\n", + qht_mode & QHT_MODE_AUTO_RESIZE ? "on" : "off"); + if (resize_rate) { + printf(" resize_rate: %f%%\n", resize_rate * 100.0); + printf(" resize range: %zu-%zu\n", resize_min, resize_max); + printf(" # resize threads %u\n", n_rz_threads); + } + printf(" update rate: %f%%\n", update_rate * 100.0); + printf(" offset: %ld\n", populate_offset); + printf(" initial key range: %zu\n", init_range); + printf(" lookup range: %lu\n", lookup_range); + printf(" update range: %lu\n", update_range); +} + +static void do_threshold(double rate, uint64_t *threshold) +{ + /* + * For 0 <= rate <= 1, scale to fit in a uint64_t. + * + * Scale by 2**64, with a special case for 1.0. + * The remainder of the possible values are scattered between 0 + * and 0xfffffffffffff800 (nextafter(0x1p64, 0)). + * + * Note that we cannot simply scale by UINT64_MAX, because that + * value is not representable as an IEEE double value. + * + * If we scale by the next largest value, nextafter(0x1p64, 0), + * then the remainder of the possible values are scattered between + * 0 and 0xfffffffffffff000. Which leaves us with a gap between + * the final two inputs that is twice as large as any other. + */ + if (rate == 1.0) { + *threshold = UINT64_MAX; + } else { + *threshold = rate * 0x1p64; + } +} + +static void htable_init(void) +{ + unsigned long n = MAX(init_range, update_range); + uint64_t r = time(NULL); + size_t retries = 0; + size_t i; + + /* avoid allocating memory later by allocating all the keys now */ + keys = g_malloc(sizeof(*keys) * n); + for (i = 0; i < n; i++) { + long val = populate_offset + i; + + keys[i] = precompute_hash ? h(val) : hval(val); + } + + /* some sanity checks */ + g_assert_cmpuint(lookup_range, <=, n); + + /* compute thresholds */ + do_threshold(update_rate, &update_threshold); + do_threshold(resize_rate, &resize_threshold); + + if (resize_rate) { + resize_min = n / 2; + resize_max = n; + assert(resize_min < resize_max); + } else { + n_rz_threads = 0; + } + + /* initialize the hash table */ + qht_init(&ht, is_equal, qht_n_elems, qht_mode); + assert(init_size <= init_range); + + pr_params(); + + fprintf(stderr, "Initialization: populating %zu items...", init_size); + for (i = 0; i < init_size; i++) { + for (;;) { + uint32_t hash; + long *p; + + r = xorshift64star(r); + p = &keys[r & (init_range - 1)]; + hash = hfunc(*p); + if (qht_insert(&ht, p, hash, NULL)) { + break; + } + retries++; + } + } + fprintf(stderr, " populated after %zu retries\n", retries); +} + +static void add_stats(struct thread_stats *s, struct thread_info *info, int n) +{ + int i; + + for (i = 0; i < n; i++) { + struct thread_stats *stats = &info[i].stats; + + s->rd += stats->rd; + s->not_rd += stats->not_rd; + + s->in += stats->in; + s->not_in += stats->not_in; + + s->rm += stats->rm; + s->not_rm += stats->not_rm; + + s->rz += stats->rz; + s->not_rz += stats->not_rz; + } +} + +static void pr_stats(void) +{ + struct thread_stats s = {}; + double tx; + + add_stats(&s, rw_info, n_rw_threads); + add_stats(&s, rz_info, n_rz_threads); + + printf("Results:\n"); + + if (resize_rate) { + printf(" Resizes: %zu (%.2f%% of %zu)\n", + s.rz, (double)s.rz / (s.rz + s.not_rz) * 100, s.rz + s.not_rz); + } + + printf(" Read: %.2f M (%.2f%% of %.2fM)\n", + (double)s.rd / 1e6, + (double)s.rd / (s.rd + s.not_rd) * 100, + (double)(s.rd + s.not_rd) / 1e6); + printf(" Inserted: %.2f M (%.2f%% of %.2fM)\n", + (double)s.in / 1e6, + (double)s.in / (s.in + s.not_in) * 100, + (double)(s.in + s.not_in) / 1e6); + printf(" Removed: %.2f M (%.2f%% of %.2fM)\n", + (double)s.rm / 1e6, + (double)s.rm / (s.rm + s.not_rm) * 100, + (double)(s.rm + s.not_rm) / 1e6); + + tx = (s.rd + s.not_rd + s.in + s.not_in + s.rm + s.not_rm) / 1e6 / duration; + printf(" Throughput: %.2f MT/s\n", tx); + printf(" Throughput/thread: %.2f MT/s/thread\n", tx / n_rw_threads); +} + +static void run_test(void) +{ + int i; + + while (qatomic_read(&n_ready_threads) != n_rw_threads + n_rz_threads) { + cpu_relax(); + } + + qatomic_set(&test_start, true); + g_usleep(duration * G_USEC_PER_SEC); + qatomic_set(&test_stop, true); + + for (i = 0; i < n_rw_threads; i++) { + qemu_thread_join(&rw_threads[i]); + } + for (i = 0; i < n_rz_threads; i++) { + qemu_thread_join(&rz_threads[i]); + } +} + +static void parse_args(int argc, char *argv[]) +{ + int c; + + for (;;) { + c = getopt(argc, argv, "d:D:g:k:K:l:hn:N:o:pr:Rs:S:u:"); + if (c < 0) { + break; + } + switch (c) { + case 'd': + duration = atoi(optarg); + break; + case 'D': + resize_delay = atol(optarg); + break; + case 'g': + init_range = pow2ceil(atol(optarg)); + lookup_range = pow2ceil(atol(optarg)); + update_range = pow2ceil(atol(optarg)); + qht_n_elems = atol(optarg); + init_size = atol(optarg); + break; + case 'h': + usage_complete(argc, argv); + exit(0); + case 'k': + init_size = atol(optarg); + break; + case 'K': + init_range = pow2ceil(atol(optarg)); + break; + case 'l': + lookup_range = pow2ceil(atol(optarg)); + break; + case 'n': + n_rw_threads = atoi(optarg); + break; + case 'N': + n_rz_threads = atoi(optarg); + break; + case 'o': + populate_offset = atol(optarg); + break; + case 'p': + precompute_hash = true; + hfunc = hval; + break; + case 'r': + update_range = pow2ceil(atol(optarg)); + break; + case 'R': + qht_mode |= QHT_MODE_AUTO_RESIZE; + break; + case 's': + qht_n_elems = atol(optarg); + break; + case 'S': + resize_rate = atof(optarg) / 100.0; + if (resize_rate > 1.0) { + resize_rate = 1.0; + } + break; + case 'u': + update_rate = atof(optarg) / 100.0; + if (update_rate > 1.0) { + update_rate = 1.0; + } + break; + } + } +} + +int main(int argc, char *argv[]) +{ + parse_args(argc, argv); + htable_init(); + create_threads(); + run_test(); + pr_stats(); + return 0; +} diff --git a/tests/benchmark-crypto-cipher.c b/tests/benchmark-crypto-cipher.c deleted file mode 100644 index c04f0a0fba..0000000000 --- a/tests/benchmark-crypto-cipher.c +++ /dev/null @@ -1,208 +0,0 @@ -/* - * QEMU Crypto cipher speed benchmark - * - * Copyright (c) 2017 HUAWEI TECHNOLOGIES CO., LTD. - * - * Authors: - * Longpeng(Mike) - * - * This work is licensed under the terms of the GNU GPL, version 2 or - * (at your option) any later version. See the COPYING file in the - * top-level directory. - */ -#include "qemu/osdep.h" -#include "qemu/units.h" -#include "crypto/init.h" -#include "crypto/cipher.h" - -static void test_cipher_speed(size_t chunk_size, - QCryptoCipherMode mode, - QCryptoCipherAlgorithm alg) -{ - QCryptoCipher *cipher; - Error *err = NULL; - uint8_t *key = NULL, *iv = NULL; - uint8_t *plaintext = NULL, *ciphertext = NULL; - size_t nkey; - size_t niv; - const size_t total = 2 * GiB; - size_t remain; - - if (!qcrypto_cipher_supports(alg, mode)) { - return; - } - - nkey = qcrypto_cipher_get_key_len(alg); - niv = qcrypto_cipher_get_iv_len(alg, mode); - if (mode == QCRYPTO_CIPHER_MODE_XTS) { - nkey *= 2; - } - - key = g_new0(uint8_t, nkey); - memset(key, g_test_rand_int(), nkey); - - iv = g_new0(uint8_t, niv); - memset(iv, g_test_rand_int(), niv); - - ciphertext = g_new0(uint8_t, chunk_size); - - plaintext = g_new0(uint8_t, chunk_size); - memset(plaintext, g_test_rand_int(), chunk_size); - - cipher = qcrypto_cipher_new(alg, mode, - key, nkey, &err); - g_assert(cipher != NULL); - - if (mode != QCRYPTO_CIPHER_MODE_ECB) - g_assert(qcrypto_cipher_setiv(cipher, - iv, niv, - &err) == 0); - - g_test_timer_start(); - remain = total; - while (remain) { - g_assert(qcrypto_cipher_encrypt(cipher, - plaintext, - ciphertext, - chunk_size, - &err) == 0); - remain -= chunk_size; - } - g_test_timer_elapsed(); - - g_test_message("enc(%s-%s) chunk %zu bytes %.2f MB/sec ", - QCryptoCipherAlgorithm_str(alg), - QCryptoCipherMode_str(mode), - chunk_size, (double)total / MiB / g_test_timer_last()); - - g_test_timer_start(); - remain = total; - while (remain) { - g_assert(qcrypto_cipher_decrypt(cipher, - plaintext, - ciphertext, - chunk_size, - &err) == 0); - remain -= chunk_size; - } - g_test_timer_elapsed(); - - g_test_message("dec(%s-%s) chunk %zu bytes %.2f MB/sec ", - QCryptoCipherAlgorithm_str(alg), - QCryptoCipherMode_str(mode), - chunk_size, (double)total / MiB / g_test_timer_last()); - - qcrypto_cipher_free(cipher); - g_free(plaintext); - g_free(ciphertext); - g_free(iv); - g_free(key); -} - - -static void test_cipher_speed_ecb_aes_128(const void *opaque) -{ - size_t chunk_size = (size_t)opaque; - test_cipher_speed(chunk_size, - QCRYPTO_CIPHER_MODE_ECB, - QCRYPTO_CIPHER_ALG_AES_128); -} - -static void test_cipher_speed_ecb_aes_256(const void *opaque) -{ - size_t chunk_size = (size_t)opaque; - test_cipher_speed(chunk_size, - QCRYPTO_CIPHER_MODE_ECB, - QCRYPTO_CIPHER_ALG_AES_256); -} - -static void test_cipher_speed_cbc_aes_128(const void *opaque) -{ - size_t chunk_size = (size_t)opaque; - test_cipher_speed(chunk_size, - QCRYPTO_CIPHER_MODE_CBC, - QCRYPTO_CIPHER_ALG_AES_128); -} - -static void test_cipher_speed_cbc_aes_256(const void *opaque) -{ - size_t chunk_size = (size_t)opaque; - test_cipher_speed(chunk_size, - QCRYPTO_CIPHER_MODE_CBC, - QCRYPTO_CIPHER_ALG_AES_256); -} - -static void test_cipher_speed_ctr_aes_128(const void *opaque) -{ - size_t chunk_size = (size_t)opaque; - test_cipher_speed(chunk_size, - QCRYPTO_CIPHER_MODE_CTR, - QCRYPTO_CIPHER_ALG_AES_128); -} - -static void test_cipher_speed_ctr_aes_256(const void *opaque) -{ - size_t chunk_size = (size_t)opaque; - test_cipher_speed(chunk_size, - QCRYPTO_CIPHER_MODE_CTR, - QCRYPTO_CIPHER_ALG_AES_256); -} - -static void test_cipher_speed_xts_aes_128(const void *opaque) -{ - size_t chunk_size = (size_t)opaque; - test_cipher_speed(chunk_size, - QCRYPTO_CIPHER_MODE_XTS, - QCRYPTO_CIPHER_ALG_AES_128); -} - -static void test_cipher_speed_xts_aes_256(const void *opaque) -{ - size_t chunk_size = (size_t)opaque; - test_cipher_speed(chunk_size, - QCRYPTO_CIPHER_MODE_XTS, - QCRYPTO_CIPHER_ALG_AES_256); -} - - -int main(int argc, char **argv) -{ - char *alg = NULL; - char *size = NULL; - g_test_init(&argc, &argv, NULL); - g_assert(qcrypto_init(NULL) == 0); - -#define ADD_TEST(mode, cipher, keysize, chunk) \ - if ((!alg || g_str_equal(alg, #mode)) && \ - (!size || g_str_equal(size, #chunk))) \ - g_test_add_data_func( \ - "/crypto/cipher/" #mode "-" #cipher "-" #keysize "/chunk-" #chunk, \ - (void *)chunk, \ - test_cipher_speed_ ## mode ## _ ## cipher ## _ ## keysize) - - if (argc >= 2) { - alg = argv[1]; - } - if (argc >= 3) { - size = argv[2]; - } - -#define ADD_TESTS(chunk) \ - do { \ - ADD_TEST(ecb, aes, 128, chunk); \ - ADD_TEST(ecb, aes, 256, chunk); \ - ADD_TEST(cbc, aes, 128, chunk); \ - ADD_TEST(cbc, aes, 256, chunk); \ - ADD_TEST(ctr, aes, 128, chunk); \ - ADD_TEST(ctr, aes, 256, chunk); \ - ADD_TEST(xts, aes, 128, chunk); \ - ADD_TEST(xts, aes, 256, chunk); \ - } while (0) - - ADD_TESTS(512); - ADD_TESTS(4096); - ADD_TESTS(16384); - ADD_TESTS(65536); - - return g_test_run(); -} diff --git a/tests/benchmark-crypto-hash.c b/tests/benchmark-crypto-hash.c deleted file mode 100644 index 927b00bb4d..0000000000 --- a/tests/benchmark-crypto-hash.c +++ /dev/null @@ -1,116 +0,0 @@ -/* - * QEMU Crypto hash speed benchmark - * - * Copyright (c) 2017 HUAWEI TECHNOLOGIES CO., LTD. - * - * Authors: - * Longpeng(Mike) - * - * This work is licensed under the terms of the GNU GPL, version 2 or - * (at your option) any later version. See the COPYING file in the - * top-level directory. - */ -#include "qemu/osdep.h" -#include "qemu/units.h" -#include "crypto/init.h" -#include "crypto/hash.h" - -typedef struct QCryptoHashOpts { - size_t chunk_size; - QCryptoHashAlgorithm alg; -} QCryptoHashOpts; - -static void test_hash_speed(const void *opaque) -{ - const QCryptoHashOpts *opts = opaque; - uint8_t *in = NULL, *out = NULL; - size_t out_len = 0; - const size_t total = 2 * GiB; - size_t remain; - struct iovec iov; - int ret; - - in = g_new0(uint8_t, opts->chunk_size); - memset(in, g_test_rand_int(), opts->chunk_size); - - iov.iov_base = (char *)in; - iov.iov_len = opts->chunk_size; - - g_test_timer_start(); - remain = total; - while (remain) { - ret = qcrypto_hash_bytesv(opts->alg, - &iov, 1, &out, &out_len, - NULL); - g_assert(ret == 0); - - remain -= opts->chunk_size; - } - g_test_timer_elapsed(); - - g_test_message("hash(%s): chunk %zu bytes %.2f MB/sec", - QCryptoHashAlgorithm_str(opts->alg), - opts->chunk_size, total / g_test_timer_last()); - - g_free(out); - g_free(in); -} - -int main(int argc, char **argv) -{ - char name[64]; - - g_test_init(&argc, &argv, NULL); - g_assert(qcrypto_init(NULL) == 0); - -#define TEST_ONE(a, c) \ - QCryptoHashOpts opts ## a ## c = { \ - .alg = QCRYPTO_HASH_ALG_ ## a, .chunk_size = c, \ - }; \ - memset(name, 0 , sizeof(name)); \ - snprintf(name, sizeof(name), \ - "/crypto/benchmark/hash/%s/bufsize-%d", \ - QCryptoHashAlgorithm_str(QCRYPTO_HASH_ALG_ ## a), \ - c); \ - if (qcrypto_hash_supports(QCRYPTO_HASH_ALG_ ## a)) \ - g_test_add_data_func(name, \ - &opts ## a ## c, \ - test_hash_speed); - - TEST_ONE(MD5, 512); - TEST_ONE(MD5, 1024); - TEST_ONE(MD5, 4096); - TEST_ONE(MD5, 16384); - - TEST_ONE(SHA1, 512); - TEST_ONE(SHA1, 1024); - TEST_ONE(SHA1, 4096); - TEST_ONE(SHA1, 16384); - - TEST_ONE(SHA224, 512); - TEST_ONE(SHA224, 1024); - TEST_ONE(SHA224, 4096); - TEST_ONE(SHA224, 16384); - - TEST_ONE(SHA384, 512); - TEST_ONE(SHA384, 1024); - TEST_ONE(SHA384, 4096); - TEST_ONE(SHA384, 16384); - - TEST_ONE(SHA256, 512); - TEST_ONE(SHA256, 1024); - TEST_ONE(SHA256, 4096); - TEST_ONE(SHA256, 16384); - - TEST_ONE(SHA512, 512); - TEST_ONE(SHA512, 1024); - TEST_ONE(SHA512, 4096); - TEST_ONE(SHA512, 16384); - - TEST_ONE(RIPEMD160, 512); - TEST_ONE(RIPEMD160, 1024); - TEST_ONE(RIPEMD160, 4096); - TEST_ONE(RIPEMD160, 16384); - - return g_test_run(); -} diff --git a/tests/benchmark-crypto-hmac.c b/tests/benchmark-crypto-hmac.c deleted file mode 100644 index 5cca636789..0000000000 --- a/tests/benchmark-crypto-hmac.c +++ /dev/null @@ -1,81 +0,0 @@ -/* - * QEMU Crypto hmac speed benchmark - * - * Copyright (c) 2017 HUAWEI TECHNOLOGIES CO., LTD. - * - * Authors: - * Longpeng(Mike) - * - * This work is licensed under the terms of the GNU GPL, version 2 or - * (at your option) any later version. See the COPYING file in the - * top-level directory. - */ -#include "qemu/osdep.h" -#include "qemu/units.h" -#include "crypto/init.h" -#include "crypto/hmac.h" - -#define KEY "monkey monkey monkey monkey" - -static void test_hmac_speed(const void *opaque) -{ - size_t chunk_size = (size_t)opaque; - QCryptoHmac *hmac = NULL; - uint8_t *in = NULL, *out = NULL; - size_t out_len = 0; - double total = 0.0; - struct iovec iov; - Error *err = NULL; - int ret; - - if (!qcrypto_hmac_supports(QCRYPTO_HASH_ALG_SHA256)) { - return; - } - - in = g_new0(uint8_t, chunk_size); - memset(in, g_test_rand_int(), chunk_size); - - iov.iov_base = (char *)in; - iov.iov_len = chunk_size; - - g_test_timer_start(); - do { - hmac = qcrypto_hmac_new(QCRYPTO_HASH_ALG_SHA256, - (const uint8_t *)KEY, strlen(KEY), &err); - g_assert(err == NULL); - g_assert(hmac != NULL); - - ret = qcrypto_hmac_bytesv(hmac, &iov, 1, &out, &out_len, &err); - g_assert(ret == 0); - g_assert(err == NULL); - - qcrypto_hmac_free(hmac); - - total += chunk_size; - } while (g_test_timer_elapsed() < 5.0); - - total /= MiB; - g_test_message("hmac(%s): chunk %zu bytes %.2f MB/sec", - QCryptoHashAlgorithm_str(QCRYPTO_HASH_ALG_SHA256), - chunk_size, total / g_test_timer_last()); - - g_free(out); - g_free(in); -} - -int main(int argc, char **argv) -{ - size_t i; - char name[64]; - - g_test_init(&argc, &argv, NULL); - g_assert(qcrypto_init(NULL) == 0); - - for (i = 512; i <= 64 * KiB; i *= 2) { - memset(name, 0 , sizeof(name)); - snprintf(name, sizeof(name), "/crypto/hmac/speed-%zu", i); - g_test_add_data_func(name, (void *)i, test_hmac_speed); - } - - return g_test_run(); -} diff --git a/tests/meson.build b/tests/meson.build index af43fd1eaf..55a7b08275 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -1,18 +1,6 @@ py3 = import('python').find_installation() -qht_bench = executable('qht-bench', - sources: 'qht-bench.c', - dependencies: [qemuutil]) - -executable('atomic_add-bench', - sources: files('atomic_add-bench.c'), - dependencies: [qemuutil], - build_by_default: false) - -executable('atomic64-bench', - sources: files('atomic64-bench.c'), - dependencies: [qemuutil], - build_by_default: false) +subdir('bench') test_qapi_outputs = [ 'qapi-builtin-types.c', @@ -73,26 +61,6 @@ test_deps = { 'test-qht-par': qht_bench, } -benchs = {} - -if have_block - benchs += { - 'benchmark-crypto-hash': [crypto], - 'benchmark-crypto-hmac': [crypto], - 'benchmark-crypto-cipher': [crypto], - } -endif - -foreach bench_name, deps: benchs - exe = executable(bench_name, bench_name + '.c', - dependencies: [qemuutil] + deps) - benchmark(bench_name, exe, - args: ['--tap', '-k'], - protocol: 'tap', - timeout: 0, - suite: ['speed']) -endforeach - if have_tools and 'CONFIG_VHOST_USER' in config_host and 'CONFIG_LINUX' in config_host executable('vhost-user-bridge', sources: files('vhost-user-bridge.c'), diff --git a/tests/qht-bench.c b/tests/qht-bench.c deleted file mode 100644 index 2e5b70ccd0..0000000000 --- a/tests/qht-bench.c +++ /dev/null @@ -1,523 +0,0 @@ -/* - * Copyright (C) 2016, Emilio G. Cota - * - * License: GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ -#include "qemu/osdep.h" -#include "qemu/processor.h" -#include "qemu/atomic.h" -#include "qemu/qht.h" -#include "qemu/rcu.h" -#include "qemu/xxhash.h" - -struct thread_stats { - size_t rd; - size_t not_rd; - size_t in; - size_t not_in; - size_t rm; - size_t not_rm; - size_t rz; - size_t not_rz; -}; - -struct thread_info { - void (*func)(struct thread_info *); - struct thread_stats stats; - /* - * Seed is in the range [1..UINT64_MAX], because the RNG requires - * a non-zero seed. To use, subtract 1 and compare against the - * threshold with =. This lets threshold = 0 never match (0% hit), - * and threshold = UINT64_MAX always match (100% hit). - */ - uint64_t seed; - bool write_op; /* writes alternate between insertions and removals */ - bool resize_down; -} QEMU_ALIGNED(64); /* avoid false sharing among threads */ - -static struct qht ht; -static QemuThread *rw_threads; - -#define DEFAULT_RANGE (4096) -#define DEFAULT_QHT_N_ELEMS DEFAULT_RANGE - -static unsigned int duration = 1; -static unsigned int n_rw_threads = 1; -static unsigned long lookup_range = DEFAULT_RANGE; -static unsigned long update_range = DEFAULT_RANGE; -static size_t init_range = DEFAULT_RANGE; -static size_t init_size = DEFAULT_RANGE; -static size_t n_ready_threads; -static long populate_offset; -static long *keys; - -static size_t resize_min; -static size_t resize_max; -static struct thread_info *rz_info; -static unsigned long resize_delay = 1000; -static double resize_rate; /* 0.0 to 1.0 */ -static unsigned int n_rz_threads = 1; -static QemuThread *rz_threads; -static bool precompute_hash; - -static double update_rate; /* 0.0 to 1.0 */ -static uint64_t update_threshold; -static uint64_t resize_threshold; - -static size_t qht_n_elems = DEFAULT_QHT_N_ELEMS; -static int qht_mode; - -static bool test_start; -static bool test_stop; - -static struct thread_info *rw_info; - -static const char commands_string[] = - " -d = duration, in seconds\n" - " -n = number of threads\n" - "\n" - " -o = offset at which keys start\n" - " -p = precompute hashes\n" - "\n" - " -g = set -s,-k,-K,-l,-r to the same value\n" - " -s = initial size hint\n" - " -k = initial number of keys\n" - " -K = initial range of keys (will be rounded up to pow2)\n" - " -l = lookup range of keys (will be rounded up to pow2)\n" - " -r = update range of keys (will be rounded up to pow2)\n" - "\n" - " -u = update rate (0.0 to 100.0), 50/50 split of insertions/removals\n" - "\n" - " -R = enable auto-resize\n" - " -S = resize rate (0.0 to 100.0)\n" - " -D = delay (in us) between potential resizes\n" - " -N = number of resize threads"; - -static void usage_complete(int argc, char *argv[]) -{ - fprintf(stderr, "Usage: %s [options]\n", argv[0]); - fprintf(stderr, "options:\n%s\n", commands_string); - exit(-1); -} - -static bool is_equal(const void *ap, const void *bp) -{ - const long *a = ap; - const long *b = bp; - - return *a == *b; -} - -static uint32_t h(unsigned long v) -{ - return qemu_xxhash2(v); -} - -static uint32_t hval(unsigned long v) -{ - return v; -} - -static uint32_t (*hfunc)(unsigned long v) = h; - -/* - * From: https://en.wikipedia.org/wiki/Xorshift - * This is faster than rand_r(), and gives us a wider range (RAND_MAX is only - * guaranteed to be >= INT_MAX). - */ -static uint64_t xorshift64star(uint64_t x) -{ - x ^= x >> 12; /* a */ - x ^= x << 25; /* b */ - x ^= x >> 27; /* c */ - return x * UINT64_C(2685821657736338717); -} - -static void do_rz(struct thread_info *info) -{ - struct thread_stats *stats = &info->stats; - uint64_t r = info->seed - 1; - - if (r < resize_threshold) { - size_t size = info->resize_down ? resize_min : resize_max; - bool resized; - - resized = qht_resize(&ht, size); - info->resize_down = !info->resize_down; - - if (resized) { - stats->rz++; - } else { - stats->not_rz++; - } - } - g_usleep(resize_delay); -} - -static void do_rw(struct thread_info *info) -{ - struct thread_stats *stats = &info->stats; - uint64_t r = info->seed - 1; - uint32_t hash; - long *p; - - if (r >= update_threshold) { - bool read; - - p = &keys[r & (lookup_range - 1)]; - hash = hfunc(*p); - read = qht_lookup(&ht, p, hash); - if (read) { - stats->rd++; - } else { - stats->not_rd++; - } - } else { - p = &keys[r & (update_range - 1)]; - hash = hfunc(*p); - if (info->write_op) { - bool written = false; - - if (qht_lookup(&ht, p, hash) == NULL) { - written = qht_insert(&ht, p, hash, NULL); - } - if (written) { - stats->in++; - } else { - stats->not_in++; - } - } else { - bool removed = false; - - if (qht_lookup(&ht, p, hash)) { - removed = qht_remove(&ht, p, hash); - } - if (removed) { - stats->rm++; - } else { - stats->not_rm++; - } - } - info->write_op = !info->write_op; - } -} - -static void *thread_func(void *p) -{ - struct thread_info *info = p; - - rcu_register_thread(); - - qatomic_inc(&n_ready_threads); - while (!qatomic_read(&test_start)) { - cpu_relax(); - } - - rcu_read_lock(); - while (!qatomic_read(&test_stop)) { - info->seed = xorshift64star(info->seed); - info->func(info); - } - rcu_read_unlock(); - - rcu_unregister_thread(); - return NULL; -} - -/* sets everything except info->func */ -static void prepare_thread_info(struct thread_info *info, int i) -{ - /* seed for the RNG; each thread should have a different one */ - info->seed = (i + 1) ^ time(NULL); - /* the first update will be a write */ - info->write_op = true; - /* the first resize will be down */ - info->resize_down = true; - - memset(&info->stats, 0, sizeof(info->stats)); -} - -static void -th_create_n(QemuThread **threads, struct thread_info **infos, const char *name, - void (*func)(struct thread_info *), int offset, int n) -{ - struct thread_info *info; - QemuThread *th; - int i; - - th = g_malloc(sizeof(*th) * n); - *threads = th; - - info = qemu_memalign(64, sizeof(*info) * n); - *infos = info; - - for (i = 0; i < n; i++) { - prepare_thread_info(&info[i], offset + i); - info[i].func = func; - qemu_thread_create(&th[i], name, thread_func, &info[i], - QEMU_THREAD_JOINABLE); - } -} - -static void create_threads(void) -{ - th_create_n(&rw_threads, &rw_info, "rw", do_rw, 0, n_rw_threads); - th_create_n(&rz_threads, &rz_info, "rz", do_rz, n_rw_threads, n_rz_threads); -} - -static void pr_params(void) -{ - printf("Parameters:\n"); - printf(" duration: %d s\n", duration); - printf(" # of threads: %u\n", n_rw_threads); - printf(" initial # of keys: %zu\n", init_size); - printf(" initial size hint: %zu\n", qht_n_elems); - printf(" auto-resize: %s\n", - qht_mode & QHT_MODE_AUTO_RESIZE ? "on" : "off"); - if (resize_rate) { - printf(" resize_rate: %f%%\n", resize_rate * 100.0); - printf(" resize range: %zu-%zu\n", resize_min, resize_max); - printf(" # resize threads %u\n", n_rz_threads); - } - printf(" update rate: %f%%\n", update_rate * 100.0); - printf(" offset: %ld\n", populate_offset); - printf(" initial key range: %zu\n", init_range); - printf(" lookup range: %lu\n", lookup_range); - printf(" update range: %lu\n", update_range); -} - -static void do_threshold(double rate, uint64_t *threshold) -{ - /* - * For 0 <= rate <= 1, scale to fit in a uint64_t. - * - * Scale by 2**64, with a special case for 1.0. - * The remainder of the possible values are scattered between 0 - * and 0xfffffffffffff800 (nextafter(0x1p64, 0)). - * - * Note that we cannot simply scale by UINT64_MAX, because that - * value is not representable as an IEEE double value. - * - * If we scale by the next largest value, nextafter(0x1p64, 0), - * then the remainder of the possible values are scattered between - * 0 and 0xfffffffffffff000. Which leaves us with a gap between - * the final two inputs that is twice as large as any other. - */ - if (rate == 1.0) { - *threshold = UINT64_MAX; - } else { - *threshold = rate * 0x1p64; - } -} - -static void htable_init(void) -{ - unsigned long n = MAX(init_range, update_range); - uint64_t r = time(NULL); - size_t retries = 0; - size_t i; - - /* avoid allocating memory later by allocating all the keys now */ - keys = g_malloc(sizeof(*keys) * n); - for (i = 0; i < n; i++) { - long val = populate_offset + i; - - keys[i] = precompute_hash ? h(val) : hval(val); - } - - /* some sanity checks */ - g_assert_cmpuint(lookup_range, <=, n); - - /* compute thresholds */ - do_threshold(update_rate, &update_threshold); - do_threshold(resize_rate, &resize_threshold); - - if (resize_rate) { - resize_min = n / 2; - resize_max = n; - assert(resize_min < resize_max); - } else { - n_rz_threads = 0; - } - - /* initialize the hash table */ - qht_init(&ht, is_equal, qht_n_elems, qht_mode); - assert(init_size <= init_range); - - pr_params(); - - fprintf(stderr, "Initialization: populating %zu items...", init_size); - for (i = 0; i < init_size; i++) { - for (;;) { - uint32_t hash; - long *p; - - r = xorshift64star(r); - p = &keys[r & (init_range - 1)]; - hash = hfunc(*p); - if (qht_insert(&ht, p, hash, NULL)) { - break; - } - retries++; - } - } - fprintf(stderr, " populated after %zu retries\n", retries); -} - -static void add_stats(struct thread_stats *s, struct thread_info *info, int n) -{ - int i; - - for (i = 0; i < n; i++) { - struct thread_stats *stats = &info[i].stats; - - s->rd += stats->rd; - s->not_rd += stats->not_rd; - - s->in += stats->in; - s->not_in += stats->not_in; - - s->rm += stats->rm; - s->not_rm += stats->not_rm; - - s->rz += stats->rz; - s->not_rz += stats->not_rz; - } -} - -static void pr_stats(void) -{ - struct thread_stats s = {}; - double tx; - - add_stats(&s, rw_info, n_rw_threads); - add_stats(&s, rz_info, n_rz_threads); - - printf("Results:\n"); - - if (resize_rate) { - printf(" Resizes: %zu (%.2f%% of %zu)\n", - s.rz, (double)s.rz / (s.rz + s.not_rz) * 100, s.rz + s.not_rz); - } - - printf(" Read: %.2f M (%.2f%% of %.2fM)\n", - (double)s.rd / 1e6, - (double)s.rd / (s.rd + s.not_rd) * 100, - (double)(s.rd + s.not_rd) / 1e6); - printf(" Inserted: %.2f M (%.2f%% of %.2fM)\n", - (double)s.in / 1e6, - (double)s.in / (s.in + s.not_in) * 100, - (double)(s.in + s.not_in) / 1e6); - printf(" Removed: %.2f M (%.2f%% of %.2fM)\n", - (double)s.rm / 1e6, - (double)s.rm / (s.rm + s.not_rm) * 100, - (double)(s.rm + s.not_rm) / 1e6); - - tx = (s.rd + s.not_rd + s.in + s.not_in + s.rm + s.not_rm) / 1e6 / duration; - printf(" Throughput: %.2f MT/s\n", tx); - printf(" Throughput/thread: %.2f MT/s/thread\n", tx / n_rw_threads); -} - -static void run_test(void) -{ - int i; - - while (qatomic_read(&n_ready_threads) != n_rw_threads + n_rz_threads) { - cpu_relax(); - } - - qatomic_set(&test_start, true); - g_usleep(duration * G_USEC_PER_SEC); - qatomic_set(&test_stop, true); - - for (i = 0; i < n_rw_threads; i++) { - qemu_thread_join(&rw_threads[i]); - } - for (i = 0; i < n_rz_threads; i++) { - qemu_thread_join(&rz_threads[i]); - } -} - -static void parse_args(int argc, char *argv[]) -{ - int c; - - for (;;) { - c = getopt(argc, argv, "d:D:g:k:K:l:hn:N:o:pr:Rs:S:u:"); - if (c < 0) { - break; - } - switch (c) { - case 'd': - duration = atoi(optarg); - break; - case 'D': - resize_delay = atol(optarg); - break; - case 'g': - init_range = pow2ceil(atol(optarg)); - lookup_range = pow2ceil(atol(optarg)); - update_range = pow2ceil(atol(optarg)); - qht_n_elems = atol(optarg); - init_size = atol(optarg); - break; - case 'h': - usage_complete(argc, argv); - exit(0); - case 'k': - init_size = atol(optarg); - break; - case 'K': - init_range = pow2ceil(atol(optarg)); - break; - case 'l': - lookup_range = pow2ceil(atol(optarg)); - break; - case 'n': - n_rw_threads = atoi(optarg); - break; - case 'N': - n_rz_threads = atoi(optarg); - break; - case 'o': - populate_offset = atol(optarg); - break; - case 'p': - precompute_hash = true; - hfunc = hval; - break; - case 'r': - update_range = pow2ceil(atol(optarg)); - break; - case 'R': - qht_mode |= QHT_MODE_AUTO_RESIZE; - break; - case 's': - qht_n_elems = atol(optarg); - break; - case 'S': - resize_rate = atof(optarg) / 100.0; - if (resize_rate > 1.0) { - resize_rate = 1.0; - } - break; - case 'u': - update_rate = atof(optarg) / 100.0; - if (update_rate > 1.0) { - update_rate = 1.0; - } - break; - } - } -} - -int main(int argc, char *argv[]) -{ - parse_args(argc, argv); - htable_init(); - create_threads(); - run_test(); - pr_stats(); - return 0; -} -- cgit v1.2.3-55-g7522 From 8e19c0098c290e2dea060eec113c4bf104b1f82c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 10 Mar 2021 17:46:12 +0100 Subject: tests: remove "make check-speed" in favor of "make bench" "make check-speed" has been broken since the removal of ninja2make last October. It was just a backwards-compatibility alias for "make bench-speed", which in turn is in principle a subset of "make bench". Advertise the latter and drop "make check-speed" completely since no one has noticed. Reported-by: Thomas Huth Signed-off-by: Paolo Bonzini Message-Id: <20210310164612.285362-1-pbonzini@redhat.com> Reviewed-by: Thomas Huth Reviewed-by: Willian Rampazzo Signed-off-by: Thomas Huth --- tests/Makefile.include | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'tests') diff --git a/tests/Makefile.include b/tests/Makefile.include index 799e47169c..8f220e15d1 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -3,13 +3,13 @@ .PHONY: check-help check-help: @echo "Regression testing targets:" - @echo @echo " $(MAKE) check Run block, qapi-schema, unit, softfloat, qtest and decodetree tests" + @echo " $(MAKE) bench Run speed tests" @echo + @echo "Individual test suites:" @echo " $(MAKE) check-qtest-TARGET Run qtest tests for given target" @echo " $(MAKE) check-qtest Run qtest tests" @echo " $(MAKE) check-unit Run qobject tests" - @echo " $(MAKE) check-speed Run qobject speed tests" @echo " $(MAKE) check-qapi-schema Run QAPI schema tests" @echo " $(MAKE) check-block Run block tests" ifneq ($(filter $(all-check-targets), check-softfloat),) @@ -155,8 +155,4 @@ check-clean: clean: check-clean -# For backwards compatibility - -check-speed: bench-speed - endif -- cgit v1.2.3-55-g7522