]> git.proxmox.com Git - mirror_qemu.git/commitdiff
tests: Move unit tests into a separate directory
authorThomas Huth <thuth@redhat.com>
Wed, 10 Mar 2021 06:33:14 +0000 (07:33 +0100)
committerThomas Huth <thuth@redhat.com>
Fri, 12 Mar 2021 14:46:30 +0000 (15:46 +0100)
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 <pbonzini@redhat.com>
Signed-off-by: Thomas Huth <thuth@redhat.com>
209 files changed:
MAINTAINERS
tests/check-block-qdict.c [deleted file]
tests/check-qdict.c [deleted file]
tests/check-qjson.c [deleted file]
tests/check-qlist.c [deleted file]
tests/check-qlit.c [deleted file]
tests/check-qnull.c [deleted file]
tests/check-qnum.c [deleted file]
tests/check-qobject.c [deleted file]
tests/check-qom-interface.c [deleted file]
tests/check-qom-proplist.c [deleted file]
tests/check-qstring.c [deleted file]
tests/crypto-tls-psk-helpers.c [deleted file]
tests/crypto-tls-psk-helpers.h [deleted file]
tests/crypto-tls-x509-helpers.c [deleted file]
tests/crypto-tls-x509-helpers.h [deleted file]
tests/io-channel-helpers.c [deleted file]
tests/io-channel-helpers.h [deleted file]
tests/iothread.c [deleted file]
tests/iothread.h [deleted file]
tests/meson.build
tests/pkix_asn1_tab.c [deleted file]
tests/ptimer-test-stubs.c [deleted file]
tests/ptimer-test.c [deleted file]
tests/ptimer-test.h [deleted file]
tests/rcutorture.c [deleted file]
tests/socket-helpers.c [deleted file]
tests/socket-helpers.h [deleted file]
tests/test-aio-multithread.c [deleted file]
tests/test-aio.c [deleted file]
tests/test-authz-list.c [deleted file]
tests/test-authz-listfile.c [deleted file]
tests/test-authz-pam.c [deleted file]
tests/test-authz-simple.c [deleted file]
tests/test-base64.c [deleted file]
tests/test-bdrv-drain.c [deleted file]
tests/test-bdrv-graph-mod.c [deleted file]
tests/test-bitcnt.c [deleted file]
tests/test-bitmap.c [deleted file]
tests/test-bitops.c [deleted file]
tests/test-block-backend.c [deleted file]
tests/test-block-iothread.c [deleted file]
tests/test-blockjob-txn.c [deleted file]
tests/test-blockjob.c [deleted file]
tests/test-bufferiszero.c [deleted file]
tests/test-char.c [deleted file]
tests/test-clone-visitor.c [deleted file]
tests/test-coroutine.c [deleted file]
tests/test-crypto-afsplit.c [deleted file]
tests/test-crypto-block.c [deleted file]
tests/test-crypto-cipher.c [deleted file]
tests/test-crypto-hash.c [deleted file]
tests/test-crypto-hmac.c [deleted file]
tests/test-crypto-ivgen.c [deleted file]
tests/test-crypto-pbkdf.c [deleted file]
tests/test-crypto-secret.c [deleted file]
tests/test-crypto-tlscredsx509.c [deleted file]
tests/test-crypto-tlssession.c [deleted file]
tests/test-crypto-xts.c [deleted file]
tests/test-cutils.c [deleted file]
tests/test-fdmon-epoll.c [deleted file]
tests/test-hbitmap.c [deleted file]
tests/test-image-locking.c [deleted file]
tests/test-int128.c [deleted file]
tests/test-io-channel-buffer.c [deleted file]
tests/test-io-channel-command.c [deleted file]
tests/test-io-channel-file.c [deleted file]
tests/test-io-channel-socket.c [deleted file]
tests/test-io-channel-tls.c [deleted file]
tests/test-io-task.c [deleted file]
tests/test-iov.c [deleted file]
tests/test-keyval.c [deleted file]
tests/test-logging.c [deleted file]
tests/test-mul64.c [deleted file]
tests/test-opts-visitor.c [deleted file]
tests/test-qapi-util.c [deleted file]
tests/test-qdev-global-props.c [deleted file]
tests/test-qdist.c [deleted file]
tests/test-qemu-opts.c [deleted file]
tests/test-qga.c [deleted file]
tests/test-qgraph.c [deleted file]
tests/test-qht.c [deleted file]
tests/test-qmp-cmds.c [deleted file]
tests/test-qmp-event.c [deleted file]
tests/test-qobject-input-visitor.c [deleted file]
tests/test-qobject-output-visitor.c [deleted file]
tests/test-rcu-list.c [deleted file]
tests/test-rcu-simpleq.c [deleted file]
tests/test-rcu-slist.c [deleted file]
tests/test-rcu-tailq.c [deleted file]
tests/test-replication.c [deleted file]
tests/test-shift128.c [deleted file]
tests/test-string-input-visitor.c [deleted file]
tests/test-string-output-visitor.c [deleted file]
tests/test-thread-pool.c [deleted file]
tests/test-throttle.c [deleted file]
tests/test-timed-average.c [deleted file]
tests/test-util-filemonitor.c [deleted file]
tests/test-util-sockets.c [deleted file]
tests/test-uuid.c [deleted file]
tests/test-visitor-serialization.c [deleted file]
tests/test-vmstate.c [deleted file]
tests/test-write-threshold.c [deleted file]
tests/test-x86-cpuid.c [deleted file]
tests/test-xbzrle.c [deleted file]
tests/unit/check-block-qdict.c [new file with mode: 0644]
tests/unit/check-qdict.c [new file with mode: 0644]
tests/unit/check-qjson.c [new file with mode: 0644]
tests/unit/check-qlist.c [new file with mode: 0644]
tests/unit/check-qlit.c [new file with mode: 0644]
tests/unit/check-qnull.c [new file with mode: 0644]
tests/unit/check-qnum.c [new file with mode: 0644]
tests/unit/check-qobject.c [new file with mode: 0644]
tests/unit/check-qom-interface.c [new file with mode: 0644]
tests/unit/check-qom-proplist.c [new file with mode: 0644]
tests/unit/check-qstring.c [new file with mode: 0644]
tests/unit/crypto-tls-psk-helpers.c [new file with mode: 0644]
tests/unit/crypto-tls-psk-helpers.h [new file with mode: 0644]
tests/unit/crypto-tls-x509-helpers.c [new file with mode: 0644]
tests/unit/crypto-tls-x509-helpers.h [new file with mode: 0644]
tests/unit/io-channel-helpers.c [new file with mode: 0644]
tests/unit/io-channel-helpers.h [new file with mode: 0644]
tests/unit/iothread.c [new file with mode: 0644]
tests/unit/iothread.h [new file with mode: 0644]
tests/unit/meson.build [new file with mode: 0644]
tests/unit/pkix_asn1_tab.c [new file with mode: 0644]
tests/unit/ptimer-test-stubs.c [new file with mode: 0644]
tests/unit/ptimer-test.c [new file with mode: 0644]
tests/unit/ptimer-test.h [new file with mode: 0644]
tests/unit/rcutorture.c [new file with mode: 0644]
tests/unit/socket-helpers.c [new file with mode: 0644]
tests/unit/socket-helpers.h [new file with mode: 0644]
tests/unit/test-aio-multithread.c [new file with mode: 0644]
tests/unit/test-aio.c [new file with mode: 0644]
tests/unit/test-authz-list.c [new file with mode: 0644]
tests/unit/test-authz-listfile.c [new file with mode: 0644]
tests/unit/test-authz-pam.c [new file with mode: 0644]
tests/unit/test-authz-simple.c [new file with mode: 0644]
tests/unit/test-base64.c [new file with mode: 0644]
tests/unit/test-bdrv-drain.c [new file with mode: 0644]
tests/unit/test-bdrv-graph-mod.c [new file with mode: 0644]
tests/unit/test-bitcnt.c [new file with mode: 0644]
tests/unit/test-bitmap.c [new file with mode: 0644]
tests/unit/test-bitops.c [new file with mode: 0644]
tests/unit/test-block-backend.c [new file with mode: 0644]
tests/unit/test-block-iothread.c [new file with mode: 0644]
tests/unit/test-blockjob-txn.c [new file with mode: 0644]
tests/unit/test-blockjob.c [new file with mode: 0644]
tests/unit/test-bufferiszero.c [new file with mode: 0644]
tests/unit/test-char.c [new file with mode: 0644]
tests/unit/test-clone-visitor.c [new file with mode: 0644]
tests/unit/test-coroutine.c [new file with mode: 0644]
tests/unit/test-crypto-afsplit.c [new file with mode: 0644]
tests/unit/test-crypto-block.c [new file with mode: 0644]
tests/unit/test-crypto-cipher.c [new file with mode: 0644]
tests/unit/test-crypto-hash.c [new file with mode: 0644]
tests/unit/test-crypto-hmac.c [new file with mode: 0644]
tests/unit/test-crypto-ivgen.c [new file with mode: 0644]
tests/unit/test-crypto-pbkdf.c [new file with mode: 0644]
tests/unit/test-crypto-secret.c [new file with mode: 0644]
tests/unit/test-crypto-tlscredsx509.c [new file with mode: 0644]
tests/unit/test-crypto-tlssession.c [new file with mode: 0644]
tests/unit/test-crypto-xts.c [new file with mode: 0644]
tests/unit/test-cutils.c [new file with mode: 0644]
tests/unit/test-fdmon-epoll.c [new file with mode: 0644]
tests/unit/test-hbitmap.c [new file with mode: 0644]
tests/unit/test-image-locking.c [new file with mode: 0644]
tests/unit/test-int128.c [new file with mode: 0644]
tests/unit/test-io-channel-buffer.c [new file with mode: 0644]
tests/unit/test-io-channel-command.c [new file with mode: 0644]
tests/unit/test-io-channel-file.c [new file with mode: 0644]
tests/unit/test-io-channel-socket.c [new file with mode: 0644]
tests/unit/test-io-channel-tls.c [new file with mode: 0644]
tests/unit/test-io-task.c [new file with mode: 0644]
tests/unit/test-iov.c [new file with mode: 0644]
tests/unit/test-keyval.c [new file with mode: 0644]
tests/unit/test-logging.c [new file with mode: 0644]
tests/unit/test-mul64.c [new file with mode: 0644]
tests/unit/test-opts-visitor.c [new file with mode: 0644]
tests/unit/test-qapi-util.c [new file with mode: 0644]
tests/unit/test-qdev-global-props.c [new file with mode: 0644]
tests/unit/test-qdist.c [new file with mode: 0644]
tests/unit/test-qemu-opts.c [new file with mode: 0644]
tests/unit/test-qga.c [new file with mode: 0644]
tests/unit/test-qgraph.c [new file with mode: 0644]
tests/unit/test-qht.c [new file with mode: 0644]
tests/unit/test-qmp-cmds.c [new file with mode: 0644]
tests/unit/test-qmp-event.c [new file with mode: 0644]
tests/unit/test-qobject-input-visitor.c [new file with mode: 0644]
tests/unit/test-qobject-output-visitor.c [new file with mode: 0644]
tests/unit/test-rcu-list.c [new file with mode: 0644]
tests/unit/test-rcu-simpleq.c [new file with mode: 0644]
tests/unit/test-rcu-slist.c [new file with mode: 0644]
tests/unit/test-rcu-tailq.c [new file with mode: 0644]
tests/unit/test-replication.c [new file with mode: 0644]
tests/unit/test-shift128.c [new file with mode: 0644]
tests/unit/test-string-input-visitor.c [new file with mode: 0644]
tests/unit/test-string-output-visitor.c [new file with mode: 0644]
tests/unit/test-thread-pool.c [new file with mode: 0644]
tests/unit/test-throttle.c [new file with mode: 0644]
tests/unit/test-timed-average.c [new file with mode: 0644]
tests/unit/test-util-filemonitor.c [new file with mode: 0644]
tests/unit/test-util-sockets.c [new file with mode: 0644]
tests/unit/test-uuid.c [new file with mode: 0644]
tests/unit/test-visitor-serialization.c [new file with mode: 0644]
tests/unit/test-vmstate.c [new file with mode: 0644]
tests/unit/test-write-threshold.c [new file with mode: 0644]
tests/unit/test-x86-cpuid.c [new file with mode: 0644]
tests/unit/test-xbzrle.c [new file with mode: 0644]

index e04ae21a2f04a3a875c416f7b73b7f6ebf988bbd..e6c43c683333f82dc43e854af099da6e89258283 100644 (file)
@@ -1537,7 +1537,7 @@ F: include/hw/southbridge/piix.h
 F: hw/misc/sga.c
 F: hw/isa/apm.c
 F: include/hw/isa/apm.h
-F: tests/test-x86-cpuid.c
+F: tests/unit/test-x86-cpuid.c
 F: tests/qtest/test-x86-cpuid-compat.c
 
 PC Chipset
@@ -2212,7 +2212,7 @@ F: qemu-io*
 F: tests/qemu-iotests/
 F: util/qemu-progress.c
 F: qobject/block-qdict.c
-F: tests/check-block-qdict.c
+F: tests/unit/check-block-qdict.c
 T: git https://repo.or.cz/qemu/kevin.git block
 
 Storage daemon
@@ -2238,7 +2238,7 @@ F: migration/block*
 F: include/block/aio.h
 F: include/block/aio-wait.h
 F: scripts/qemugdb/aio.py
-F: tests/test-fdmon-epoll.c
+F: tests/unit/test-fdmon-epoll.c
 T: git https://github.com/stefanha/qemu.git block
 
 Block SCSI subsystem
@@ -2298,7 +2298,7 @@ F: block/dirty-bitmap.c
 F: block/qcow2-bitmap.c
 F: migration/block-dirty-bitmap.c
 F: util/hbitmap.c
-F: tests/test-hbitmap.c
+F: tests/unit/test-hbitmap.c
 F: docs/interop/bitmaps.rst
 T: git https://repo.or.cz/qemu/ericb.git bitmaps
 
@@ -2319,8 +2319,8 @@ Command line option argument parsing
 M: Markus Armbruster <armbru@redhat.com>
 S: Supported
 F: include/qemu/option.h
-F: tests/test-keyval.c
-F: tests/test-qemu-opts.c
+F: tests/unit/test-keyval.c
+F: tests/unit/test-qemu-opts.c
 F: util/keyval.c
 F: util/qemu-option.c
 
@@ -2438,8 +2438,8 @@ Read, Copy, Update (RCU)
 M: Paolo Bonzini <pbonzini@redhat.com>
 S: Maintained
 F: include/qemu/rcu*.h
-F: tests/rcutorture.c
-F: tests/test-rcu-*.c
+F: tests/unit/rcutorture.c
+F: tests/unit/test-rcu-*.c
 F: util/rcu.c
 
 Human Monitor (HMP)
@@ -2517,10 +2517,10 @@ F: include/qapi/
 X: include/qapi/qmp/
 F: include/qapi/qmp/dispatch.h
 F: tests/qapi-schema/
-F: tests/test-*-visitor.c
-F: tests/test-qapi-*.c
-F: tests/test-qmp-*.c
-F: tests/test-visitor-serialization.c
+F: tests/unit/test-*-visitor.c
+F: tests/unit/test-qapi-*.c
+F: tests/unit/test-qmp-*.c
+F: tests/unit/test-visitor-serialization.c
 F: scripts/qapi-gen.py
 F: scripts/qapi/*
 F: docs/sphinx/qapidoc.py
@@ -2541,14 +2541,14 @@ F: qobject/
 F: include/qapi/qmp/
 X: include/qapi/qmp/dispatch.h
 F: scripts/coccinelle/qobject.cocci
-F: tests/check-qdict.c
-F: tests/check-qjson.c
-F: tests/check-qlist.c
-F: tests/check-qlit.c
-F: tests/check-qnull.c
-F: tests/check-qnum.c
-F: tests/check-qobject.c
-F: tests/check-qstring.c
+F: tests/unit/check-qdict.c
+F: tests/unit/check-qjson.c
+F: tests/unit/check-qlist.c
+F: tests/unit/check-qlit.c
+F: tests/unit/check-qnull.c
+F: tests/unit/check-qnum.c
+F: tests/unit/check-qobject.c
+F: tests/unit/check-qstring.c
 F: tests/data/qobject/qdict.txt
 T: git https://repo.or.cz/qemu/armbru.git qapi-next
 
@@ -2559,7 +2559,7 @@ F: qga/
 F: docs/interop/qemu-ga.rst
 F: docs/interop/qemu-ga-ref.rst
 F: scripts/qemu-guest-agent/
-F: tests/test-qga.c
+F: tests/unit/test-qga.c
 T: git https://github.com/mdroth/qemu.git qga
 
 QOM
@@ -2580,9 +2580,9 @@ F: scripts/coccinelle/qom-parent-type.cocci
 F: softmmu/qdev-monitor.c
 F: stubs/qdev.c
 F: qom/
-F: tests/check-qom-interface.c
-F: tests/check-qom-proplist.c
-F: tests/test-qdev-global-props.c
+F: tests/unit/check-qom-interface.c
+F: tests/unit/check-qom-proplist.c
+F: tests/unit/test-qdev-global-props.c
 
 QOM boilerplate conversion script
 M: Eduardo Habkost <ehabkost@redhat.com>
@@ -2721,10 +2721,10 @@ S: Maintained
 F: crypto/
 F: include/crypto/
 F: qapi/crypto.json
-F: tests/test-crypto-*
+F: tests/unit/test-crypto-*
 F: tests/benchmark-crypto-*
-F: tests/crypto-tls-*
-F: tests/pkix_asn1_tab.c
+F: tests/unit/crypto-tls-*
+F: tests/unit/pkix_asn1_tab.c
 F: qemu.sasl
 
 Coroutines
@@ -2733,7 +2733,7 @@ M: Kevin Wolf <kwolf@redhat.com>
 S: Maintained
 F: util/*coroutine*
 F: include/qemu/coroutine*
-F: tests/test-coroutine.c
+F: tests/unit/test-coroutine.c
 
 Buffers
 M: Daniel P. Berrange <berrange@redhat.com>
@@ -2746,7 +2746,7 @@ M: Daniel P. Berrange <berrange@redhat.com>
 S: Maintained
 F: io/
 F: include/io/
-F: tests/test-io-*
+F: tests/unit/test-io-*
 
 User authorization
 M: Daniel P. Berrange <berrange@redhat.com>
@@ -2754,7 +2754,7 @@ S: Maintained
 F: authz/
 F: qapi/authz.json
 F: include/authz/
-F: tests/test-authz-*
+F: tests/unit/test-authz-*
 
 Sockets
 M: Daniel P. Berrange <berrange@redhat.com>
@@ -2769,7 +2769,7 @@ M: Daniel P. Berrange <berrange@redhat.com>
 S: Odd Fixes
 F: util/filemonitor*.c
 F: include/qemu/filemonitor.h
-F: tests/test-util-filemonitor.c
+F: tests/unit/test-util-filemonitor.c
 
 Throttling infrastructure
 M: Alberto Garcia <berto@igalia.com>
@@ -2779,7 +2779,7 @@ F: include/block/throttle-groups.h
 F: include/qemu/throttle*.h
 F: util/throttle.c
 F: docs/throttle.txt
-F: tests/test-throttle.c
+F: tests/unit/test-throttle.c
 L: qemu-block@nongnu.org
 
 UUID
@@ -2787,7 +2787,7 @@ M: Fam Zheng <fam@euphon.net>
 S: Supported
 F: util/uuid.c
 F: include/qemu/uuid.h
-F: tests/test-uuid.c
+F: tests/unit/test-uuid.c
 
 Yank feature
 M: Lukas Straub <lukasstraub2@web.de>
@@ -3237,7 +3237,7 @@ M: Xie Changlong <xiechanglong.d@gmail.com>
 S: Supported
 F: replication*
 F: block/replication.c
-F: tests/test-replication.c
+F: tests/unit/test-replication.c
 F: docs/block-replication.txt
 
 PVRDMA
diff --git a/tests/check-block-qdict.c b/tests/check-block-qdict.c
deleted file mode 100644 (file)
index 5a25825..0000000
+++ /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 (file)
index b5efa85..0000000
+++ /dev/null
@@ -1,379 +0,0 @@
-/*
- * QDict unit-tests.
- *
- * Copyright (C) 2009 Red Hat Inc.
- *
- * Authors:
- *  Luiz Capitulino <lcapitulino@redhat.com>
- *
- * 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 (file)
index c845f91..0000000
+++ /dev/null
@@ -1,1518 +0,0 @@
-/*
- * Copyright IBM, Corp. 2009
- * Copyright (c) 2013, 2015 Red Hat Inc.
- *
- * Authors:
- *  Anthony Liguori   <aliguori@us.ibm.com>
- *  Markus Armbruster <armbru@redhat.com>
- *
- * 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 (file)
index 3cd0ccb..0000000
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * QList unit-tests.
- *
- * Copyright (C) 2009 Red Hat Inc.
- *
- * Authors:
- *  Luiz Capitulino <lcapitulino@redhat.com>
- *
- * 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 (file)
index bd6798d..0000000
+++ /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 (file)
index ebf21db..0000000
+++ /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 (file)
index b85fca2..0000000
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- * QNum unit-tests.
- *
- * Copyright (C) 2009 Red Hat Inc.
- * Copyright IBM, Corp. 2009
- *
- * Authors:
- *  Luiz Capitulino <lcapitulino@redhat.com>
- *  Anthony Liguori <aliguori@us.ibm.com>
- *
- * 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 (file)
index c1713d1..0000000
+++ /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 <math.h>
-
-/* 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 (file)
index c99be97..0000000
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * QOM interface test.
- *
- * Copyright (C) 2013 Red Hat Inc.
- *
- * Authors:
- *  Igor Mammedov <imammedo@redhat.com>
- *
- * 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 (file)
index 1b76581..0000000
+++ /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
- * <http://www.gnu.org/licenses/>.
- *
- * Author: Daniel P. Berrange <berrange@redhat.com>
- */
-
-#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 (file)
index 4bf9772..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * QString unit-tests.
- *
- * Copyright (C) 2009 Red Hat Inc.
- *
- * Authors:
- *  Luiz Capitulino <lcapitulino@redhat.com>
- *
- * 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 (file)
index a839547..0000000
+++ /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
- * <http://www.gnu.org/licenses/>.
- *
- * Author: Richard W.M. Jones <rjones@redhat.com>
- */
-
-#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 (file)
index 5aa9951..0000000
+++ /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
- * <http://www.gnu.org/licenses/>.
- *
- * Author: Richard W.M. Jones <rjones@redhat.com>
- */
-
-#ifndef TESTS_CRYPTO_TLS_PSK_HELPERS_H
-#define TESTS_CRYPTO_TLS_PSK_HELPERS_H
-
-#include <gnutls/gnutls.h>
-
-#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 (file)
index 9765859..0000000
+++ /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
- * <http://www.gnu.org/licenses/>.
- *
- * Author: Daniel P. Berrange <berrange@redhat.com>
- */
-
-#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 (file)
index 8fcd778..0000000
+++ /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
- * <http://www.gnu.org/licenses/>.
- *
- * Author: Daniel P. Berrange <berrange@redhat.com>
- */
-
-#ifndef TESTS_CRYPTO_TLS_X509_HELPERS_H
-#define TESTS_CRYPTO_TLS_X509_HELPERS_H
-
-#include <gnutls/gnutls.h>
-#include <gnutls/x509.h>
-
-#if !(defined WIN32) && \
-    defined(CONFIG_TASN1)
-# define QCRYPTO_HAVE_TLS_TEST_SUPPORT
-#endif
-
-#ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT
-# include <libtasn1.h>
-
-
-/*
- * 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 (file)
index ff156ed..0000000
+++ /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 <http://www.gnu.org/licenses/>.
- *
- */
-
-#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 (file)
index 3d14043..0000000
+++ /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 <http://www.gnu.org/licenses/>.
- */
-
-#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 (file)
index afde12b..0000000
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Event loop thread implementation for unit tests
- *
- * Copyright Red Hat Inc., 2013, 2016
- *
- * Authors:
- *  Stefan Hajnoczi   <stefanha@redhat.com>
- *  Paolo Bonzini     <pbonzini@redhat.com>
- *
- * 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 (file)
index 4877cea..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Event loop thread implementation for unit tests
- *
- * Copyright Red Hat Inc., 2013, 2016
- *
- * Authors:
- *  Stefan Hajnoczi   <stefanha@redhat.com>
- *  Paolo Bonzini     <pbonzini@redhat.com>
- *
- * 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
index 656d211e2595dd94d9cd79449a79fb520e00d199..af43fd1eaf7ef9144cbcbdfa847ea6dc40b9a5ac 100644 (file)
@@ -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 (file)
index 4aaf736..0000000
+++ /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 (file)
index 7f801a4..0000000
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Stubs for the ptimer-test
- *
- * Copyright (c) 2016 Dmitry Osipenko <digetx@gmail.com>
- *
- * 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 (file)
index 9176b96..0000000
+++ /dev/null
@@ -1,892 +0,0 @@
-/*
- * QTest testcase for the ptimer
- *
- * Copyright (c) 2016 Dmitry Osipenko <digetx@gmail.com>
- *
- * 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 <glib/gprintf.h>
-
-#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 (file)
index 09ac56d..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * QTest testcase for the ptimer
- *
- * Copyright (c) 2016 Dmitry Osipenko <digetx@gmail.com>
- *
- * 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 (file)
index de6f649..0000000
+++ /dev/null
@@ -1,483 +0,0 @@
-/*
- * rcutorture.c: simple user-level performance/stress test of RCU.
- *
- * Usage:
- *     ./rcu <nreaders> rperf [ <seconds> ]
- *         Run a read-side performance test with the specified
- *         number of readers for <seconds> seconds.
- *     ./rcu <nupdaters> uperf [ <seconds> ]
- *         Run an update-side performance test with the specified
- *         number of updaters and specified duration.
- *     ./rcu <nreaders> perf [ <seconds> ]
- *         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 <nreaders> stress [ <seconds> ]
- *         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 (file)
index f704fd1..0000000
+++ /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 <http://www.gnu.org/licenses/>.
- *
- */
-
-#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 (file)
index 512a004..0000000
+++ /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 <http://www.gnu.org/licenses/>.
- */
-
-#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 (file)
index a555cc8..0000000
+++ /dev/null
@@ -1,460 +0,0 @@
-/*
- * AioContext multithreading tests
- *
- * Copyright Red Hat, Inc. 2016
- *
- * Authors:
- *  Paolo Bonzini    <pbonzini@redhat.com>
- *
- * 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 (file)
index 8a46078..0000000
+++ /dev/null
@@ -1,921 +0,0 @@
-/*
- * AioContext tests
- *
- * Copyright Red Hat, Inc. 2012
- *
- * Authors:
- *  Paolo Bonzini    <pbonzini@redhat.com>
- *
- * 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 (file)
index 5351992..0000000
+++ /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 <http://www.gnu.org/licenses/>.
- *
- */
-
-#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 (file)
index 64d0e15..0000000
+++ /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 <http://www.gnu.org/licenses/>.
- *
- */
-
-#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 (file)
index 4fe1ef2..0000000
+++ /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 <http://www.gnu.org/licenses/>.
- *
- */
-
-#include "qemu/osdep.h"
-#include "qapi/error.h"
-#include "qemu/module.h"
-#include "authz/pamacct.h"
-
-#include <security/pam_appl.h>
-
-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 (file)
index 6f9034d..0000000
+++ /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 <http://www.gnu.org/licenses/>.
- *
- */
-
-#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 (file)
index 3012d7b..0000000
+++ /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 <http://www.gnu.org/licenses/>.
- *
- */
-
-#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 (file)
index 8a29e33..0000000
+++ /dev/null
@@ -1,2230 +0,0 @@
-/*
- * Block node draining tests
- *
- * Copyright (c) 2017 Kevin Wolf <kwolf@redhat.com>
- *
- * 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 (file)
index c4f7d16..0000000
+++ /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 <http://www.gnu.org/licenses/>.
- *
- */
-
-#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 (file)
index e153dcb..0000000
+++ /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 (file)
index 8db4f67..0000000
+++ /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 <peterx@redhat.com>
- */
-
-#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 (file)
index 5a791d2..0000000
+++ /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 (file)
index 2fb1a44..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * BlockBackend tests
- *
- * Copyright (c) 2017 Kevin Wolf <kwolf@redhat.com>
- *
- * 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 (file)
index 3f866a3..0000000
+++ /dev/null
@@ -1,774 +0,0 @@
-/*
- * Block tests for iothreads
- *
- * Copyright (c) 2018 Kevin Wolf <kwolf@redhat.com>
- *
- * 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 (file)
index 8bd13b9..0000000
+++ /dev/null
@@ -1,262 +0,0 @@
-/*
- * Blockjob transactions tests
- *
- * Copyright Red Hat, Inc. 2015
- *
- * Authors:
- *  Stefan Hajnoczi    <stefanha@redhat.com>
- *
- * 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 (file)
index 7519847..0000000
+++ /dev/null
@@ -1,393 +0,0 @@
-/*
- * Blockjob tests
- *
- * Copyright Igalia, S.L. 2016
- *
- * Authors:
- *  Alberto Garcia   <berto@igalia.com>
- *
- * 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 (file)
index e45fd31..0000000
+++ /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 <http://www.gnu.org/licenses/>.
- *
- */
-
-#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 (file)
index 755d54c..0000000
+++ /dev/null
@@ -1,1560 +0,0 @@
-#include "qemu/osdep.h"
-#include <glib/gstdio.h>
-
-#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 (file)
index 4944b3d..0000000
+++ /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 (file)
index e946d93..0000000
+++ /dev/null
@@ -1,512 +0,0 @@
-/*
- * Coroutine tests
- *
- * Copyright IBM, Corp. 2011
- *
- * Authors:
- *  Stefan Hajnoczi    <stefanha@linux.vnet.ibm.com>
- *
- * 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 (file)
index 00a7c18..0000000
+++ /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 <http://www.gnu.org/licenses/>.
- *
- */
-
-#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 (file)
index 3b1f0d5..0000000
+++ /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 <http://www.gnu.org/licenses/>.
- *
- */
-
-#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 <sys/resource.h>
-#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 (file)
index 280319a..0000000
+++ /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 <http://www.gnu.org/licenses/>.
- *
- */
-
-#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 (file)
index ce7d0ab..0000000
+++ /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 <http://www.gnu.org/licenses/>.
- *
- */
-
-#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 (file)
index ee55382..0000000
+++ /dev/null
@@ -1,266 +0,0 @@
-/*
- * QEMU Crypto hmac algorithms tests
- *
- * Copyright (c) 2016 HUAWEI TECHNOLOGIES CO., LTD.
- *
- * Authors:
- *    Longpeng(Mike) <longpeng2@huawei.com>
- *
- * 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 (file)
index f581e6a..0000000
+++ /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 <http://www.gnu.org/licenses/>.
- *
- */
-
-#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 (file)
index c50fd63..0000000
+++ /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 <http://www.gnu.org/licenses/>.
- *
- */
-
-#include "qemu/osdep.h"
-#include "qapi/error.h"
-#include "crypto/init.h"
-#ifndef _WIN32
-#include <sys/resource.h>
-#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 (file)
index 34a4aec..0000000
+++ /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 <http://www.gnu.org/licenses/>.
- *
- */
-
-#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 <keyutils.h>
-#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: <https://www.spinics.net/lists/keyrings/index.html>
- * 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 (file)
index f487349..0000000
+++ /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
- * <http://www.gnu.org/licenses/>.
- *
- * Author: Daniel P. Berrange <berrange@redhat.com>
- */
-
-#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 (file)
index 8b2453f..0000000
+++ /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
- * <http://www.gnu.org/licenses/>.
- *
- * Author: Daniel P. Berrange <berrange@redhat.com>
- */
-
-#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 (file)
index 7acbc95..0000000
+++ /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 <http://www.gnu.org/licenses/>.
- *
- * 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 (file)
index 1aa8351..0000000
+++ /dev/null
@@ -1,2460 +0,0 @@
-/*
- * cutils.c unit-tests
- *
- * Copyright (C) 2013 Red Hat Inc.
- *
- * Authors:
- *  Eduardo Habkost <ehabkost@redhat.com>
- *
- * 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 (file)
index 11fd8a2..0000000
+++ /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(&notifiers[i], false);
-        aio_set_event_notifier(ctx, &notifiers[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, &notifiers[i], false, NULL, NULL);
-        event_notifier_cleanup(&notifiers[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(&notifiers[0]);
-    assert(aio_poll(ctx, true));
-
-    aio_disable_external(ctx);
-    event_notifier_set(&notifiers[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 (file)
index b6726cf..0000000
+++ /dev/null
@@ -1,1119 +0,0 @@
-/*
- * Hierarchical bitmap unit-tests.
- *
- * Copyright (C) 2012 Red Hat Inc.
- *
- * Author: Paolo Bonzini <pbonzini@redhat.com>
- *
- * 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 (file)
index ba057bd..0000000
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Image locking tests
- *
- * Copyright (c) 2018 Red Hat Inc.
- *
- * Author: Fam Zheng <famz@redhat.com>
- *
- * 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 (file)
index b86a3c7..0000000
+++ /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 (file)
index 9c6724d..0000000
+++ /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 <http://www.gnu.org/licenses/>.
- *
- */
-
-#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 (file)
index 99056e0..0000000
+++ /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 <http://www.gnu.org/licenses/>.
- *
- */
-
-#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 (file)
index 29038e6..0000000
+++ /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 <http://www.gnu.org/licenses/>.
- *
- */
-
-#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 (file)
index c49eec1..0000000
+++ /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 <http://www.gnu.org/licenses/>.
- *
- */
-
-#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 (file)
index ad7554c..0000000
+++ /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
- * <http://www.gnu.org/licenses/>.
- *
- * Author: Daniel P. Berrange <berrange@redhat.com>
- */
-
-
-#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 (file)
index 953a50a..0000000
+++ /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 <http://www.gnu.org/licenses/>.
- *
- */
-
-#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 (file)
index 9c415e2..0000000
+++ /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 (file)
index ee927fe..0000000
+++ /dev/null
@@ -1,750 +0,0 @@
-/*
- * Unit tests for parsing of KEY=VALUE,... strings
- *
- * Copyright (C) 2017 Red Hat Inc.
- *
- * Authors:
- *  Markus Armbruster <armbru@redhat.com>,
- *
- * 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 (file)
index ccb819f..0000000
+++ /dev/null
@@ -1,218 +0,0 @@
-/*
- * logging unit-tests
- *
- * Copyright (C) 2016 Linaro Ltd.
- *
- *  Author: Alex Bennée <alex.bennee@linaro.org>
- *
- * 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 <glib/gstdio.h>
-
-#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 (file)
index 9be775d..0000000
+++ /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 (file)
index 23e8970..0000000
+++ /dev/null
@@ -1,371 +0,0 @@
-/*
- * Options Visitor unit-tests.
- *
- * Copyright (C) 2013 Red Hat, Inc.
- *
- * Authors:
- *   Laszlo Ersek <lersek@redhat.com> (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
-     * <http://en.wikipedia.org/wiki/Magic_number_%28programming%29>, 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 (file)
index 847f305..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Unit tests for QAPI utility functions
- *
- * Copyright (C) 2017 Red Hat Inc.
- *
- * Authors:
- *  Markus Armbruster <armbru@redhat.com>,
- *
- * 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 (file)
index c8862ca..0000000
+++ /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 (file)
index 9541ce3..0000000
+++ /dev/null
@@ -1,389 +0,0 @@
-/*
- * Copyright (C) 2016, Emilio G. Cota <cota@braap.org>
- *
- * 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 <math.h>
-
-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 (file)
index 8bbb17b..0000000
+++ /dev/null
@@ -1,1046 +0,0 @@
-/*
- * QemuOpts unit-tests.
- *
- * Copyright (C) 2014 Leandro Dorileo <l@dorileo.org>
- *
- * 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 (file)
index eb33264..0000000
+++ /dev/null
@@ -1,1019 +0,0 @@
-#include "qemu/osdep.h"
-#include <locale.h>
-#include <glib/gstdio.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-
-#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 (file)
index ae2f7b2..0000000
+++ /dev/null
@@ -1,434 +0,0 @@
-/*
- * libqos driver framework
- *
- * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
- *
- * 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 <http://www.gnu.org/licenses/>
- */
-
-#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 (file)
index 4d23cef..0000000
+++ /dev/null
@@ -1,260 +0,0 @@
-/*
- * Copyright (C) 2016, Emilio G. Cota <cota@braap.org>
- *
- * 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 (file)
index d3413bf..0000000
+++ /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 (file)
index 7dd0053..0000000
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * qapi event unit-tests.
- *
- * Copyright (c) 2014 Wenchao Xia
- *
- * Authors:
- *  Wenchao Xia   <wenchaoqemu@gmail.com>
- *
- * 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 (file)
index e41b91a..0000000
+++ /dev/null
@@ -1,1379 +0,0 @@
-/*
- * QObject Input Visitor unit-tests.
- *
- * Copyright (C) 2011-2016 Red Hat Inc.
- *
- * Authors:
- *  Luiz Capitulino <lcapitulino@redhat.com>
- *  Paolo Bonzini <pbonzini@redhat.com>
- *
- * 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 (file)
index 9dc1e07..0000000
+++ /dev/null
@@ -1,807 +0,0 @@
-/*
- * QObject Output Visitor unit-tests.
- *
- * Copyright (C) 2011-2016 Red Hat Inc.
- *
- * Authors:
- *  Luiz Capitulino <lcapitulino@redhat.com>
- *
- * 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 (file)
index 49641e1..0000000
+++ /dev/null
@@ -1,381 +0,0 @@
-/*
- * rcuq_test.c
- *
- * usage: rcuq_test <readers> <duration>
- *
- * 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 (file)
index 057f7d3..0000000
+++ /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 (file)
index 868e1e4..0000000
+++ /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 (file)
index 8d487e0..0000000
+++ /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 (file)
index b067240..0000000
+++ /dev/null
@@ -1,623 +0,0 @@
-/*
- * Block replication tests
- *
- * Copyright (c) 2016 FUJITSU LIMITED
- * Author: Changlong Xie <xiecl.fnst@cn.fujitsu.com>
- *
- * 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 (file)
index f3ff736..0000000
+++ /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 (file)
index 249faaf..0000000
+++ /dev/null
@@ -1,501 +0,0 @@
-/*
- * String Input Visitor unit-tests.
- *
- * Copyright (C) 2012 Red Hat Inc.
- *
- * Authors:
- *  Paolo Bonzini <pbonzini@redhat.com> (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 (file)
index e2bedc5..0000000
+++ /dev/null
@@ -1,248 +0,0 @@
-/*
- * String Output Visitor unit-tests.
- *
- * Copyright (C) 2012 Red Hat Inc.
- *
- * Authors:
- *  Paolo Bonzini <pbonzini@redhat.com> (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, ==, "<null>");
-    } 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 (file)
index 70dc631..0000000
+++ /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 (file)
index 7adb5e6..0000000
+++ /dev/null
@@ -1,770 +0,0 @@
-/*
- * Throttle infrastructure tests
- *
- * Copyright Nodalink, EURL. 2013-2014
- * Copyright Igalia, S.L. 2015
- *
- * Authors:
- *  Benoît Canet     <benoit.canet@nodalink.com>
- *  Alberto Garcia   <berto@igalia.com>
- *
- * 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 <math.h>
-#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 (file)
index 82c9250..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Timed average computation tests
- *
- * Copyright Nodalink, EURL. 2014
- *
- * Authors:
- *  Benoît Canet     <benoit.canet@nodalink.com>
- *
- * 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 (file)
index b629e10..0000000
+++ /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 <http://www.gnu.org/licenses/>.
- *
- */
-
-#include "qemu/osdep.h"
-#include "qemu/main-loop.h"
-#include "qapi/error.h"
-#include "qemu/filemonitor.h"
-
-#include <glib/gstdio.h>
-
-#include <utime.h>
-
-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 (file)
index 6748605..0000000
+++ /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 <http://www.gnu.org/licenses/>.
- *
- */
-
-#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 (file)
index c111de5..0000000
+++ /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 <http://www.gnu.org/licenses/>.
- *
- */
-
-#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 (file)
index 4629958..0000000
+++ /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 <mdroth@linux.vnet.ibm.com>
- *
- * 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 <float.h>
-
-#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 (file)
index a001879..0000000
+++ /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 (file)
index fc1c45a..0000000
+++ /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 (file)
index bfabc04..0000000
+++ /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 (file)
index 795d6f1..0000000
+++ /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  <owasserm@redhat.com>
- *
- * 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 (file)
index 0000000..5a25825
--- /dev/null
@@ -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 (file)
index 0000000..b5efa85
--- /dev/null
@@ -0,0 +1,379 @@
+/*
+ * QDict unit-tests.
+ *
+ * Copyright (C) 2009 Red Hat Inc.
+ *
+ * Authors:
+ *  Luiz Capitulino <lcapitulino@redhat.com>
+ *
+ * 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 (file)
index 0000000..c845f91
--- /dev/null
@@ -0,0 +1,1518 @@
+/*
+ * Copyright IBM, Corp. 2009
+ * Copyright (c) 2013, 2015 Red Hat Inc.
+ *
+ * Authors:
+ *  Anthony Liguori   <aliguori@us.ibm.com>
+ *  Markus Armbruster <armbru@redhat.com>
+ *
+ * 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 (file)
index 0000000..3cd0ccb
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * QList unit-tests.
+ *
+ * Copyright (C) 2009 Red Hat Inc.
+ *
+ * Authors:
+ *  Luiz Capitulino <lcapitulino@redhat.com>
+ *
+ * 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 (file)
index 0000000..bd6798d
--- /dev/null
@@ -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 (file)
index 0000000..ebf21db
--- /dev/null
@@ -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 (file)
index 0000000..b85fca2
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * QNum unit-tests.
+ *
+ * Copyright (C) 2009 Red Hat Inc.
+ * Copyright IBM, Corp. 2009
+ *
+ * Authors:
+ *  Luiz Capitulino <lcapitulino@redhat.com>
+ *  Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * 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 (file)
index 0000000..c1713d1
--- /dev/null
@@ -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 <math.h>
+
+/* 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 (file)
index 0000000..c99be97
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * QOM interface test.
+ *
+ * Copyright (C) 2013 Red Hat Inc.
+ *
+ * Authors:
+ *  Igor Mammedov <imammedo@redhat.com>
+ *
+ * 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 (file)
index 0000000..1b76581
--- /dev/null
@@ -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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Author: Daniel P. Berrange <berrange@redhat.com>
+ */
+
+#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 (file)
index 0000000..4bf9772
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * QString unit-tests.
+ *
+ * Copyright (C) 2009 Red Hat Inc.
+ *
+ * Authors:
+ *  Luiz Capitulino <lcapitulino@redhat.com>
+ *
+ * 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 (file)
index 0000000..a839547
--- /dev/null
@@ -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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Author: Richard W.M. Jones <rjones@redhat.com>
+ */
+
+#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 (file)
index 0000000..5aa9951
--- /dev/null
@@ -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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Author: Richard W.M. Jones <rjones@redhat.com>
+ */
+
+#ifndef TESTS_CRYPTO_TLS_PSK_HELPERS_H
+#define TESTS_CRYPTO_TLS_PSK_HELPERS_H
+
+#include <gnutls/gnutls.h>
+
+#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 (file)
index 0000000..9765859
--- /dev/null
@@ -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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Author: Daniel P. Berrange <berrange@redhat.com>
+ */
+
+#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 (file)
index 0000000..8fcd778
--- /dev/null
@@ -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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Author: Daniel P. Berrange <berrange@redhat.com>
+ */
+
+#ifndef TESTS_CRYPTO_TLS_X509_HELPERS_H
+#define TESTS_CRYPTO_TLS_X509_HELPERS_H
+
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+
+#if !(defined WIN32) && \
+    defined(CONFIG_TASN1)
+# define QCRYPTO_HAVE_TLS_TEST_SUPPORT
+#endif
+
+#ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT
+# include <libtasn1.h>
+
+
+/*
+ * 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 (file)
index 0000000..ff156ed
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#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 (file)
index 0000000..3d14043
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+ */
+
+#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 (file)
index 0000000..afde12b
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * Event loop thread implementation for unit tests
+ *
+ * Copyright Red Hat Inc., 2013, 2016
+ *
+ * Authors:
+ *  Stefan Hajnoczi   <stefanha@redhat.com>
+ *  Paolo Bonzini     <pbonzini@redhat.com>
+ *
+ * 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 (file)
index 0000000..4877cea
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Event loop thread implementation for unit tests
+ *
+ * Copyright Red Hat Inc., 2013, 2016
+ *
+ * Authors:
+ *  Stefan Hajnoczi   <stefanha@redhat.com>
+ *  Paolo Bonzini     <pbonzini@redhat.com>
+ *
+ * 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 (file)
index 0000000..4bfe462
--- /dev/null
@@ -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 (file)
index 0000000..15397cf
--- /dev/null
@@ -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 (file)
index 0000000..7f801a4
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Stubs for the ptimer-test
+ *
+ * Copyright (c) 2016 Dmitry Osipenko <digetx@gmail.com>
+ *
+ * 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 (file)
index 0000000..9176b96
--- /dev/null
@@ -0,0 +1,892 @@
+/*
+ * QTest testcase for the ptimer
+ *
+ * Copyright (c) 2016 Dmitry Osipenko <digetx@gmail.com>
+ *
+ * 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 <glib/gprintf.h>
+
+#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 (file)
index 0000000..09ac56d
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * QTest testcase for the ptimer
+ *
+ * Copyright (c) 2016 Dmitry Osipenko <digetx@gmail.com>
+ *
+ * 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 (file)
index 0000000..de6f649
--- /dev/null
@@ -0,0 +1,483 @@
+/*
+ * rcutorture.c: simple user-level performance/stress test of RCU.
+ *
+ * Usage:
+ *     ./rcu <nreaders> rperf [ <seconds> ]
+ *         Run a read-side performance test with the specified
+ *         number of readers for <seconds> seconds.
+ *     ./rcu <nupdaters> uperf [ <seconds> ]
+ *         Run an update-side performance test with the specified
+ *         number of updaters and specified duration.
+ *     ./rcu <nreaders> perf [ <seconds> ]
+ *         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 <nreaders> stress [ <seconds> ]
+ *         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 (file)
index 0000000..f704fd1
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#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 (file)
index 0000000..512a004
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+ */
+
+#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 (file)
index 0000000..a555cc8
--- /dev/null
@@ -0,0 +1,460 @@
+/*
+ * AioContext multithreading tests
+ *
+ * Copyright Red Hat, Inc. 2016
+ *
+ * Authors:
+ *  Paolo Bonzini    <pbonzini@redhat.com>
+ *
+ * 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 (file)
index 0000000..8a46078
--- /dev/null
@@ -0,0 +1,921 @@
+/*
+ * AioContext tests
+ *
+ * Copyright Red Hat, Inc. 2012
+ *
+ * Authors:
+ *  Paolo Bonzini    <pbonzini@redhat.com>
+ *
+ * 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 (file)
index 0000000..5351992
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#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 (file)
index 0000000..64d0e15
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#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 (file)
index 0000000..4fe1ef2
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/module.h"
+#include "authz/pamacct.h"
+
+#include <security/pam_appl.h>
+
+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 (file)
index 0000000..6f9034d
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#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 (file)
index 0000000..3012d7b
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#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 (file)
index 0000000..8a29e33
--- /dev/null
@@ -0,0 +1,2230 @@
+/*
+ * Block node draining tests
+ *
+ * Copyright (c) 2017 Kevin Wolf <kwolf@redhat.com>
+ *
+ * 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 (file)
index 0000000..c4f7d16
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#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 (file)
index 0000000..e153dcb
--- /dev/null
@@ -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 (file)
index 0000000..8db4f67
--- /dev/null
@@ -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 <peterx@redhat.com>
+ */
+
+#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 (file)
index 0000000..5a791d2
--- /dev/null
@@ -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 (file)
index 0000000..2fb1a44
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * BlockBackend tests
+ *
+ * Copyright (c) 2017 Kevin Wolf <kwolf@redhat.com>
+ *
+ * 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 (file)
index 0000000..3f866a3
--- /dev/null
@@ -0,0 +1,774 @@
+/*
+ * Block tests for iothreads
+ *
+ * Copyright (c) 2018 Kevin Wolf <kwolf@redhat.com>
+ *
+ * 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 (file)
index 0000000..8bd13b9
--- /dev/null
@@ -0,0 +1,262 @@
+/*
+ * Blockjob transactions tests
+ *
+ * Copyright Red Hat, Inc. 2015
+ *
+ * Authors:
+ *  Stefan Hajnoczi    <stefanha@redhat.com>
+ *
+ * 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 (file)
index 0000000..7519847
--- /dev/null
@@ -0,0 +1,393 @@
+/*
+ * Blockjob tests
+ *
+ * Copyright Igalia, S.L. 2016
+ *
+ * Authors:
+ *  Alberto Garcia   <berto@igalia.com>
+ *
+ * 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 (file)
index 0000000..e45fd31
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#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 (file)
index 0000000..755d54c
--- /dev/null
@@ -0,0 +1,1560 @@
+#include "qemu/osdep.h"
+#include <glib/gstdio.h>
+
+#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 (file)
index 0000000..4944b3d
--- /dev/null
@@ -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 (file)
index 0000000..e946d93
--- /dev/null
@@ -0,0 +1,512 @@
+/*
+ * Coroutine tests
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ *  Stefan Hajnoczi    <stefanha@linux.vnet.ibm.com>
+ *
+ * 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 (file)
index 0000000..00a7c18
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#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 (file)
index 0000000..3b1f0d5
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#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 <sys/resource.h>
+#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 (file)
index 0000000..280319a
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#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 (file)
index 0000000..ce7d0ab
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#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 (file)
index 0000000..ee55382
--- /dev/null
@@ -0,0 +1,266 @@
+/*
+ * QEMU Crypto hmac algorithms tests
+ *
+ * Copyright (c) 2016 HUAWEI TECHNOLOGIES CO., LTD.
+ *
+ * Authors:
+ *    Longpeng(Mike) <longpeng2@huawei.com>
+ *
+ * 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 (file)
index 0000000..f581e6a
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#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 (file)
index 0000000..c50fd63
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "crypto/init.h"
+#ifndef _WIN32
+#include <sys/resource.h>
+#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 (file)
index 0000000..34a4aec
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#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 <keyutils.h>
+#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: <https://www.spinics.net/lists/keyrings/index.html>
+ * 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 (file)
index 0000000..f487349
--- /dev/null
@@ -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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Author: Daniel P. Berrange <berrange@redhat.com>
+ */
+
+#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 (file)
index 0000000..8b2453f
--- /dev/null
@@ -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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Author: Daniel P. Berrange <berrange@redhat.com>
+ */
+
+#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 (file)
index 0000000..7acbc95
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ * 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 (file)
index 0000000..1aa8351
--- /dev/null
@@ -0,0 +1,2460 @@
+/*
+ * cutils.c unit-tests
+ *
+ * Copyright (C) 2013 Red Hat Inc.
+ *
+ * Authors:
+ *  Eduardo Habkost <ehabkost@redhat.com>
+ *
+ * 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 (file)
index 0000000..11fd8a2
--- /dev/null
@@ -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(&notifiers[i], false);
+        aio_set_event_notifier(ctx, &notifiers[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, &notifiers[i], false, NULL, NULL);
+        event_notifier_cleanup(&notifiers[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(&notifiers[0]);
+    assert(aio_poll(ctx, true));
+
+    aio_disable_external(ctx);
+    event_notifier_set(&notifiers[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 (file)
index 0000000..b6726cf
--- /dev/null
@@ -0,0 +1,1119 @@
+/*
+ * Hierarchical bitmap unit-tests.
+ *
+ * Copyright (C) 2012 Red Hat Inc.
+ *
+ * Author: Paolo Bonzini <pbonzini@redhat.com>
+ *
+ * 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 (file)
index 0000000..ba057bd
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * Image locking tests
+ *
+ * Copyright (c) 2018 Red Hat Inc.
+ *
+ * Author: Fam Zheng <famz@redhat.com>
+ *
+ * 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 (file)
index 0000000..b86a3c7
--- /dev/null
@@ -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 (file)
index 0000000..9c6724d
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#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 (file)
index 0000000..99056e0
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#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 (file)
index 0000000..29038e6
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#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 (file)
index 0000000..c49eec1
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#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 (file)
index 0000000..ad7554c
--- /dev/null
@@ -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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Author: Daniel P. Berrange <berrange@redhat.com>
+ */
+
+
+#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 (file)
index 0000000..953a50a
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#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 (file)
index 0000000..9c415e2
--- /dev/null
@@ -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 (file)
index 0000000..ee927fe
--- /dev/null
@@ -0,0 +1,750 @@
+/*
+ * Unit tests for parsing of KEY=VALUE,... strings
+ *
+ * Copyright (C) 2017 Red Hat Inc.
+ *
+ * Authors:
+ *  Markus Armbruster <armbru@redhat.com>,
+ *
+ * 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 (file)
index 0000000..ccb819f
--- /dev/null
@@ -0,0 +1,218 @@
+/*
+ * logging unit-tests
+ *
+ * Copyright (C) 2016 Linaro Ltd.
+ *
+ *  Author: Alex Bennée <alex.bennee@linaro.org>
+ *
+ * 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 <glib/gstdio.h>
+
+#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 (file)
index 0000000..9be775d
--- /dev/null
@@ -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 (file)
index 0000000..23e8970
--- /dev/null
@@ -0,0 +1,371 @@
+/*
+ * Options Visitor unit-tests.
+ *
+ * Copyright (C) 2013 Red Hat, Inc.
+ *
+ * Authors:
+ *   Laszlo Ersek <lersek@redhat.com> (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
+     * <http://en.wikipedia.org/wiki/Magic_number_%28programming%29>, 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 (file)
index 0000000..847f305
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Unit tests for QAPI utility functions
+ *
+ * Copyright (C) 2017 Red Hat Inc.
+ *
+ * Authors:
+ *  Markus Armbruster <armbru@redhat.com>,
+ *
+ * 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 (file)
index 0000000..c8862ca
--- /dev/null
@@ -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 (file)
index 0000000..9541ce3
--- /dev/null
@@ -0,0 +1,389 @@
+/*
+ * Copyright (C) 2016, Emilio G. Cota <cota@braap.org>
+ *
+ * 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 <math.h>
+
+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 (file)
index 0000000..8bbb17b
--- /dev/null
@@ -0,0 +1,1046 @@
+/*
+ * QemuOpts unit-tests.
+ *
+ * Copyright (C) 2014 Leandro Dorileo <l@dorileo.org>
+ *
+ * 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 (file)
index 0000000..5cb140d
--- /dev/null
@@ -0,0 +1,1019 @@
+#include "qemu/osdep.h"
+#include <locale.h>
+#include <glib/gstdio.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#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 (file)
index 0000000..f819430
--- /dev/null
@@ -0,0 +1,434 @@
+/*
+ * libqos driver framework
+ *
+ * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>
+ */
+
+#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 (file)
index 0000000..4d23cef
--- /dev/null
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2016, Emilio G. Cota <cota@braap.org>
+ *
+ * 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 (file)
index 0000000..d3413bf
--- /dev/null
@@ -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 (file)
index 0000000..7dd0053
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * qapi event unit-tests.
+ *
+ * Copyright (c) 2014 Wenchao Xia
+ *
+ * Authors:
+ *  Wenchao Xia   <wenchaoqemu@gmail.com>
+ *
+ * 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 (file)
index 0000000..e41b91a
--- /dev/null
@@ -0,0 +1,1379 @@
+/*
+ * QObject Input Visitor unit-tests.
+ *
+ * Copyright (C) 2011-2016 Red Hat Inc.
+ *
+ * Authors:
+ *  Luiz Capitulino <lcapitulino@redhat.com>
+ *  Paolo Bonzini <pbonzini@redhat.com>
+ *
+ * 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 (file)
index 0000000..9dc1e07
--- /dev/null
@@ -0,0 +1,807 @@
+/*
+ * QObject Output Visitor unit-tests.
+ *
+ * Copyright (C) 2011-2016 Red Hat Inc.
+ *
+ * Authors:
+ *  Luiz Capitulino <lcapitulino@redhat.com>
+ *
+ * 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 (file)
index 0000000..49641e1
--- /dev/null
@@ -0,0 +1,381 @@
+/*
+ * rcuq_test.c
+ *
+ * usage: rcuq_test <readers> <duration>
+ *
+ * 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 (file)
index 0000000..057f7d3
--- /dev/null
@@ -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 (file)
index 0000000..868e1e4
--- /dev/null
@@ -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 (file)
index 0000000..8d487e0
--- /dev/null
@@ -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 (file)
index 0000000..b067240
--- /dev/null
@@ -0,0 +1,623 @@
+/*
+ * Block replication tests
+ *
+ * Copyright (c) 2016 FUJITSU LIMITED
+ * Author: Changlong Xie <xiecl.fnst@cn.fujitsu.com>
+ *
+ * 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 (file)
index 0000000..f3ff736
--- /dev/null
@@ -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 (file)
index 0000000..249faaf
--- /dev/null
@@ -0,0 +1,501 @@
+/*
+ * String Input Visitor unit-tests.
+ *
+ * Copyright (C) 2012 Red Hat Inc.
+ *
+ * Authors:
+ *  Paolo Bonzini <pbonzini@redhat.com> (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 (file)
index 0000000..e2bedc5
--- /dev/null
@@ -0,0 +1,248 @@
+/*
+ * String Output Visitor unit-tests.
+ *
+ * Copyright (C) 2012 Red Hat Inc.
+ *
+ * Authors:
+ *  Paolo Bonzini <pbonzini@redhat.com> (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, ==, "<null>");
+    } 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 (file)
index 0000000..70dc631
--- /dev/null
@@ -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 (file)
index 0000000..7adb5e6
--- /dev/null
@@ -0,0 +1,770 @@
+/*
+ * Throttle infrastructure tests
+ *
+ * Copyright Nodalink, EURL. 2013-2014
+ * Copyright Igalia, S.L. 2015
+ *
+ * Authors:
+ *  Benoît Canet     <benoit.canet@nodalink.com>
+ *  Alberto Garcia   <berto@igalia.com>
+ *
+ * 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 <math.h>
+#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 (file)
index 0000000..82c9250
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Timed average computation tests
+ *
+ * Copyright Nodalink, EURL. 2014
+ *
+ * Authors:
+ *  Benoît Canet     <benoit.canet@nodalink.com>
+ *
+ * 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 (file)
index 0000000..b629e10
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/main-loop.h"
+#include "qapi/error.h"
+#include "qemu/filemonitor.h"
+
+#include <glib/gstdio.h>
+
+#include <utime.h>
+
+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 (file)
index 0000000..6748605
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#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 (file)
index 0000000..c111de5
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#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 (file)
index 0000000..4629958
--- /dev/null
@@ -0,0 +1,1116 @@
+/*
+ * Unit-tests for visitor-based serialization
+ *
+ * Copyright (C) 2014-2015 Red Hat, Inc.
+ * Copyright IBM, Corp. 2012
+ *
+ * Authors:
+ *  Michael Roth <mdroth@linux.vnet.ibm.com>
+ *
+ * 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 <float.h>
+
+#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 (file)
index 0000000..a001879
--- /dev/null
@@ -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 (file)
index 0000000..fc1c45a
--- /dev/null
@@ -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 (file)
index 0000000..bfabc04
--- /dev/null
@@ -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 (file)
index 0000000..795d6f1
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+ * Xor Based Zero Run Length Encoding unit tests.
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates
+ *
+ * Authors:
+ *  Orit Wasserman  <owasserm@redhat.com>
+ *
+ * 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();
+}