summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorYonghee Han <onstudy@samsung.com>2016-07-27 16:40:17 +0900
committerYonghee Han <onstudy@samsung.com>2016-07-27 00:53:56 -0700
commit3158f4a51894e46ecb593bffbfd12824e1d6534a (patch)
tree2bef7f0238e687c5de65f48b5995ee124a95d157 /tests
parenta3b133b0ea0696e42fd876b9a803e28bc6ef5299 (diff)
downloadqemu-3158f4a51894e46ecb593bffbfd12824e1d6534a.tar.gz
qemu-3158f4a51894e46ecb593bffbfd12824e1d6534a.tar.bz2
qemu-3158f4a51894e46ecb593bffbfd12824e1d6534a.zip
Imported Upstream version 2.4.1upstream/2.4.1
Change-Id: I0b584f569cb0e0f4eac13cdb79e110c2dbc34bfc
Diffstat (limited to 'tests')
-rw-r--r--tests/.gitignore3
-rw-r--r--tests/Makefile100
-rw-r--r--tests/acpi-test-data/pc/DSDTbin2970 -> 3028 bytes
-rw-r--r--tests/acpi-test-data/pc/SSDTbin2475 -> 2486 bytes
-rw-r--r--tests/acpi-test-data/pc/SSDT.bridgebin4334 -> 4345 bytes
-rw-r--r--tests/acpi-test-data/q35/DSDTbin7608 -> 7666 bytes
-rw-r--r--tests/acpi-test-data/q35/SSDTbin680 -> 691 bytes
-rw-r--r--tests/acpi-test-data/q35/SSDT.bridgebin697 -> 708 bytes
-rw-r--r--tests/ahci-test.c668
-rw-r--r--tests/bios-tables-test.c78
-rw-r--r--tests/check-qdict.c67
-rw-r--r--tests/check-qjson.c26
-rw-r--r--tests/check-qom-proplist.c302
-rw-r--r--tests/device-introspect-test.c158
-rw-r--r--tests/display-vga-test.c18
-rw-r--r--tests/drive_del-test.c22
-rw-r--r--tests/e1000-test.c4
-rw-r--r--tests/eepro100-test.c5
-rw-r--r--tests/endianness-test.c20
-rw-r--r--tests/fdc-test.c34
-rw-r--r--tests/ide-test.c69
-rw-r--r--tests/libqos/ahci.c240
-rw-r--r--tests/libqos/ahci.h68
-rw-r--r--tests/libqos/libqos-pc.c5
-rw-r--r--tests/libqos/libqos-pc.h1
-rw-r--r--tests/libqos/libqos.c151
-rw-r--r--tests/libqos/libqos.h5
-rw-r--r--tests/libqos/malloc.c77
-rw-r--r--tests/libqos/malloc.h1
-rw-r--r--tests/libqos/virtio.h10
-rw-r--r--tests/libqtest.c131
-rw-r--r--tests/libqtest.h129
-rw-r--r--tests/pc-cpu-test.c13
-rw-r--r--tests/q35-test.c91
-rw-r--r--tests/qapi-schema/alternate-array.err1
-rw-r--r--tests/qapi-schema/alternate-array.exit1
-rw-r--r--tests/qapi-schema/alternate-array.json7
-rw-r--r--tests/qapi-schema/alternate-array.out0
-rw-r--r--tests/qapi-schema/alternate-base.err1
-rw-r--r--tests/qapi-schema/alternate-base.exit1
-rw-r--r--tests/qapi-schema/alternate-base.json6
-rw-r--r--tests/qapi-schema/alternate-base.out0
-rw-r--r--tests/qapi-schema/alternate-clash.err1
-rw-r--r--tests/qapi-schema/alternate-clash.exit1
-rw-r--r--tests/qapi-schema/alternate-clash.json3
-rw-r--r--tests/qapi-schema/alternate-clash.out0
-rw-r--r--tests/qapi-schema/alternate-conflict-dict.err1
-rw-r--r--tests/qapi-schema/alternate-conflict-dict.exit1
-rw-r--r--tests/qapi-schema/alternate-conflict-dict.json8
-rw-r--r--tests/qapi-schema/alternate-conflict-dict.out0
-rw-r--r--tests/qapi-schema/alternate-conflict-string.err1
-rw-r--r--tests/qapi-schema/alternate-conflict-string.exit1
-rw-r--r--tests/qapi-schema/alternate-conflict-string.json6
-rw-r--r--tests/qapi-schema/alternate-conflict-string.out0
-rw-r--r--tests/qapi-schema/alternate-good.err0
-rw-r--r--tests/qapi-schema/alternate-good.exit1
-rw-r--r--tests/qapi-schema/alternate-good.json9
-rw-r--r--tests/qapi-schema/alternate-good.out6
-rw-r--r--tests/qapi-schema/alternate-nested.err1
-rw-r--r--tests/qapi-schema/alternate-nested.exit1
-rw-r--r--tests/qapi-schema/alternate-nested.json5
-rw-r--r--tests/qapi-schema/alternate-nested.out0
-rw-r--r--tests/qapi-schema/alternate-unknown.err1
-rw-r--r--tests/qapi-schema/alternate-unknown.exit1
-rw-r--r--tests/qapi-schema/alternate-unknown.json3
-rw-r--r--tests/qapi-schema/alternate-unknown.out0
-rw-r--r--tests/qapi-schema/bad-base.err1
-rw-r--r--tests/qapi-schema/bad-base.exit1
-rw-r--r--tests/qapi-schema/bad-base.json3
-rw-r--r--tests/qapi-schema/bad-base.out0
-rw-r--r--tests/qapi-schema/bad-data.err1
-rw-r--r--tests/qapi-schema/bad-data.exit1
-rw-r--r--tests/qapi-schema/bad-data.json2
-rw-r--r--tests/qapi-schema/bad-data.out0
-rw-r--r--tests/qapi-schema/bad-ident.err1
-rw-r--r--tests/qapi-schema/bad-ident.exit1
-rw-r--r--tests/qapi-schema/bad-ident.json2
-rw-r--r--tests/qapi-schema/bad-ident.out0
-rw-r--r--tests/qapi-schema/bad-type-bool.err1
-rw-r--r--tests/qapi-schema/bad-type-bool.exit1
-rw-r--r--tests/qapi-schema/bad-type-bool.json2
-rw-r--r--tests/qapi-schema/bad-type-bool.out0
-rw-r--r--tests/qapi-schema/bad-type-dict.err1
-rw-r--r--tests/qapi-schema/bad-type-dict.exit1
-rw-r--r--tests/qapi-schema/bad-type-dict.json2
-rw-r--r--tests/qapi-schema/bad-type-dict.out0
-rw-r--r--tests/qapi-schema/bad-type-int.err1
-rw-r--r--tests/qapi-schema/bad-type-int.exit1
-rw-r--r--tests/qapi-schema/bad-type-int.json3
-rw-r--r--tests/qapi-schema/bad-type-int.out0
-rw-r--r--tests/qapi-schema/command-int.err1
-rw-r--r--tests/qapi-schema/command-int.exit1
-rw-r--r--tests/qapi-schema/command-int.json3
-rw-r--r--tests/qapi-schema/command-int.out0
-rw-r--r--tests/qapi-schema/data-array-empty.err1
-rw-r--r--tests/qapi-schema/data-array-empty.exit1
-rw-r--r--tests/qapi-schema/data-array-empty.json2
-rw-r--r--tests/qapi-schema/data-array-empty.out0
-rw-r--r--tests/qapi-schema/data-array-unknown.err1
-rw-r--r--tests/qapi-schema/data-array-unknown.exit1
-rw-r--r--tests/qapi-schema/data-array-unknown.json2
-rw-r--r--tests/qapi-schema/data-array-unknown.out0
-rw-r--r--tests/qapi-schema/data-int.err1
-rw-r--r--tests/qapi-schema/data-int.exit1
-rw-r--r--tests/qapi-schema/data-int.json2
-rw-r--r--tests/qapi-schema/data-int.out0
-rw-r--r--tests/qapi-schema/data-member-array-bad.err1
-rw-r--r--tests/qapi-schema/data-member-array-bad.exit1
-rw-r--r--tests/qapi-schema/data-member-array-bad.json2
-rw-r--r--tests/qapi-schema/data-member-array-bad.out0
-rw-r--r--tests/qapi-schema/data-member-array.err0
-rw-r--r--tests/qapi-schema/data-member-array.exit1
-rw-r--r--tests/qapi-schema/data-member-array.json4
-rw-r--r--tests/qapi-schema/data-member-array.out5
-rw-r--r--tests/qapi-schema/data-member-unknown.err1
-rw-r--r--tests/qapi-schema/data-member-unknown.exit1
-rw-r--r--tests/qapi-schema/data-member-unknown.json2
-rw-r--r--tests/qapi-schema/data-member-unknown.out0
-rw-r--r--tests/qapi-schema/data-unknown.err1
-rw-r--r--tests/qapi-schema/data-unknown.exit1
-rw-r--r--tests/qapi-schema/data-unknown.json2
-rw-r--r--tests/qapi-schema/data-unknown.out0
-rw-r--r--tests/qapi-schema/double-data.err1
-rw-r--r--tests/qapi-schema/double-data.exit1
-rw-r--r--tests/qapi-schema/double-data.json2
-rw-r--r--tests/qapi-schema/double-data.out0
-rw-r--r--tests/qapi-schema/double-type.err1
-rw-r--r--tests/qapi-schema/double-type.exit1
-rw-r--r--tests/qapi-schema/double-type.json2
-rw-r--r--tests/qapi-schema/double-type.out0
-rw-r--r--tests/qapi-schema/enum-bad-name.err1
-rw-r--r--tests/qapi-schema/enum-bad-name.exit1
-rw-r--r--tests/qapi-schema/enum-bad-name.json2
-rw-r--r--tests/qapi-schema/enum-bad-name.out0
-rw-r--r--tests/qapi-schema/enum-clash-member.err1
-rw-r--r--tests/qapi-schema/enum-clash-member.exit1
-rw-r--r--tests/qapi-schema/enum-clash-member.json2
-rw-r--r--tests/qapi-schema/enum-clash-member.out0
-rw-r--r--tests/qapi-schema/enum-dict-member.err1
-rw-r--r--tests/qapi-schema/enum-dict-member.exit1
-rw-r--r--tests/qapi-schema/enum-dict-member.json2
-rw-r--r--tests/qapi-schema/enum-dict-member.out0
-rw-r--r--tests/qapi-schema/enum-empty.err0
-rw-r--r--tests/qapi-schema/enum-empty.exit1
-rw-r--r--tests/qapi-schema/enum-empty.json2
-rw-r--r--tests/qapi-schema/enum-empty.out3
-rw-r--r--tests/qapi-schema/enum-int-member.err1
-rw-r--r--tests/qapi-schema/enum-int-member.exit1
-rw-r--r--tests/qapi-schema/enum-int-member.json3
-rw-r--r--tests/qapi-schema/enum-int-member.out0
-rw-r--r--tests/qapi-schema/enum-max-member.err1
-rw-r--r--tests/qapi-schema/enum-max-member.exit1
-rw-r--r--tests/qapi-schema/enum-max-member.json3
-rw-r--r--tests/qapi-schema/enum-max-member.out0
-rw-r--r--tests/qapi-schema/enum-missing-data.err1
-rw-r--r--tests/qapi-schema/enum-missing-data.exit1
-rw-r--r--tests/qapi-schema/enum-missing-data.json2
-rw-r--r--tests/qapi-schema/enum-missing-data.out0
-rw-r--r--tests/qapi-schema/enum-union-clash.err1
-rw-r--r--tests/qapi-schema/enum-union-clash.exit1
-rw-r--r--tests/qapi-schema/enum-union-clash.json4
-rw-r--r--tests/qapi-schema/enum-union-clash.out0
-rw-r--r--tests/qapi-schema/enum-wrong-data.err1
-rw-r--r--tests/qapi-schema/enum-wrong-data.exit1
-rw-r--r--tests/qapi-schema/enum-wrong-data.json2
-rw-r--r--tests/qapi-schema/enum-wrong-data.out0
-rw-r--r--tests/qapi-schema/escape-outside-string.err1
-rw-r--r--tests/qapi-schema/escape-outside-string.exit1
-rw-r--r--tests/qapi-schema/escape-outside-string.json3
-rw-r--r--tests/qapi-schema/escape-outside-string.out0
-rw-r--r--tests/qapi-schema/escape-too-big.err1
-rw-r--r--tests/qapi-schema/escape-too-big.exit1
-rw-r--r--tests/qapi-schema/escape-too-big.json3
-rw-r--r--tests/qapi-schema/escape-too-big.out0
-rw-r--r--tests/qapi-schema/escape-too-short.err1
-rw-r--r--tests/qapi-schema/escape-too-short.exit1
-rw-r--r--tests/qapi-schema/escape-too-short.json3
-rw-r--r--tests/qapi-schema/escape-too-short.out0
-rw-r--r--tests/qapi-schema/event-case.err0
-rw-r--r--tests/qapi-schema/event-case.exit1
-rw-r--r--tests/qapi-schema/event-case.json3
-rw-r--r--tests/qapi-schema/event-case.out3
-rw-r--r--tests/qapi-schema/event-max.err1
-rw-r--r--tests/qapi-schema/event-max.exit1
-rw-r--r--tests/qapi-schema/event-max.json2
-rw-r--r--tests/qapi-schema/event-max.out0
-rw-r--r--tests/qapi-schema/event-nest-struct.err2
-rw-r--r--tests/qapi-schema/flat-union-array-branch.err1
-rw-r--r--tests/qapi-schema/flat-union-array-branch.exit1
-rw-r--r--tests/qapi-schema/flat-union-array-branch.json12
-rw-r--r--tests/qapi-schema/flat-union-array-branch.out0
-rw-r--r--tests/qapi-schema/flat-union-bad-base.err1
-rw-r--r--tests/qapi-schema/flat-union-bad-base.exit1
-rw-r--r--tests/qapi-schema/flat-union-bad-base.json13
-rw-r--r--tests/qapi-schema/flat-union-bad-base.out0
-rw-r--r--tests/qapi-schema/flat-union-bad-discriminator.err1
-rw-r--r--tests/qapi-schema/flat-union-bad-discriminator.exit1
-rw-r--r--tests/qapi-schema/flat-union-bad-discriminator.json15
-rw-r--r--tests/qapi-schema/flat-union-bad-discriminator.out0
-rw-r--r--tests/qapi-schema/flat-union-base-star.err1
-rw-r--r--tests/qapi-schema/flat-union-base-star.exit1
-rw-r--r--tests/qapi-schema/flat-union-base-star.json12
-rw-r--r--tests/qapi-schema/flat-union-base-star.out0
-rw-r--r--tests/qapi-schema/flat-union-base-union.err1
-rw-r--r--tests/qapi-schema/flat-union-base-union.exit1
-rw-r--r--tests/qapi-schema/flat-union-base-union.json15
-rw-r--r--tests/qapi-schema/flat-union-base-union.out0
-rw-r--r--tests/qapi-schema/flat-union-branch-clash.err1
-rw-r--r--tests/qapi-schema/flat-union-branch-clash.exit1
-rw-r--r--tests/qapi-schema/flat-union-branch-clash.json14
-rw-r--r--tests/qapi-schema/flat-union-branch-clash.out0
-rw-r--r--tests/qapi-schema/flat-union-inline.err1
-rw-r--r--tests/qapi-schema/flat-union-inline.exit1
-rw-r--r--tests/qapi-schema/flat-union-inline.json11
-rw-r--r--tests/qapi-schema/flat-union-inline.out0
-rw-r--r--tests/qapi-schema/flat-union-int-branch.err1
-rw-r--r--tests/qapi-schema/flat-union-int-branch.exit1
-rw-r--r--tests/qapi-schema/flat-union-int-branch.json12
-rw-r--r--tests/qapi-schema/flat-union-int-branch.out0
-rw-r--r--tests/qapi-schema/flat-union-invalid-branch-key.json6
-rw-r--r--tests/qapi-schema/flat-union-invalid-discriminator.err2
-rw-r--r--tests/qapi-schema/flat-union-invalid-discriminator.json6
-rw-r--r--tests/qapi-schema/flat-union-no-base.err2
-rw-r--r--tests/qapi-schema/flat-union-no-base.json12
-rw-r--r--tests/qapi-schema/flat-union-optional-discriminator.err1
-rw-r--r--tests/qapi-schema/flat-union-optional-discriminator.exit1
-rw-r--r--tests/qapi-schema/flat-union-optional-discriminator.json10
-rw-r--r--tests/qapi-schema/flat-union-optional-discriminator.out0
-rw-r--r--tests/qapi-schema/flat-union-reverse-define.json6
-rw-r--r--tests/qapi-schema/flat-union-reverse-define.out12
-rw-r--r--tests/qapi-schema/flat-union-string-discriminator.json6
-rw-r--r--tests/qapi-schema/ident-with-escape.err0
-rw-r--r--tests/qapi-schema/ident-with-escape.exit1
-rw-r--r--tests/qapi-schema/ident-with-escape.json4
-rw-r--r--tests/qapi-schema/ident-with-escape.out3
-rw-r--r--tests/qapi-schema/include-cycle.err4
-rw-r--r--tests/qapi-schema/include-nested-err.err2
-rw-r--r--tests/qapi-schema/indented-expr.json4
-rw-r--r--tests/qapi-schema/indented-expr.out2
-rw-r--r--tests/qapi-schema/missing-type.err1
-rw-r--r--tests/qapi-schema/missing-type.exit1
-rw-r--r--tests/qapi-schema/missing-type.json2
-rw-r--r--tests/qapi-schema/missing-type.out0
-rw-r--r--tests/qapi-schema/nested-struct-data.err1
-rw-r--r--tests/qapi-schema/nested-struct-data.exit1
-rw-r--r--tests/qapi-schema/nested-struct-data.json4
-rw-r--r--tests/qapi-schema/nested-struct-data.out0
-rw-r--r--tests/qapi-schema/nested-struct-returns.err1
-rw-r--r--tests/qapi-schema/nested-struct-returns.exit1
-rw-r--r--tests/qapi-schema/nested-struct-returns.json3
-rw-r--r--tests/qapi-schema/nested-struct-returns.out0
-rw-r--r--tests/qapi-schema/qapi-schema-test.json66
-rw-r--r--tests/qapi-schema/qapi-schema-test.out73
-rw-r--r--tests/qapi-schema/redefined-builtin.err1
-rw-r--r--tests/qapi-schema/redefined-builtin.exit1
-rw-r--r--tests/qapi-schema/redefined-builtin.json2
-rw-r--r--tests/qapi-schema/redefined-builtin.out0
-rw-r--r--tests/qapi-schema/redefined-command.err1
-rw-r--r--tests/qapi-schema/redefined-command.exit1
-rw-r--r--tests/qapi-schema/redefined-command.json3
-rw-r--r--tests/qapi-schema/redefined-command.out0
-rw-r--r--tests/qapi-schema/redefined-event.err1
-rw-r--r--tests/qapi-schema/redefined-event.exit1
-rw-r--r--tests/qapi-schema/redefined-event.json3
-rw-r--r--tests/qapi-schema/redefined-event.out0
-rw-r--r--tests/qapi-schema/redefined-type.err1
-rw-r--r--tests/qapi-schema/redefined-type.exit1
-rw-r--r--tests/qapi-schema/redefined-type.json3
-rw-r--r--tests/qapi-schema/redefined-type.out0
-rw-r--r--tests/qapi-schema/returns-alternate.err1
-rw-r--r--tests/qapi-schema/returns-alternate.exit1
-rw-r--r--tests/qapi-schema/returns-alternate.json3
-rw-r--r--tests/qapi-schema/returns-alternate.out0
-rw-r--r--tests/qapi-schema/returns-array-bad.err1
-rw-r--r--tests/qapi-schema/returns-array-bad.exit1
-rw-r--r--tests/qapi-schema/returns-array-bad.json2
-rw-r--r--tests/qapi-schema/returns-array-bad.out0
-rw-r--r--tests/qapi-schema/returns-int.err0
-rw-r--r--tests/qapi-schema/returns-int.exit1
-rw-r--r--tests/qapi-schema/returns-int.json3
-rw-r--r--tests/qapi-schema/returns-int.out3
-rw-r--r--tests/qapi-schema/returns-unknown.err1
-rw-r--r--tests/qapi-schema/returns-unknown.exit1
-rw-r--r--tests/qapi-schema/returns-unknown.json2
-rw-r--r--tests/qapi-schema/returns-unknown.out0
-rw-r--r--tests/qapi-schema/returns-whitelist.err1
-rw-r--r--tests/qapi-schema/returns-whitelist.exit1
-rw-r--r--tests/qapi-schema/returns-whitelist.json11
-rw-r--r--tests/qapi-schema/returns-whitelist.out0
-rw-r--r--tests/qapi-schema/struct-base-clash-deep.err1
-rw-r--r--tests/qapi-schema/struct-base-clash-deep.exit1
-rw-r--r--tests/qapi-schema/struct-base-clash-deep.json9
-rw-r--r--tests/qapi-schema/struct-base-clash-deep.out0
-rw-r--r--tests/qapi-schema/struct-base-clash.err1
-rw-r--r--tests/qapi-schema/struct-base-clash.exit1
-rw-r--r--tests/qapi-schema/struct-base-clash.json6
-rw-r--r--tests/qapi-schema/struct-base-clash.out0
-rw-r--r--tests/qapi-schema/type-bypass-bad-gen.err1
-rw-r--r--tests/qapi-schema/type-bypass-bad-gen.exit1
-rw-r--r--tests/qapi-schema/type-bypass-bad-gen.json2
-rw-r--r--tests/qapi-schema/type-bypass-bad-gen.out0
-rw-r--r--tests/qapi-schema/type-bypass-no-gen.err1
-rw-r--r--tests/qapi-schema/type-bypass-no-gen.exit1
-rw-r--r--tests/qapi-schema/type-bypass-no-gen.json2
-rw-r--r--tests/qapi-schema/type-bypass-no-gen.out0
-rw-r--r--tests/qapi-schema/type-bypass.err0
-rw-r--r--tests/qapi-schema/type-bypass.exit1
-rw-r--r--tests/qapi-schema/type-bypass.json2
-rw-r--r--tests/qapi-schema/type-bypass.out3
-rw-r--r--tests/qapi-schema/unicode-str.err1
-rw-r--r--tests/qapi-schema/unicode-str.exit1
-rw-r--r--tests/qapi-schema/unicode-str.json2
-rw-r--r--tests/qapi-schema/unicode-str.out0
-rw-r--r--tests/qapi-schema/union-bad-branch.err1
-rw-r--r--tests/qapi-schema/union-bad-branch.exit1
-rw-r--r--tests/qapi-schema/union-bad-branch.json8
-rw-r--r--tests/qapi-schema/union-bad-branch.out0
-rw-r--r--tests/qapi-schema/union-base-no-discriminator.err1
-rw-r--r--tests/qapi-schema/union-base-no-discriminator.exit1
-rw-r--r--tests/qapi-schema/union-base-no-discriminator.json14
-rw-r--r--tests/qapi-schema/union-base-no-discriminator.out0
-rw-r--r--tests/qapi-schema/union-invalid-base.err2
-rw-r--r--tests/qapi-schema/union-invalid-base.json8
-rw-r--r--tests/qapi-schema/union-max.err1
-rw-r--r--tests/qapi-schema/union-max.exit1
-rw-r--r--tests/qapi-schema/union-max.json3
-rw-r--r--tests/qapi-schema/union-max.out0
-rw-r--r--tests/qapi-schema/union-optional-branch.err1
-rw-r--r--tests/qapi-schema/union-optional-branch.exit1
-rw-r--r--tests/qapi-schema/union-optional-branch.json2
-rw-r--r--tests/qapi-schema/union-optional-branch.out0
-rw-r--r--tests/qapi-schema/union-unknown.err1
-rw-r--r--tests/qapi-schema/union-unknown.exit1
-rw-r--r--tests/qapi-schema/union-unknown.json3
-rw-r--r--tests/qapi-schema/union-unknown.out0
-rw-r--r--tests/qapi-schema/unknown-escape.err1
-rw-r--r--tests/qapi-schema/unknown-escape.exit1
-rw-r--r--tests/qapi-schema/unknown-escape.json3
-rw-r--r--tests/qapi-schema/unknown-escape.out0
-rw-r--r--tests/qapi-schema/unknown-expr-key.err1
-rw-r--r--tests/qapi-schema/unknown-expr-key.exit1
-rw-r--r--tests/qapi-schema/unknown-expr-key.json2
-rw-r--r--tests/qapi-schema/unknown-expr-key.out0
-rwxr-xr-xtests/qemu-iotests/0511
-rw-r--r--tests/qemu-iotests/051.out11
-rwxr-xr-xtests/qemu-iotests/0595
-rw-r--r--tests/qemu-iotests/059.out3
-rwxr-xr-xtests/qemu-iotests/07615
-rw-r--r--tests/qemu-iotests/076.out10
-rwxr-xr-xtests/qemu-iotests/09393
-rwxr-xr-xtests/qemu-iotests/11960
-rw-r--r--tests/qemu-iotests/119.out11
-rwxr-xr-xtests/qemu-iotests/12065
-rw-r--r--tests/qemu-iotests/120.out15
-rwxr-xr-xtests/qemu-iotests/122223
-rw-r--r--tests/qemu-iotests/122.out209
-rw-r--r--tests/qemu-iotests/124363
-rw-r--r--tests/qemu-iotests/124.out5
-rwxr-xr-xtests/qemu-iotests/1284
-rw-r--r--tests/qemu-iotests/12986
-rw-r--r--tests/qemu-iotests/129.out5
-rwxr-xr-xtests/qemu-iotests/13177
-rw-r--r--tests/qemu-iotests/131.out41
-rwxr-xr-xtests/qemu-iotests/13469
-rw-r--r--tests/qemu-iotests/134.out46
-rwxr-xr-xtests/qemu-iotests/check12
-rw-r--r--tests/qemu-iotests/common6
-rw-r--r--tests/qemu-iotests/group7
-rw-r--r--tests/qemu-iotests/iotests.py14
-rw-r--r--tests/qemu-iotests/sample_images/afl9.vmdk.bz2bin0 -> 178 bytes
-rw-r--r--tests/qom-test.c4
-rw-r--r--tests/rocker/README5
-rwxr-xr-xtests/rocker/all19
-rwxr-xr-xtests/rocker/bridge43
-rwxr-xr-xtests/rocker/bridge-stp52
-rwxr-xr-xtests/rocker/bridge-vlan52
-rwxr-xr-xtests/rocker/bridge-vlan-stp64
-rwxr-xr-xtests/rocker/port22
-rw-r--r--tests/rocker/tut.dot8
-rw-r--r--tests/rtl8139-test.c10
-rw-r--r--tests/tco-test.c465
-rw-r--r--tests/test-aio.c26
-rw-r--r--tests/test-crypto-cipher.c302
-rw-r--r--tests/test-crypto-hash.c209
-rw-r--r--tests/test-hbitmap.c293
-rw-r--r--tests/test-opts-visitor.c3
-rw-r--r--tests/test-qemu-opts.c2
-rw-r--r--tests/test-qmp-commands.c52
-rw-r--r--tests/test-qmp-event.c4
-rw-r--r--tests/test-qmp-input-strict.c98
-rw-r--r--tests/test-qmp-input-visitor.c119
-rw-r--r--tests/test-qmp-output-visitor.c162
-rw-r--r--tests/test-rcu-list.c4
-rw-r--r--tests/test-throttle.c163
-rw-r--r--tests/test-visitor-serialization.c87
-rw-r--r--tests/virtio-scsi-test.c201
-rw-r--r--tests/wdt_ib700-test.c15
397 files changed, 6338 insertions, 815 deletions
diff --git a/tests/.gitignore b/tests/.gitignore
index 0dcb61829..ccc92e476 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -5,10 +5,13 @@ check-qjson
check-qlist
check-qstring
check-qom-interface
+check-qom-proplist
rcutorture
test-aio
test-bitops
test-coroutine
+test-crypto-cipher
+test-crypto-hash
test-cutils
test-hbitmap
test-int128
diff --git a/tests/Makefile b/tests/Makefile
index 55aa7452b..b453eb44a 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -68,16 +68,23 @@ check-unit-y += tests/test-bitops$(EXESUF)
check-unit-$(CONFIG_HAS_GLIB_SUBPROCESS_TESTS) += tests/test-qdev-global-props$(EXESUF)
check-unit-y += tests/check-qom-interface$(EXESUF)
gcov-files-check-qom-interface-y = qom/object.c
+check-unit-y += tests/check-qom-proplist$(EXESUF)
+gcov-files-check-qom-proplist-y = qom/object.c
check-unit-y += tests/test-qemu-opts$(EXESUF)
gcov-files-test-qemu-opts-y = qom/test-qemu-opts.c
check-unit-y += tests/test-write-threshold$(EXESUF)
gcov-files-test-write-threshold-y = block/write-threshold.c
+check-unit-$(CONFIG_GNUTLS_HASH) += tests/test-crypto-hash$(EXESUF)
+check-unit-y += tests/test-crypto-cipher$(EXESUF)
check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh
# All QTests for now are POSIX-only, but the dependencies are
# really in libqtest, not in the testcases themselves.
+check-qtest-generic-y = tests/device-introspect-test$(EXESUF)
+gcov-files-generic-y = qdev-monitor.c qmp.c
+
gcov-files-ipack-y += hw/ipack/ipack.c
check-qtest-ipack-y += tests/ipoctal232-test$(EXESUF)
gcov-files-ipack-y += hw/char/ipoctal232.c
@@ -133,6 +140,9 @@ check-qtest-pci-y += tests/display-vga-test$(EXESUF)
gcov-files-pci-y += hw/display/vga.c
gcov-files-pci-y += hw/display/cirrus_vga.c
gcov-files-pci-y += hw/display/vga-pci.c
+gcov-files-pci-y += hw/display/virtio-gpu.c
+gcov-files-pci-y += hw/display/virtio-gpu-pci.c
+gcov-files-pci-$(CONFIG_VIRTIO_VGA) += hw/display/virtio-vga.c
check-qtest-pci-y += tests/intel-hda-test$(EXESUF)
gcov-files-pci-y += hw/audio/intel-hda.c hw/audio/hda-codec.c
@@ -150,6 +160,7 @@ check-qtest-i386-y += tests/i440fx-test$(EXESUF)
check-qtest-i386-y += tests/fw_cfg-test$(EXESUF)
check-qtest-i386-y += tests/drive_del-test$(EXESUF)
check-qtest-i386-y += tests/wdt_ib700-test$(EXESUF)
+check-qtest-i386-y += tests/tco-test$(EXESUF)
gcov-files-i386-y += hw/watchdog/watchdog.c hw/watchdog/wdt_ib700.c
check-qtest-i386-y += $(check-qtest-pci-y)
gcov-files-i386-y += $(gcov-files-pci-y)
@@ -174,6 +185,8 @@ gcov-files-i386-y += hw/usb/dev-storage.c
check-qtest-i386-y += tests/usb-hcd-xhci-test$(EXESUF)
gcov-files-i386-y += hw/usb/hcd-xhci.c
check-qtest-i386-y += tests/pc-cpu-test$(EXESUF)
+check-qtest-i386-y += tests/q35-test$(EXESUF)
+gcov-files-i386-y += hw/pci-host/q35.c
check-qtest-i386-$(CONFIG_LINUX) += tests/vhost-user-test$(EXESUF)
check-qtest-x86_64-y = $(check-qtest-i386-y)
gcov-files-i386-y += i386-softmmu/hw/timer/mc146818rtc.c
@@ -201,26 +214,48 @@ gcov-files-ppc64-y += ppc64-softmmu/hw/ppc/spapr_pci.c
check-qtest-microblazeel-y = $(check-qtest-microblaze-y)
check-qtest-xtensaeb-y = $(check-qtest-xtensa-y)
-# qom-test works for all sysemu architectures:
-$(foreach target,$(SYSEMU_TARGET_LIST), \
- $(if $(findstring tests/qom-test$(EXESUF), $(check-qtest-$(target)-y)),, \
- $(eval check-qtest-$(target)-y += tests/qom-test$(EXESUF))))
+check-qtest-generic-y += tests/qom-test$(EXESUF)
check-qapi-schema-y := $(addprefix tests/qapi-schema/, \
- comments.json empty.json funny-char.json indented-expr.json \
- missing-colon.json missing-comma-list.json \
- missing-comma-object.json non-objects.json \
+ comments.json empty.json enum-empty.json enum-missing-data.json \
+ enum-wrong-data.json enum-int-member.json enum-dict-member.json \
+ enum-clash-member.json enum-max-member.json enum-union-clash.json \
+ enum-bad-name.json funny-char.json indented-expr.json \
+ missing-type.json bad-ident.json ident-with-escape.json \
+ escape-outside-string.json unknown-escape.json \
+ escape-too-short.json escape-too-big.json unicode-str.json \
+ double-type.json bad-base.json bad-type-bool.json bad-type-int.json \
+ bad-type-dict.json double-data.json unknown-expr-key.json \
+ redefined-type.json redefined-command.json redefined-builtin.json \
+ redefined-event.json command-int.json bad-data.json event-max.json \
+ type-bypass.json type-bypass-no-gen.json type-bypass-bad-gen.json \
+ data-array-empty.json data-array-unknown.json data-int.json \
+ data-unknown.json data-member-unknown.json data-member-array.json \
+ data-member-array-bad.json returns-array-bad.json returns-int.json \
+ returns-unknown.json returns-alternate.json returns-whitelist.json \
+ missing-colon.json missing-comma-list.json missing-comma-object.json \
+ nested-struct-data.json nested-struct-returns.json non-objects.json \
qapi-schema-test.json quoted-structural-chars.json \
trailing-comma-list.json trailing-comma-object.json \
unclosed-list.json unclosed-object.json unclosed-string.json \
- duplicate-key.json union-invalid-base.json flat-union-no-base.json \
- flat-union-invalid-discriminator.json \
+ duplicate-key.json union-invalid-base.json union-bad-branch.json \
+ union-optional-branch.json union-unknown.json union-max.json \
+ flat-union-optional-discriminator.json flat-union-no-base.json \
+ flat-union-invalid-discriminator.json flat-union-inline.json \
flat-union-invalid-branch-key.json flat-union-reverse-define.json \
- flat-union-string-discriminator.json \
+ flat-union-string-discriminator.json union-base-no-discriminator.json \
+ flat-union-bad-discriminator.json flat-union-bad-base.json \
+ flat-union-base-star.json \
+ flat-union-array-branch.json flat-union-int-branch.json \
+ flat-union-base-union.json flat-union-branch-clash.json \
+ alternate-nested.json alternate-unknown.json alternate-clash.json \
+ alternate-good.json alternate-base.json alternate-array.json \
+ alternate-conflict-string.json alternate-conflict-dict.json \
include-simple.json include-relpath.json include-format-err.json \
include-non-file.json include-no-file.json include-before-err.json \
include-nested-err.json include-self-cycle.json include-cycle.json \
- include-repetition.json event-nest-struct.json)
+ include-repetition.json event-nest-struct.json event-case.json \
+ struct-base-clash.json struct-base-clash-deep.json )
GENERATED_HEADERS += tests/test-qapi-types.h tests/test-qapi-visit.h \
tests/test-qmp-commands.h tests/test-qapi-event.h
@@ -240,7 +275,7 @@ test-qapi-obj-y = tests/test-qapi-visit.o tests/test-qapi-types.o \
$(test-obj-y): QEMU_INCLUDES += -Itests
QEMU_CFLAGS += -I$(SRC_PATH)/tests
-qom-core-obj = qom/object.o qom/qom-qobject.o qom/container.o
+qom-core-obj = qom/object.o qom/qom-qobject.o qom/container.o qom/object_interfaces.o
tests/check-qint$(EXESUF): tests/check-qint.o libqemuutil.a
tests/check-qstring$(EXESUF): tests/check-qstring.o libqemuutil.a
@@ -249,6 +284,7 @@ tests/check-qlist$(EXESUF): tests/check-qlist.o libqemuutil.a
tests/check-qfloat$(EXESUF): tests/check-qfloat.o libqemuutil.a
tests/check-qjson$(EXESUF): tests/check-qjson.o libqemuutil.a libqemustub.a
tests/check-qom-interface$(EXESUF): tests/check-qom-interface.o $(qom-core-obj) libqemuutil.a libqemustub.a
+tests/check-qom-proplist$(EXESUF): tests/check-qom-proplist.o $(qom-core-obj) libqemuutil.a libqemustub.a
tests/test-coroutine$(EXESUF): tests/test-coroutine.o $(block-obj-y) libqemuutil.a libqemustub.a
tests/test-aio$(EXESUF): tests/test-aio.o $(block-obj-y) libqemuutil.a libqemustub.a
tests/test-rfifolock$(EXESUF): tests/test-rfifolock.o libqemuutil.a libqemustub.a
@@ -277,24 +313,24 @@ tests/test-vmstate$(EXESUF): tests/test-vmstate.o \
libqemuutil.a libqemustub.a
tests/test-qapi-types.c tests/test-qapi-types.h :\
-$(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-types.py
+$(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-types.py $(qapi-py)
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-types.py \
- $(gen-out-type) -o tests -p "test-" -i $<, \
+ $(gen-out-type) -o tests -p "test-" $<, \
" GEN $@")
tests/test-qapi-visit.c tests/test-qapi-visit.h :\
-$(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-visit.py
+$(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-visit.py $(qapi-py)
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-visit.py \
- $(gen-out-type) -o tests -p "test-" -i $<, \
+ $(gen-out-type) -o tests -p "test-" $<, \
" GEN $@")
tests/test-qmp-commands.h tests/test-qmp-marshal.c :\
-$(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-commands.py
+$(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py)
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py \
- $(gen-out-type) -o tests -p "test-" -i $<, \
+ $(gen-out-type) -o tests -p "test-" $<, \
" GEN $@")
tests/test-qapi-event.c tests/test-qapi-event.h :\
-$(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-event.py
+$(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-event.py $(qapi-py)
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-event.py \
- $(gen-out-type) -o tests -p "test-" -i $<, \
+ $(gen-out-type) -o tests -p "test-" $<, \
" GEN $@")
tests/test-string-output-visitor$(EXESUF): tests/test-string-output-visitor.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a
@@ -309,6 +345,8 @@ tests/test-opts-visitor$(EXESUF): tests/test-opts-visitor.o $(test-qapi-obj-y) l
tests/test-mul64$(EXESUF): tests/test-mul64.o libqemuutil.a
tests/test-bitops$(EXESUF): tests/test-bitops.o libqemuutil.a
+tests/test-crypto-hash$(EXESUF): tests/test-crypto-hash.o libqemuutil.a libqemustub.a
+tests/test-crypto-cipher$(EXESUF): tests/test-crypto-cipher.o libqemuutil.a libqemustub.a
libqos-obj-y = tests/libqos/pci.o tests/libqos/fw_cfg.o tests/libqos/malloc.o
libqos-obj-y += tests/libqos/i2c.o tests/libqos/libqos.o
@@ -319,6 +357,7 @@ libqos-omap-obj-y = $(libqos-obj-y) tests/libqos/i2c-omap.o
libqos-usb-obj-y = $(libqos-pc-obj-y) tests/libqos/usb.o
libqos-virtio-obj-y = $(libqos-pc-obj-y) tests/libqos/virtio.o tests/libqos/virtio-pci.o tests/libqos/virtio-mmio.o tests/libqos/malloc-generic.o
+tests/device-introspect-test$(EXESUF): tests/device-introspect-test.o
tests/rtc-test$(EXESUF): tests/rtc-test.o
tests/m48t59-test$(EXESUF): tests/m48t59-test.o
tests/endianness-test$(EXESUF): tests/endianness-test.o
@@ -331,6 +370,7 @@ tests/boot-order-test$(EXESUF): tests/boot-order-test.o $(libqos-obj-y)
tests/bios-tables-test$(EXESUF): tests/bios-tables-test.o $(libqos-obj-y)
tests/tmp105-test$(EXESUF): tests/tmp105-test.o $(libqos-omap-obj-y)
tests/i440fx-test$(EXESUF): tests/i440fx-test.o $(libqos-pc-obj-y)
+tests/q35-test$(EXESUF): tests/q35-test.o $(libqos-pc-obj-y)
tests/fw_cfg-test$(EXESUF): tests/fw_cfg-test.o $(libqos-pc-obj-y)
tests/e1000-test$(EXESUF): tests/e1000-test.o
tests/rtl8139-test$(EXESUF): tests/rtl8139-test.o $(libqos-pc-obj-y)
@@ -339,11 +379,12 @@ tests/eepro100-test$(EXESUF): tests/eepro100-test.o
tests/vmxnet3-test$(EXESUF): tests/vmxnet3-test.o
tests/ne2000-test$(EXESUF): tests/ne2000-test.o
tests/wdt_ib700-test$(EXESUF): tests/wdt_ib700-test.o
+tests/tco-test$(EXESUF): tests/tco-test.o $(libqos-pc-obj-y)
tests/virtio-balloon-test$(EXESUF): tests/virtio-balloon-test.o
tests/virtio-blk-test$(EXESUF): tests/virtio-blk-test.o $(libqos-virtio-obj-y)
tests/virtio-net-test$(EXESUF): tests/virtio-net-test.o $(libqos-pc-obj-y)
tests/virtio-rng-test$(EXESUF): tests/virtio-rng-test.o $(libqos-pc-obj-y)
-tests/virtio-scsi-test$(EXESUF): tests/virtio-scsi-test.o
+tests/virtio-scsi-test$(EXESUF): tests/virtio-scsi-test.o $(libqos-virtio-obj-y)
tests/virtio-9p-test$(EXESUF): tests/virtio-9p-test.o
tests/virtio-serial-test$(EXESUF): tests/virtio-serial-test.o
tests/virtio-console-test$(EXESUF): tests/virtio-console-test.o
@@ -378,8 +419,11 @@ endif
TARGETS=$(patsubst %-softmmu,%, $(filter %-softmmu,$(TARGET_DIRS)))
ifeq ($(CONFIG_POSIX),y)
-QTEST_TARGETS=$(foreach TARGET,$(TARGETS), $(if $(check-qtest-$(TARGET)-y), $(TARGET),))
+QTEST_TARGETS = $(TARGETS)
check-qtest-y=$(foreach TARGET,$(TARGETS), $(check-qtest-$(TARGET)-y))
+check-qtest-y += $(check-qtest-generic-y)
+else
+QTEST_TARGETS =
endif
qtest-obj-y = tests/libqtest.o libqemuutil.a libqemustub.a
@@ -415,9 +459,10 @@ GCOV_OPTIONS = -n $(if $(V),-f,)
$(patsubst %, check-qtest-%, $(QTEST_TARGETS)): check-qtest-%: $(check-qtest-y)
$(if $(CONFIG_GCOV),@rm -f *.gcda */*.gcda */*/*.gcda */*/*/*.gcda,)
$(call quiet-command,QTEST_QEMU_BINARY=$*-softmmu/qemu-system-$* \
+ QTEST_QEMU_IMG=qemu-img$(EXESUF) \
MALLOC_PERTURB_=$${MALLOC_PERTURB_:-$$((RANDOM % 255 + 1))} \
- gtester $(GTESTER_OPTIONS) -m=$(SPEED) $(check-qtest-$*-y),"GTESTER $@")
- $(if $(CONFIG_GCOV),@for f in $(gcov-files-$*-y); do \
+ gtester $(GTESTER_OPTIONS) -m=$(SPEED) $(check-qtest-$*-y) $(check-qtest-generic-y),"GTESTER $@")
+ $(if $(CONFIG_GCOV),@for f in $(gcov-files-$*-y) $(gcov-files-generic-y); do \
echo Gcov report for $$f:;\
$(GCOV) $(GCOV_OPTIONS) $$f -o `dirname $$f`; \
done,)
@@ -428,7 +473,7 @@ $(patsubst %, check-%, $(check-unit-y)): check-%: %
$(call quiet-command, \
MALLOC_PERTURB_=$${MALLOC_PERTURB_:-$$((RANDOM % 255 + 1))} \
gtester $(GTESTER_OPTIONS) -m=$(SPEED) $*,"GTESTER $*")
- $(if $(CONFIG_GCOV),@for f in $(gcov-files-$(subst tests/,,$*)-y); do \
+ $(if $(CONFIG_GCOV),@for f in $(gcov-files-$(subst tests/,,$*)-y) $(gcov-files-generic-y); do \
echo Gcov report for $$f:;\
$(GCOV) $(GCOV_OPTIONS) $$f -o `dirname $$f`; \
done,)
@@ -437,6 +482,7 @@ $(patsubst %, check-%, $(check-unit-y)): check-%: %
$(patsubst %, check-report-qtest-%.xml, $(QTEST_TARGETS)): check-report-qtest-%.xml: $(check-qtest-y)
$(call quiet-command,QTEST_QEMU_BINARY=$*-softmmu/qemu-system-$* \
+ QTEST_QEMU_IMG=qemu-img$(EXESUF) \
gtester -q $(GTESTER_OPTIONS) -o $@ -m=$(SPEED) $(check-qtest-$*-y),"GTESTER $@")
check-report-unit.xml: $(check-unit-y)
@@ -445,10 +491,10 @@ check-report-unit.xml: $(check-unit-y)
# Reports and overall runs
check-report.xml: $(patsubst %,check-report-qtest-%.xml, $(QTEST_TARGETS)) check-report-unit.xml
- $(call quiet-command,$(SRC_PATH)/scripts/gtester-cat $^ > $@, " GEN $@")
+ $(call quiet-command,$(SRC_PATH)/scripts/gtester-cat $^ > $@, " GEN $@")
check-report.html: check-report.xml
- $(call quiet-command,gtester-report $< > $@, " GEN $@")
+ $(call quiet-command,gtester-report $< > $@, " GEN $@")
# Other tests
diff --git a/tests/acpi-test-data/pc/DSDT b/tests/acpi-test-data/pc/DSDT
index 1693c3783..c658203db 100644
--- a/tests/acpi-test-data/pc/DSDT
+++ b/tests/acpi-test-data/pc/DSDT
Binary files differ
diff --git a/tests/acpi-test-data/pc/SSDT b/tests/acpi-test-data/pc/SSDT
index 59be31523..210d6a71e 100644
--- a/tests/acpi-test-data/pc/SSDT
+++ b/tests/acpi-test-data/pc/SSDT
Binary files differ
diff --git a/tests/acpi-test-data/pc/SSDT.bridge b/tests/acpi-test-data/pc/SSDT.bridge
index fa6136935..6e6660b1f 100644
--- a/tests/acpi-test-data/pc/SSDT.bridge
+++ b/tests/acpi-test-data/pc/SSDT.bridge
Binary files differ
diff --git a/tests/acpi-test-data/q35/DSDT b/tests/acpi-test-data/q35/DSDT
index e9ac11c38..4723e5954 100644
--- a/tests/acpi-test-data/q35/DSDT
+++ b/tests/acpi-test-data/q35/DSDT
Binary files differ
diff --git a/tests/acpi-test-data/q35/SSDT b/tests/acpi-test-data/q35/SSDT
index e87f5a35c..0970c67dd 100644
--- a/tests/acpi-test-data/q35/SSDT
+++ b/tests/acpi-test-data/q35/SSDT
Binary files differ
diff --git a/tests/acpi-test-data/q35/SSDT.bridge b/tests/acpi-test-data/q35/SSDT.bridge
index b3cac34d7..a77868861 100644
--- a/tests/acpi-test-data/q35/SSDT.bridge
+++ b/tests/acpi-test-data/q35/SSDT.bridge
Binary files differ
diff --git a/tests/ahci-test.c b/tests/ahci-test.c
index ea62e249f..87d769186 100644
--- a/tests/ahci-test.c
+++ b/tests/ahci-test.c
@@ -39,11 +39,14 @@
#include "hw/pci/pci_ids.h"
#include "hw/pci/pci_regs.h"
-/* Test-specific defines. */
-#define TEST_IMAGE_SIZE (64 * 1024 * 1024)
+/* Test-specific defines -- in MiB */
+#define TEST_IMAGE_SIZE_MB (200 * 1024)
+#define TEST_IMAGE_SECTORS ((TEST_IMAGE_SIZE_MB / AHCI_SECTOR_SIZE) \
+ * 1024 * 1024)
/*** Globals ***/
static char tmp_path[] = "/tmp/qtest.XXXXXX";
+static char debug_path[] = "/tmp/qtest-blkdebug.XXXXXX";
static bool ahci_pedantic;
/*** Function Declarations ***/
@@ -94,24 +97,83 @@ static void generate_pattern(void *buffer, size_t len, size_t cycle_len)
}
}
+/**
+ * Verify that the transfer did not corrupt our state at all.
+ */
+static void verify_state(AHCIQState *ahci)
+{
+ int i, j;
+ uint32_t ahci_fingerprint;
+ uint64_t hba_base;
+ uint64_t hba_stored;
+ AHCICommandHeader cmd;
+
+ ahci_fingerprint = qpci_config_readl(ahci->dev, PCI_VENDOR_ID);
+ g_assert_cmphex(ahci_fingerprint, ==, ahci->fingerprint);
+
+ /* If we haven't initialized, this is as much as can be validated. */
+ if (!ahci->hba_base) {
+ return;
+ }
+
+ hba_base = (uint64_t)qpci_config_readl(ahci->dev, PCI_BASE_ADDRESS_5);
+ hba_stored = (uint64_t)(uintptr_t)ahci->hba_base;
+ g_assert_cmphex(hba_base, ==, hba_stored);
+
+ g_assert_cmphex(ahci_rreg(ahci, AHCI_CAP), ==, ahci->cap);
+ g_assert_cmphex(ahci_rreg(ahci, AHCI_CAP2), ==, ahci->cap2);
+
+ for (i = 0; i < 32; i++) {
+ g_assert_cmphex(ahci_px_rreg(ahci, i, AHCI_PX_FB), ==,
+ ahci->port[i].fb);
+ g_assert_cmphex(ahci_px_rreg(ahci, i, AHCI_PX_CLB), ==,
+ ahci->port[i].clb);
+ for (j = 0; j < 32; j++) {
+ ahci_get_command_header(ahci, i, j, &cmd);
+ g_assert_cmphex(cmd.prdtl, ==, ahci->port[i].prdtl[j]);
+ g_assert_cmphex(cmd.ctba, ==, ahci->port[i].ctba[j]);
+ }
+ }
+}
+
+static void ahci_migrate(AHCIQState *from, AHCIQState *to, const char *uri)
+{
+ QOSState *tmp = to->parent;
+ QPCIDevice *dev = to->dev;
+ if (uri == NULL) {
+ uri = "tcp:127.0.0.1:1234";
+ }
+
+ /* context will be 'to' after completion. */
+ migrate(from->parent, to->parent, uri);
+
+ /* We'd like for the AHCIState objects to still point
+ * to information specific to its specific parent
+ * instance, but otherwise just inherit the new data. */
+ memcpy(to, from, sizeof(AHCIQState));
+ to->parent = tmp;
+ to->dev = dev;
+
+ tmp = from->parent;
+ dev = from->dev;
+ memset(from, 0x00, sizeof(AHCIQState));
+ from->parent = tmp;
+ from->dev = dev;
+
+ verify_state(to);
+}
+
/*** Test Setup & Teardown ***/
/**
* Start a Q35 machine and bookmark a handle to the AHCI device.
*/
-static AHCIQState *ahci_boot(void)
+static AHCIQState *ahci_vboot(const char *cli, va_list ap)
{
AHCIQState *s;
- const char *cli;
s = g_malloc0(sizeof(AHCIQState));
-
- cli = "-drive if=none,id=drive0,file=%s,cache=writeback,serial=%s"
- ",format=raw"
- " -M q35 "
- "-device ide-hd,drive=drive0 "
- "-global ide-hd.ver=%s";
- s->parent = qtest_pc_boot(cli, tmp_path, "testdisk", "version");
+ s->parent = qtest_pc_vboot(cli, ap);
alloc_set_flags(s->parent->alloc, ALLOC_LEAK_ASSERT);
/* Verify that we have an AHCI device present. */
@@ -121,12 +183,37 @@ static AHCIQState *ahci_boot(void)
}
/**
+ * Start a Q35 machine and bookmark a handle to the AHCI device.
+ */
+static AHCIQState *ahci_boot(const char *cli, ...)
+{
+ AHCIQState *s;
+ va_list ap;
+
+ if (cli) {
+ va_start(ap, cli);
+ s = ahci_vboot(cli, ap);
+ va_end(ap);
+ } else {
+ cli = "-drive if=none,id=drive0,file=%s,cache=writeback,serial=%s"
+ ",format=qcow2"
+ " -M q35 "
+ "-device ide-hd,drive=drive0 "
+ "-global ide-hd.ver=%s";
+ s = ahci_boot(cli, tmp_path, "testdisk", "version");
+ }
+
+ return s;
+}
+
+/**
* Clean up the PCI device, then terminate the QEMU instance.
*/
static void ahci_shutdown(AHCIQState *ahci)
{
QOSState *qs = ahci->parent;
+ set_context(qs);
ahci_clean_mem(ahci);
free_ahci_device(ahci->dev);
g_free(ahci);
@@ -137,13 +224,27 @@ static void ahci_shutdown(AHCIQState *ahci)
* Boot and fully enable the HBA device.
* @see ahci_boot, ahci_pci_enable and ahci_hba_enable.
*/
-static AHCIQState *ahci_boot_and_enable(void)
+static AHCIQState *ahci_boot_and_enable(const char *cli, ...)
{
AHCIQState *ahci;
- ahci = ahci_boot();
+ va_list ap;
+ uint16_t buff[256];
+ uint8_t port;
+
+ if (cli) {
+ va_start(ap, cli);
+ ahci = ahci_vboot(cli, ap);
+ va_end(ap);
+ } else {
+ ahci = ahci_boot(NULL);
+ }
ahci_pci_enable(ahci);
ahci_hba_enable(ahci);
+ /* Initialize test device */
+ port = ahci_port_select(ahci);
+ ahci_port_clear(ahci, port);
+ ahci_io(ahci, port, CMD_IDENTIFY, &buff, sizeof(buff), 0);
return ahci;
}
@@ -738,7 +839,7 @@ static void ahci_test_identify(AHCIQState *ahci)
ahci_port_clear(ahci, px);
/* "Read" 512 bytes using CMD_IDENTIFY into the host buffer. */
- ahci_io(ahci, px, CMD_IDENTIFY, &buff, buffsize);
+ ahci_io(ahci, px, CMD_IDENTIFY, &buff, buffsize, 0);
/* Check serial number/version in the buffer */
/* NB: IDENTIFY strings are packed in 16bit little endian chunks.
@@ -754,11 +855,12 @@ static void ahci_test_identify(AHCIQState *ahci)
g_assert_cmphex(rc, ==, 0);
sect_size = le16_to_cpu(*((uint16_t *)(&buff[5])));
- g_assert_cmphex(sect_size, ==, 0x200);
+ g_assert_cmphex(sect_size, ==, AHCI_SECTOR_SIZE);
}
static void ahci_test_io_rw_simple(AHCIQState *ahci, unsigned bufsize,
- uint8_t read_cmd, uint8_t write_cmd)
+ uint64_t sector, uint8_t read_cmd,
+ uint8_t write_cmd)
{
uint64_t ptr;
uint8_t port;
@@ -778,15 +880,15 @@ static void ahci_test_io_rw_simple(AHCIQState *ahci, unsigned bufsize,
/* Write some indicative pattern to our buffer. */
generate_pattern(tx, bufsize, AHCI_SECTOR_SIZE);
- memwrite(ptr, tx, bufsize);
+ bufwrite(ptr, tx, bufsize);
/* Write this buffer to disk, then read it back to the DMA buffer. */
- ahci_guest_io(ahci, port, write_cmd, ptr, bufsize);
+ ahci_guest_io(ahci, port, write_cmd, ptr, bufsize, sector);
qmemset(ptr, 0x00, bufsize);
- ahci_guest_io(ahci, port, read_cmd, ptr, bufsize);
+ ahci_guest_io(ahci, port, read_cmd, ptr, bufsize, sector);
/*** Read back the Data ***/
- memread(ptr, rx, bufsize);
+ bufread(ptr, rx, bufsize);
g_assert_cmphex(memcmp(tx, rx, bufsize), ==, 0);
ahci_free(ahci, ptr);
@@ -794,6 +896,58 @@ static void ahci_test_io_rw_simple(AHCIQState *ahci, unsigned bufsize,
g_free(rx);
}
+static uint8_t ahci_test_nondata(AHCIQState *ahci, uint8_t ide_cmd)
+{
+ uint8_t port;
+ AHCICommand *cmd;
+
+ /* Sanitize */
+ port = ahci_port_select(ahci);
+ ahci_port_clear(ahci, port);
+
+ /* Issue Command */
+ cmd = ahci_command_create(ide_cmd);
+ ahci_command_commit(ahci, cmd, port);
+ ahci_command_issue(ahci, cmd);
+ ahci_command_verify(ahci, cmd);
+ ahci_command_free(cmd);
+
+ return port;
+}
+
+static void ahci_test_flush(AHCIQState *ahci)
+{
+ ahci_test_nondata(ahci, CMD_FLUSH_CACHE);
+}
+
+static void ahci_test_max(AHCIQState *ahci)
+{
+ RegD2HFIS *d2h = g_malloc0(0x20);
+ uint64_t nsect;
+ uint8_t port;
+ uint8_t cmd;
+ uint64_t config_sect = TEST_IMAGE_SECTORS - 1;
+
+ if (config_sect > 0xFFFFFF) {
+ cmd = CMD_READ_MAX_EXT;
+ } else {
+ cmd = CMD_READ_MAX;
+ }
+
+ port = ahci_test_nondata(ahci, cmd);
+ memread(ahci->port[port].fb + 0x40, d2h, 0x20);
+ nsect = (uint64_t)d2h->lba_hi[2] << 40 |
+ (uint64_t)d2h->lba_hi[1] << 32 |
+ (uint64_t)d2h->lba_hi[0] << 24 |
+ (uint64_t)d2h->lba_lo[2] << 16 |
+ (uint64_t)d2h->lba_lo[1] << 8 |
+ (uint64_t)d2h->lba_lo[0];
+
+ g_assert_cmphex(nsect, ==, config_sect);
+ g_free(d2h);
+}
+
+
/******************************************************************************/
/* Test Interfaces */
/******************************************************************************/
@@ -804,7 +958,7 @@ static void ahci_test_io_rw_simple(AHCIQState *ahci, unsigned bufsize,
static void test_sanity(void)
{
AHCIQState *ahci;
- ahci = ahci_boot();
+ ahci = ahci_boot(NULL);
ahci_shutdown(ahci);
}
@@ -815,7 +969,7 @@ static void test_sanity(void)
static void test_pci_spec(void)
{
AHCIQState *ahci;
- ahci = ahci_boot();
+ ahci = ahci_boot(NULL);
ahci_test_pci_spec(ahci);
ahci_shutdown(ahci);
}
@@ -827,8 +981,7 @@ static void test_pci_spec(void)
static void test_pci_enable(void)
{
AHCIQState *ahci;
-
- ahci = ahci_boot();
+ ahci = ahci_boot(NULL);
ahci_pci_enable(ahci);
ahci_shutdown(ahci);
}
@@ -841,7 +994,7 @@ static void test_hba_spec(void)
{
AHCIQState *ahci;
- ahci = ahci_boot();
+ ahci = ahci_boot(NULL);
ahci_pci_enable(ahci);
ahci_test_hba_spec(ahci);
ahci_shutdown(ahci);
@@ -855,7 +1008,7 @@ static void test_hba_enable(void)
{
AHCIQState *ahci;
- ahci = ahci_boot();
+ ahci = ahci_boot(NULL);
ahci_pci_enable(ahci);
ahci_hba_enable(ahci);
ahci_shutdown(ahci);
@@ -869,7 +1022,7 @@ static void test_identify(void)
{
AHCIQState *ahci;
- ahci = ahci_boot_and_enable();
+ ahci = ahci_boot_and_enable(NULL);
ahci_test_identify(ahci);
ahci_shutdown(ahci);
}
@@ -890,7 +1043,7 @@ static void test_dma_fragmented(void)
unsigned char *rx = g_malloc0(bufsize);
uint64_t ptr;
- ahci = ahci_boot_and_enable();
+ ahci = ahci_boot_and_enable(NULL);
px = ahci_port_select(ahci);
ahci_port_clear(ahci, px);
@@ -900,7 +1053,7 @@ static void test_dma_fragmented(void)
/* Create a DMA buffer in guest memory, and write our pattern to it. */
ptr = guest_alloc(ahci->parent->alloc, bufsize);
g_assert(ptr);
- memwrite(ptr, tx, bufsize);
+ bufwrite(ptr, tx, bufsize);
cmd = ahci_command_create(CMD_WRITE_DMA);
ahci_command_adjust(cmd, 0, ptr, bufsize, 32);
@@ -917,7 +1070,7 @@ static void test_dma_fragmented(void)
g_free(cmd);
/* Read back the guest's receive buffer into local memory */
- memread(ptr, rx, bufsize);
+ bufread(ptr, rx, bufsize);
guest_free(ahci->parent->alloc, ptr);
g_assert_cmphex(memcmp(tx, rx, bufsize), ==, 0);
@@ -928,6 +1081,367 @@ static void test_dma_fragmented(void)
g_free(tx);
}
+static void test_flush(void)
+{
+ AHCIQState *ahci;
+
+ ahci = ahci_boot_and_enable(NULL);
+ ahci_test_flush(ahci);
+ ahci_shutdown(ahci);
+}
+
+static void test_flush_retry(void)
+{
+ AHCIQState *ahci;
+ AHCICommand *cmd;
+ uint8_t port;
+ const char *s;
+
+ prepare_blkdebug_script(debug_path, "flush_to_disk");
+ ahci = ahci_boot_and_enable("-drive file=blkdebug:%s:%s,if=none,id=drive0,"
+ "format=qcow2,cache=writeback,"
+ "rerror=stop,werror=stop "
+ "-M q35 "
+ "-device ide-hd,drive=drive0 ",
+ debug_path,
+ tmp_path);
+
+ /* Issue Flush Command and wait for error */
+ port = ahci_port_select(ahci);
+ ahci_port_clear(ahci, port);
+ cmd = ahci_command_create(CMD_FLUSH_CACHE);
+ ahci_command_commit(ahci, cmd, port);
+ ahci_command_issue_async(ahci, cmd);
+ qmp_eventwait("STOP");
+
+ /* Complete the command */
+ s = "{'execute':'cont' }";
+ qmp_async(s);
+ qmp_eventwait("RESUME");
+ ahci_command_wait(ahci, cmd);
+ ahci_command_verify(ahci, cmd);
+
+ ahci_command_free(cmd);
+ ahci_shutdown(ahci);
+}
+
+/**
+ * Basic sanity test to boot a machine, find an AHCI device, and shutdown.
+ */
+static void test_migrate_sanity(void)
+{
+ AHCIQState *src, *dst;
+ const char *uri = "tcp:127.0.0.1:1234";
+
+ src = ahci_boot("-m 1024 -M q35 "
+ "-hda %s ", tmp_path);
+ dst = ahci_boot("-m 1024 -M q35 "
+ "-hda %s "
+ "-incoming %s", tmp_path, uri);
+
+ ahci_migrate(src, dst, uri);
+
+ ahci_shutdown(src);
+ ahci_shutdown(dst);
+}
+
+/**
+ * Simple migration test: Write a pattern, migrate, then read.
+ */
+static void ahci_migrate_simple(uint8_t cmd_read, uint8_t cmd_write)
+{
+ AHCIQState *src, *dst;
+ uint8_t px;
+ size_t bufsize = 4096;
+ unsigned char *tx = g_malloc(bufsize);
+ unsigned char *rx = g_malloc0(bufsize);
+ unsigned i;
+ const char *uri = "tcp:127.0.0.1:1234";
+
+ src = ahci_boot_and_enable("-m 1024 -M q35 "
+ "-hda %s ", tmp_path);
+ dst = ahci_boot("-m 1024 -M q35 "
+ "-hda %s "
+ "-incoming %s", tmp_path, uri);
+
+ set_context(src->parent);
+
+ /* initialize */
+ px = ahci_port_select(src);
+ ahci_port_clear(src, px);
+
+ /* create pattern */
+ for (i = 0; i < bufsize; i++) {
+ tx[i] = (bufsize - i);
+ }
+
+ /* Write, migrate, then read. */
+ ahci_io(src, px, cmd_write, tx, bufsize, 0);
+ ahci_migrate(src, dst, uri);
+ ahci_io(dst, px, cmd_read, rx, bufsize, 0);
+
+ /* Verify pattern */
+ g_assert_cmphex(memcmp(tx, rx, bufsize), ==, 0);
+
+ ahci_shutdown(src);
+ ahci_shutdown(dst);
+ g_free(rx);
+ g_free(tx);
+}
+
+static void test_migrate_dma(void)
+{
+ ahci_migrate_simple(CMD_READ_DMA, CMD_WRITE_DMA);
+}
+
+static void test_migrate_ncq(void)
+{
+ ahci_migrate_simple(READ_FPDMA_QUEUED, WRITE_FPDMA_QUEUED);
+}
+
+/**
+ * Halted IO Error Test
+ *
+ * Simulate an error on first write, Try to write a pattern,
+ * Confirm the VM has stopped, resume the VM, verify command
+ * has completed, then read back the data and verify.
+ */
+static void ahci_halted_io_test(uint8_t cmd_read, uint8_t cmd_write)
+{
+ AHCIQState *ahci;
+ uint8_t port;
+ size_t bufsize = 4096;
+ unsigned char *tx = g_malloc(bufsize);
+ unsigned char *rx = g_malloc0(bufsize);
+ unsigned i;
+ uint64_t ptr;
+ AHCICommand *cmd;
+
+ prepare_blkdebug_script(debug_path, "write_aio");
+
+ ahci = ahci_boot_and_enable("-drive file=blkdebug:%s:%s,if=none,id=drive0,"
+ "format=qcow2,cache=writeback,"
+ "rerror=stop,werror=stop "
+ "-M q35 "
+ "-device ide-hd,drive=drive0 ",
+ debug_path,
+ tmp_path);
+
+ /* Initialize and prepare */
+ port = ahci_port_select(ahci);
+ ahci_port_clear(ahci, port);
+
+ for (i = 0; i < bufsize; i++) {
+ tx[i] = (bufsize - i);
+ }
+
+ /* create DMA source buffer and write pattern */
+ ptr = ahci_alloc(ahci, bufsize);
+ g_assert(ptr);
+ memwrite(ptr, tx, bufsize);
+
+ /* Attempt to write (and fail) */
+ cmd = ahci_guest_io_halt(ahci, port, cmd_write,
+ ptr, bufsize, 0);
+
+ /* Attempt to resume the command */
+ ahci_guest_io_resume(ahci, cmd);
+ ahci_free(ahci, ptr);
+
+ /* Read back and verify */
+ ahci_io(ahci, port, cmd_read, rx, bufsize, 0);
+ g_assert_cmphex(memcmp(tx, rx, bufsize), ==, 0);
+
+ /* Cleanup and go home */
+ ahci_shutdown(ahci);
+ g_free(rx);
+ g_free(tx);
+}
+
+static void test_halted_dma(void)
+{
+ ahci_halted_io_test(CMD_READ_DMA, CMD_WRITE_DMA);
+}
+
+static void test_halted_ncq(void)
+{
+ ahci_halted_io_test(READ_FPDMA_QUEUED, WRITE_FPDMA_QUEUED);
+}
+
+/**
+ * IO Error Migration Test
+ *
+ * Simulate an error on first write, Try to write a pattern,
+ * Confirm the VM has stopped, migrate, resume the VM,
+ * verify command has completed, then read back the data and verify.
+ */
+static void ahci_migrate_halted_io(uint8_t cmd_read, uint8_t cmd_write)
+{
+ AHCIQState *src, *dst;
+ uint8_t port;
+ size_t bufsize = 4096;
+ unsigned char *tx = g_malloc(bufsize);
+ unsigned char *rx = g_malloc0(bufsize);
+ unsigned i;
+ uint64_t ptr;
+ AHCICommand *cmd;
+ const char *uri = "tcp:127.0.0.1:1234";
+
+ prepare_blkdebug_script(debug_path, "write_aio");
+
+ src = ahci_boot_and_enable("-drive file=blkdebug:%s:%s,if=none,id=drive0,"
+ "format=qcow2,cache=writeback,"
+ "rerror=stop,werror=stop "
+ "-M q35 "
+ "-device ide-hd,drive=drive0 ",
+ debug_path,
+ tmp_path);
+
+ dst = ahci_boot("-drive file=%s,if=none,id=drive0,"
+ "format=qcow2,cache=writeback,"
+ "rerror=stop,werror=stop "
+ "-M q35 "
+ "-device ide-hd,drive=drive0 "
+ "-incoming %s",
+ tmp_path, uri);
+
+ set_context(src->parent);
+
+ /* Initialize and prepare */
+ port = ahci_port_select(src);
+ ahci_port_clear(src, port);
+
+ for (i = 0; i < bufsize; i++) {
+ tx[i] = (bufsize - i);
+ }
+
+ /* create DMA source buffer and write pattern */
+ ptr = ahci_alloc(src, bufsize);
+ g_assert(ptr);
+ memwrite(ptr, tx, bufsize);
+
+ /* Write, trigger the VM to stop, migrate, then resume. */
+ cmd = ahci_guest_io_halt(src, port, cmd_write,
+ ptr, bufsize, 0);
+ ahci_migrate(src, dst, uri);
+ ahci_guest_io_resume(dst, cmd);
+ ahci_free(dst, ptr);
+
+ /* Read back */
+ ahci_io(dst, port, cmd_read, rx, bufsize, 0);
+
+ /* Verify TX and RX are identical */
+ g_assert_cmphex(memcmp(tx, rx, bufsize), ==, 0);
+
+ /* Cleanup and go home. */
+ ahci_shutdown(src);
+ ahci_shutdown(dst);
+ g_free(rx);
+ g_free(tx);
+}
+
+static void test_migrate_halted_dma(void)
+{
+ ahci_migrate_halted_io(CMD_READ_DMA, CMD_WRITE_DMA);
+}
+
+static void test_migrate_halted_ncq(void)
+{
+ ahci_migrate_halted_io(READ_FPDMA_QUEUED, WRITE_FPDMA_QUEUED);
+}
+
+/**
+ * Migration test: Try to flush, migrate, then resume.
+ */
+static void test_flush_migrate(void)
+{
+ AHCIQState *src, *dst;
+ AHCICommand *cmd;
+ uint8_t px;
+ const char *s;
+ const char *uri = "tcp:127.0.0.1:1234";
+
+ prepare_blkdebug_script(debug_path, "flush_to_disk");
+
+ src = ahci_boot_and_enable("-drive file=blkdebug:%s:%s,if=none,id=drive0,"
+ "cache=writeback,rerror=stop,werror=stop "
+ "-M q35 "
+ "-device ide-hd,drive=drive0 ",
+ debug_path, tmp_path);
+ dst = ahci_boot("-drive file=%s,if=none,id=drive0,"
+ "cache=writeback,rerror=stop,werror=stop "
+ "-M q35 "
+ "-device ide-hd,drive=drive0 "
+ "-incoming %s", tmp_path, uri);
+
+ set_context(src->parent);
+
+ /* Issue Flush Command */
+ px = ahci_port_select(src);
+ ahci_port_clear(src, px);
+ cmd = ahci_command_create(CMD_FLUSH_CACHE);
+ ahci_command_commit(src, cmd, px);
+ ahci_command_issue_async(src, cmd);
+ qmp_eventwait("STOP");
+
+ /* Migrate over */
+ ahci_migrate(src, dst, uri);
+
+ /* Complete the command */
+ s = "{'execute':'cont' }";
+ qmp_async(s);
+ qmp_eventwait("RESUME");
+ ahci_command_wait(dst, cmd);
+ ahci_command_verify(dst, cmd);
+
+ ahci_command_free(cmd);
+ ahci_shutdown(src);
+ ahci_shutdown(dst);
+}
+
+static void test_max(void)
+{
+ AHCIQState *ahci;
+
+ ahci = ahci_boot_and_enable(NULL);
+ ahci_test_max(ahci);
+ ahci_shutdown(ahci);
+}
+
+static void test_reset(void)
+{
+ AHCIQState *ahci;
+ int i;
+
+ ahci = ahci_boot(NULL);
+ ahci_test_pci_spec(ahci);
+ ahci_pci_enable(ahci);
+
+ for (i = 0; i < 2; i++) {
+ ahci_test_hba_spec(ahci);
+ ahci_hba_enable(ahci);
+ ahci_test_identify(ahci);
+ ahci_test_io_rw_simple(ahci, 4096, 0,
+ CMD_READ_DMA_EXT,
+ CMD_WRITE_DMA_EXT);
+ ahci_set(ahci, AHCI_GHC, AHCI_GHC_HR);
+ ahci_clean_mem(ahci);
+ }
+
+ ahci_shutdown(ahci);
+}
+
+static void test_ncq_simple(void)
+{
+ AHCIQState *ahci;
+
+ ahci = ahci_boot_and_enable(NULL);
+ ahci_test_io_rw_simple(ahci, 4096, 0,
+ READ_FPDMA_QUEUED,
+ WRITE_FPDMA_QUEUED);
+ ahci_shutdown(ahci);
+}
+
/******************************************************************************/
/* AHCI I/O Test Matrix Definitions */
@@ -968,12 +1482,45 @@ enum IOOps {
NUM_IO_OPS
};
+enum OffsetType {
+ OFFSET_BEGIN = 0,
+ OFFSET_ZERO = OFFSET_BEGIN,
+ OFFSET_LOW,
+ OFFSET_HIGH,
+ NUM_OFFSETS
+};
+
+static const char *offset_str[NUM_OFFSETS] = { "zero", "low", "high" };
+
typedef struct AHCIIOTestOptions {
enum BuffLen length;
enum AddrMode address_type;
enum IOMode io_type;
+ enum OffsetType offset;
} AHCIIOTestOptions;
+static uint64_t offset_sector(enum OffsetType ofst,
+ enum AddrMode addr_type,
+ uint64_t buffsize)
+{
+ uint64_t ceil;
+ uint64_t nsectors;
+
+ switch (ofst) {
+ case OFFSET_ZERO:
+ return 0;
+ case OFFSET_LOW:
+ return 1;
+ case OFFSET_HIGH:
+ ceil = (addr_type == ADDR_MODE_LBA28) ? 0xfffffff : 0xffffffffffff;
+ ceil = MIN(ceil, TEST_IMAGE_SECTORS - 1);
+ nsectors = buffsize / AHCI_SECTOR_SIZE;
+ return ceil - nsectors + 1;
+ default:
+ g_assert_not_reached();
+ }
+}
+
/**
* Table of possible I/O ATA commands given a set of enumerations.
*/
@@ -1001,12 +1548,12 @@ static const uint8_t io_cmds[NUM_MODES][NUM_ADDR_MODES][NUM_IO_OPS] = {
* transfer modes, and buffer sizes.
*/
static void test_io_rw_interface(enum AddrMode lba48, enum IOMode dma,
- unsigned bufsize)
+ unsigned bufsize, uint64_t sector)
{
AHCIQState *ahci;
- ahci = ahci_boot_and_enable();
- ahci_test_io_rw_simple(ahci, bufsize,
+ ahci = ahci_boot_and_enable(NULL);
+ ahci_test_io_rw_simple(ahci, bufsize, sector,
io_cmds[dma][lba48][IO_READ],
io_cmds[dma][lba48][IO_WRITE]);
ahci_shutdown(ahci);
@@ -1019,6 +1566,7 @@ static void test_io_interface(gconstpointer opaque)
{
AHCIIOTestOptions *opts = (AHCIIOTestOptions *)opaque;
unsigned bufsize;
+ uint64_t sector;
switch (opts->length) {
case LEN_SIMPLE:
@@ -1037,32 +1585,30 @@ static void test_io_interface(gconstpointer opaque)
g_assert_not_reached();
}
- test_io_rw_interface(opts->address_type, opts->io_type, bufsize);
+ sector = offset_sector(opts->offset, opts->address_type, bufsize);
+ test_io_rw_interface(opts->address_type, opts->io_type, bufsize, sector);
g_free(opts);
return;
}
static void create_ahci_io_test(enum IOMode type, enum AddrMode addr,
- enum BuffLen len)
+ enum BuffLen len, enum OffsetType offset)
{
- static const char *arch;
char *name;
AHCIIOTestOptions *opts = g_malloc(sizeof(AHCIIOTestOptions));
opts->length = len;
opts->address_type = addr;
opts->io_type = type;
+ opts->offset = offset;
- if (!arch) {
- arch = qtest_get_arch();
- }
-
- name = g_strdup_printf("/%s/ahci/io/%s/%s/%s", arch,
+ name = g_strdup_printf("ahci/io/%s/%s/%s/%s",
io_mode_str[type],
addr_mode_str[addr],
- buff_len_str[len]);
+ buff_len_str[len],
+ offset_str[offset]);
- g_test_add_data_func(name, opts, test_io_interface);
+ qtest_add_data_func(name, opts, test_io_interface);
g_free(name);
}
@@ -1071,10 +1617,10 @@ static void create_ahci_io_test(enum IOMode type, enum AddrMode addr,
int main(int argc, char **argv)
{
const char *arch;
- int fd;
int ret;
+ int fd;
int c;
- int i, j, k;
+ int i, j, k, m;
static struct option long_options[] = {
{"pedantic", no_argument, 0, 'p' },
@@ -1108,11 +1654,13 @@ int main(int argc, char **argv)
return 0;
}
- /* Create a temporary raw image */
- fd = mkstemp(tmp_path);
+ /* Create a temporary qcow2 image */
+ close(mkstemp(tmp_path));
+ mkqcow2(tmp_path, TEST_IMAGE_SIZE_MB);
+
+ /* Create temporary blkdebug instructions */
+ fd = mkstemp(debug_path);
g_assert(fd >= 0);
- ret = ftruncate(fd, TEST_IMAGE_SIZE);
- g_assert(ret == 0);
close(fd);
/* Run the tests */
@@ -1126,17 +1674,37 @@ int main(int argc, char **argv)
for (i = MODE_BEGIN; i < NUM_MODES; i++) {
for (j = ADDR_MODE_BEGIN; j < NUM_ADDR_MODES; j++) {
for (k = LEN_BEGIN; k < NUM_LENGTHS; k++) {
- create_ahci_io_test(i, j, k);
+ for (m = OFFSET_BEGIN; m < NUM_OFFSETS; m++) {
+ create_ahci_io_test(i, j, k, m);
+ }
}
}
}
qtest_add_func("/ahci/io/dma/lba28/fragmented", test_dma_fragmented);
+ qtest_add_func("/ahci/flush/simple", test_flush);
+ qtest_add_func("/ahci/flush/retry", test_flush_retry);
+ qtest_add_func("/ahci/flush/migrate", test_flush_migrate);
+
+ qtest_add_func("/ahci/migrate/sanity", test_migrate_sanity);
+ qtest_add_func("/ahci/migrate/dma/simple", test_migrate_dma);
+ qtest_add_func("/ahci/io/dma/lba28/retry", test_halted_dma);
+ qtest_add_func("/ahci/migrate/dma/halted", test_migrate_halted_dma);
+
+ qtest_add_func("/ahci/max", test_max);
+ qtest_add_func("/ahci/reset", test_reset);
+
+ qtest_add_func("/ahci/io/ncq/simple", test_ncq_simple);
+ qtest_add_func("/ahci/migrate/ncq/simple", test_migrate_ncq);
+ qtest_add_func("/ahci/io/ncq/retry", test_halted_ncq);
+ qtest_add_func("/ahci/migrate/ncq/halted", test_migrate_halted_ncq);
+
ret = g_test_run();
/* Cleanup */
unlink(tmp_path);
+ unlink(debug_path);
return ret;
}
diff --git a/tests/bios-tables-test.c b/tests/bios-tables-test.c
index 735ac610b..0de1742d7 100644
--- a/tests/bios-tables-test.c
+++ b/tests/bios-tables-test.c
@@ -17,7 +17,7 @@
#include "qemu-common.h"
#include "libqtest.h"
#include "qemu/compiler.h"
-#include "hw/i386/acpi-defs.h"
+#include "hw/acpi/acpi-defs.h"
#include "hw/i386/smbios.h"
#include "qemu/bitmap.h"
@@ -599,35 +599,15 @@ static void test_acpi_asl(test_data *data)
free_test_data(&exp_data);
}
-static void test_smbios_ep_address(test_data *data)
-{
- uint32_t off;
-
- /* find smbios entry point structure */
- for (off = 0xf0000; off < 0x100000; off += 0x10) {
- uint8_t sig[] = "_SM_";
- int i;
-
- for (i = 0; i < sizeof sig - 1; ++i) {
- sig[i] = readb(off + i);
- }
-
- if (!memcmp(sig, "_SM_", sizeof sig)) {
- break;
- }
- }
-
- g_assert_cmphex(off, <, 0x100000);
- data->smbios_ep_addr = off;
-}
-
-static void test_smbios_ep_table(test_data *data)
+static bool smbios_ep_table_ok(test_data *data)
{
struct smbios_entry_point *ep_table = &data->smbios_ep_table;
uint32_t addr = data->smbios_ep_addr;
ACPI_READ_ARRAY(ep_table->anchor_string, addr);
- g_assert(!memcmp(ep_table->anchor_string, "_SM_", 4));
+ if (memcmp(ep_table->anchor_string, "_SM_", 4)) {
+ return false;
+ }
ACPI_READ_FIELD(ep_table->checksum, addr);
ACPI_READ_FIELD(ep_table->length, addr);
ACPI_READ_FIELD(ep_table->smbios_major_version, addr);
@@ -636,17 +616,50 @@ static void test_smbios_ep_table(test_data *data)
ACPI_READ_FIELD(ep_table->entry_point_revision, addr);
ACPI_READ_ARRAY(ep_table->formatted_area, addr);
ACPI_READ_ARRAY(ep_table->intermediate_anchor_string, addr);
- g_assert(!memcmp(ep_table->intermediate_anchor_string, "_DMI_", 5));
+ if (memcmp(ep_table->intermediate_anchor_string, "_DMI_", 5)) {
+ return false;
+ }
ACPI_READ_FIELD(ep_table->intermediate_checksum, addr);
ACPI_READ_FIELD(ep_table->structure_table_length, addr);
- g_assert_cmpuint(ep_table->structure_table_length, >, 0);
+ if (ep_table->structure_table_length == 0) {
+ return false;
+ }
ACPI_READ_FIELD(ep_table->structure_table_address, addr);
ACPI_READ_FIELD(ep_table->number_of_structures, addr);
- g_assert_cmpuint(ep_table->number_of_structures, >, 0);
+ if (ep_table->number_of_structures == 0) {
+ return false;
+ }
ACPI_READ_FIELD(ep_table->smbios_bcd_revision, addr);
- g_assert(!acpi_checksum((uint8_t *)ep_table, sizeof *ep_table));
- g_assert(!acpi_checksum((uint8_t *)ep_table + 0x10,
- sizeof *ep_table - 0x10));
+ if (acpi_checksum((uint8_t *)ep_table, sizeof *ep_table) ||
+ acpi_checksum((uint8_t *)ep_table + 0x10, sizeof *ep_table - 0x10)) {
+ return false;
+ }
+ return true;
+}
+
+static void test_smbios_entry_point(test_data *data)
+{
+ uint32_t off;
+
+ /* find smbios entry point structure */
+ for (off = 0xf0000; off < 0x100000; off += 0x10) {
+ uint8_t sig[] = "_SM_";
+ int i;
+
+ for (i = 0; i < sizeof sig - 1; ++i) {
+ sig[i] = readb(off + i);
+ }
+
+ if (!memcmp(sig, "_SM_", sizeof sig)) {
+ /* signature match, but is this a valid entry point? */
+ data->smbios_ep_addr = off;
+ if (smbios_ep_table_ok(data)) {
+ break;
+ }
+ }
+ }
+
+ g_assert_cmphex(off, <, 0x100000);
}
static inline bool smbios_single_instance(uint8_t type)
@@ -767,8 +780,7 @@ static void test_acpi_one(const char *params, test_data *data)
}
}
- test_smbios_ep_address(data);
- test_smbios_ep_table(data);
+ test_smbios_entry_point(data);
test_smbios_structs(data);
qtest_quit(global_qtest);
diff --git a/tests/check-qdict.c b/tests/check-qdict.c
index a9296f083..a136f2add 100644
--- a/tests/check-qdict.c
+++ b/tests/check-qdict.c
@@ -152,6 +152,28 @@ static void qdict_get_try_str_test(void)
QDECREF(tests_dict);
}
+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");
+
+ QDECREF(copy);
+ QDECREF(dict);
+}
+
static void qdict_haskey_not_test(void)
{
QDict *tests_dict = qdict_new();
@@ -444,6 +466,49 @@ static void qdict_array_split_test(void)
QDECREF(test_dict);
}
+static void qdict_array_entries_test(void)
+{
+ QDict *dict = qdict_new();
+
+ g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 0);
+
+ qdict_put(dict, "bar", qint_from_int(0));
+ qdict_put(dict, "baz.0", qint_from_int(0));
+ g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 0);
+
+ qdict_put(dict, "foo.1", qint_from_int(0));
+ g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, -EINVAL);
+ qdict_put(dict, "foo.0", qint_from_int(0));
+ g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 2);
+ qdict_put(dict, "foo.bar", qint_from_int(0));
+ g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, -EINVAL);
+ qdict_del(dict, "foo.bar");
+
+ qdict_put(dict, "foo.2.a", qint_from_int(0));
+ qdict_put(dict, "foo.2.b", qint_from_int(0));
+ qdict_put(dict, "foo.2.c", qint_from_int(0));
+ g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 3);
+ g_assert_cmpint(qdict_array_entries(dict, ""), ==, -EINVAL);
+
+ QDECREF(dict);
+
+ dict = qdict_new();
+ qdict_put(dict, "1", qint_from_int(0));
+ g_assert_cmpint(qdict_array_entries(dict, ""), ==, -EINVAL);
+ qdict_put(dict, "0", qint_from_int(0));
+ g_assert_cmpint(qdict_array_entries(dict, ""), ==, 2);
+ qdict_put(dict, "bar", qint_from_int(0));
+ g_assert_cmpint(qdict_array_entries(dict, ""), ==, -EINVAL);
+ qdict_del(dict, "bar");
+
+ qdict_put(dict, "2.a", qint_from_int(0));
+ qdict_put(dict, "2.b", qint_from_int(0));
+ qdict_put(dict, "2.c", qint_from_int(0));
+ g_assert_cmpint(qdict_array_entries(dict, ""), ==, 3);
+
+ QDECREF(dict);
+}
+
static void qdict_join_test(void)
{
QDict *dict1, *dict2;
@@ -663,6 +728,7 @@ int main(int argc, char **argv)
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/defaults", qdict_defaults_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);
@@ -670,6 +736,7 @@ int main(int argc, char **argv)
g_test_add_func("/public/iterapi", qdict_iterapi_test);
g_test_add_func("/public/flatten", qdict_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("/errors/put_exists", qdict_put_exists_test);
diff --git a/tests/check-qjson.c b/tests/check-qjson.c
index 95497a037..1cfffa593 100644
--- a/tests/check-qjson.c
+++ b/tests/check-qjson.c
@@ -1,6 +1,6 @@
/*
* Copyright IBM, Corp. 2009
- * Copyright (c) 2013 Red Hat Inc.
+ * Copyright (c) 2013, 2015 Red Hat Inc.
*
* Authors:
* Anthony Liguori <aliguori@us.ibm.com>
@@ -1005,6 +1005,7 @@ static void keyword_literal(void)
{
QObject *obj;
QBool *qbool;
+ QObject *null;
QString *str;
obj = qobject_from_json("true");
@@ -1012,7 +1013,7 @@ static void keyword_literal(void)
g_assert(qobject_type(obj) == QTYPE_QBOOL);
qbool = qobject_to_qbool(obj);
- g_assert(qbool_get_int(qbool) != 0);
+ g_assert(qbool_get_bool(qbool) == true);
str = qobject_to_json(obj);
g_assert(strcmp(qstring_get_str(str), "true") == 0);
@@ -1025,7 +1026,7 @@ static void keyword_literal(void)
g_assert(qobject_type(obj) == QTYPE_QBOOL);
qbool = qobject_to_qbool(obj);
- g_assert(qbool_get_int(qbool) == 0);
+ g_assert(qbool_get_bool(qbool) == false);
str = qobject_to_json(obj);
g_assert(strcmp(qstring_get_str(str), "false") == 0);
@@ -1038,18 +1039,29 @@ static void keyword_literal(void)
g_assert(qobject_type(obj) == QTYPE_QBOOL);
qbool = qobject_to_qbool(obj);
- g_assert(qbool_get_int(qbool) == 0);
+ g_assert(qbool_get_bool(qbool) == false);
QDECREF(qbool);
-
- obj = qobject_from_jsonf("%i", true);
+
+ /* Test that non-zero values other than 1 get collapsed to true */
+ obj = qobject_from_jsonf("%i", 2);
g_assert(obj != NULL);
g_assert(qobject_type(obj) == QTYPE_QBOOL);
qbool = qobject_to_qbool(obj);
- g_assert(qbool_get_int(qbool) != 0);
+ g_assert(qbool_get_bool(qbool) == true);
QDECREF(qbool);
+
+ obj = qobject_from_json("null");
+ g_assert(obj != NULL);
+ g_assert(qobject_type(obj) == QTYPE_QNULL);
+
+ null = qnull();
+ g_assert(null == obj);
+
+ qobject_decref(obj);
+ qobject_decref(null);
}
typedef struct LiteralQDictEntry LiteralQDictEntry;
diff --git a/tests/check-qom-proplist.c b/tests/check-qom-proplist.c
new file mode 100644
index 000000000..7400b1fce
--- /dev/null
+++ b/tests/check-qom-proplist.c
@@ -0,0 +1,302 @@
+/*
+ * 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 <glib.h>
+
+#include "qom/object.h"
+#include "qemu/module.h"
+
+
+#define TYPE_DUMMY "qemu-dummy"
+
+typedef struct DummyObject DummyObject;
+typedef struct DummyObjectClass DummyObjectClass;
+
+#define DUMMY_OBJECT(obj) \
+ OBJECT_CHECK(DummyObject, (obj), TYPE_DUMMY)
+
+typedef enum DummyAnimal DummyAnimal;
+
+enum DummyAnimal {
+ DUMMY_FROG,
+ DUMMY_ALLIGATOR,
+ DUMMY_PLATYPUS,
+
+ DUMMY_LAST,
+};
+
+static const char *const dummy_animal_map[DUMMY_LAST + 1] = {
+ [DUMMY_FROG] = "frog",
+ [DUMMY_ALLIGATOR] = "alligator",
+ [DUMMY_PLATYPUS] = "platypus",
+ [DUMMY_LAST] = NULL,
+};
+
+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,
+ NULL);
+ object_property_add_str(obj, "sv",
+ dummy_get_sv,
+ dummy_set_sv,
+ NULL);
+ object_property_add_enum(obj, "av",
+ "DummyAnimal",
+ dummy_animal_map,
+ dummy_get_av,
+ dummy_set_av,
+ NULL);
+}
+
+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),
+};
+
+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_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",
+ &err);
+ g_assert(err == NULL);
+ g_assert(val == DUMMY_PLATYPUS);
+
+ /* A bad enum type name */
+ val = object_property_get_enum(OBJECT(dobj),
+ "av",
+ "BadAnimal",
+ &err);
+ g_assert(err != NULL);
+ error_free(err);
+ err = NULL;
+
+ /* A non-enum property name */
+ val = object_property_get_enum(OBJECT(dobj),
+ "iv",
+ "DummyAnimal",
+ &err);
+ g_assert(err != NULL);
+ error_free(err);
+}
+
+
+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("/qom/proplist/createlist", test_dummy_createlist);
+ g_test_add_func("/qom/proplist/createv", test_dummy_createv);
+ g_test_add_func("/qom/proplist/badenum", test_dummy_badenum);
+ g_test_add_func("/qom/proplist/getenum", test_dummy_getenum);
+
+ return g_test_run();
+}
diff --git a/tests/device-introspect-test.c b/tests/device-introspect-test.c
new file mode 100644
index 000000000..f240b5c82
--- /dev/null
+++ b/tests/device-introspect-test.c
@@ -0,0 +1,158 @@
+/*
+ * Device introspection test cases
+ *
+ * Copyright (c) 2015 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.
+ */
+
+/*
+ * Covers QMP device-list-properties and HMP device_add help. We
+ * currently don't check that their output makes sense, only that QEMU
+ * survives. Useful since we've had an astounding number of crash
+ * bugs around here.
+ */
+
+#include <glib.h>
+#include <stdarg.h>
+#include "qemu-common.h"
+#include "qapi/qmp/qstring.h"
+#include "libqtest.h"
+
+const char common_args[] = "-nodefaults -machine none";
+
+static QList *device_type_list(bool abstract)
+{
+ QDict *resp;
+ QList *ret;
+
+ resp = qmp("{'execute': 'qom-list-types',"
+ " 'arguments': {'implements': 'device', 'abstract': %i}}",
+ abstract);
+ g_assert(qdict_haskey(resp, "return"));
+ ret = qdict_get_qlist(resp, "return");
+ QINCREF(ret);
+ QDECREF(resp);
+ return ret;
+}
+
+static void test_one_device(const char *type)
+{
+ QDict *resp;
+ char *help, *qom_tree;
+
+ /*
+ * Skip this part for the abstract device test case, because
+ * device-list-properties crashes for such devices.
+ * FIXME fix it not to crash
+ */
+ if (strcmp(type, "device")) {
+ resp = qmp("{'execute': 'device-list-properties',"
+ " 'arguments': {'typename': %s}}",
+ type);
+ QDECREF(resp);
+ }
+
+ help = hmp("device_add \"%s,help\"", type);
+ g_free(help);
+
+ /*
+ * Some devices leave dangling pointers in QOM behind.
+ * "info qom-tree" has a good chance at crashing then
+ */
+ qom_tree = hmp("info qom-tree");
+ g_free(qom_tree);
+}
+
+static void test_device_intro_list(void)
+{
+ QList *types;
+ char *help;
+
+ qtest_start(common_args);
+
+ types = device_type_list(true);
+ QDECREF(types);
+
+ help = hmp("device_add help");
+ g_free(help);
+
+ qtest_end();
+}
+
+static void test_device_intro_none(void)
+{
+ qtest_start(common_args);
+ test_one_device("nonexistent");
+ qtest_end();
+}
+
+static void test_device_intro_abstract(void)
+{
+ qtest_start(common_args);
+ test_one_device("device");
+ qtest_end();
+}
+
+static bool blacklisted(const char *type)
+{
+ static const char *blacklist[] = {
+ /* hang in object_unref(): */
+ "realview_pci", "versatile_pci",
+ /* create a CPU, thus use after free (see below): */
+ "allwinner-a10", "digic", "fsl,imx25", "fsl,imx31", "xlnx,zynqmp",
+ };
+ size_t len = strlen(type);
+ int i;
+
+ if (len >= 4 && !strcmp(type + len - 4, "-cpu")) {
+ /* use after free: cpu_exec_init() saves CPUState in cpus */
+ return true;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(blacklist); i++) {
+ if (!strcmp(blacklist[i], type)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static void test_device_intro_concrete(void)
+{
+ QList *types;
+ QListEntry *entry;
+ const char *type;
+
+ qtest_start(common_args);
+ types = device_type_list(false);
+
+ QLIST_FOREACH_ENTRY(types, entry) {
+ type = qdict_get_try_str(qobject_to_qdict(qlist_entry_obj(entry)),
+ "name");
+ g_assert(type);
+ if (blacklisted(type)) {
+ continue; /* FIXME broken device, skip */
+ }
+ test_one_device(type);
+ }
+
+ QDECREF(types);
+ qtest_end();
+}
+
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+
+ qtest_add_func("device/introspect/list", test_device_intro_list);
+ qtest_add_func("device/introspect/none", test_device_intro_none);
+ qtest_add_func("device/introspect/abstract", test_device_intro_abstract);
+ qtest_add_func("device/introspect/concrete", test_device_intro_concrete);
+
+ return g_test_run();
+}
diff --git a/tests/display-vga-test.c b/tests/display-vga-test.c
index 17f59101e..7694344ea 100644
--- a/tests/display-vga-test.c
+++ b/tests/display-vga-test.c
@@ -36,6 +36,20 @@ static void pci_multihead(void)
qtest_end();
}
+static void pci_virtio_gpu(void)
+{
+ qtest_start("-vga none -device virtio-gpu-pci");
+ qtest_end();
+}
+
+#ifdef CONFIG_VIRTIO_VGA
+static void pci_virtio_vga(void)
+{
+ qtest_start("-vga none -device virtio-vga");
+ qtest_end();
+}
+#endif
+
int main(int argc, char **argv)
{
int ret;
@@ -46,6 +60,10 @@ int main(int argc, char **argv)
qtest_add_func("/display/pci/stdvga", pci_stdvga);
qtest_add_func("/display/pci/secondary", pci_secondary);
qtest_add_func("/display/pci/multihead", pci_multihead);
+ qtest_add_func("/display/pci/virtio-gpu", pci_virtio_gpu);
+#ifdef CONFIG_VIRTIO_VGA
+ qtest_add_func("/display/pci/virtio-vga", pci_virtio_vga);
+#endif
ret = g_test_run();
return ret;
diff --git a/tests/drive_del-test.c b/tests/drive_del-test.c
index 8951f6f61..33909469f 100644
--- a/tests/drive_del-test.c
+++ b/tests/drive_del-test.c
@@ -16,28 +16,18 @@
static void drive_add(void)
{
- QDict *response;
+ char *resp = hmp("drive_add 0 if=none,id=drive0");
- response = qmp("{'execute': 'human-monitor-command',"
- " 'arguments': {"
- " 'command-line': 'drive_add 0 if=none,id=drive0'"
- "}}");
- g_assert(response);
- g_assert_cmpstr(qdict_get_try_str(response, "return"), ==, "OK\r\n");
- QDECREF(response);
+ g_assert_cmpstr(resp, ==, "OK\r\n");
+ g_free(resp);
}
static void drive_del(void)
{
- QDict *response;
+ char *resp = hmp("drive_del drive0");
- response = qmp("{'execute': 'human-monitor-command',"
- " 'arguments': {"
- " 'command-line': 'drive_del drive0'"
- "}}");
- g_assert(response);
- g_assert_cmpstr(qdict_get_try_str(response, "return"), ==, "");
- QDECREF(response);
+ g_assert_cmpstr(resp, ==, "");
+ g_free(resp);
}
static void device_del(void)
diff --git a/tests/e1000-test.c b/tests/e1000-test.c
index 81f164d9e..7ca6d7e72 100644
--- a/tests/e1000-test.c
+++ b/tests/e1000-test.c
@@ -44,8 +44,8 @@ int main(int argc, char **argv)
for (i = 0; i < ARRAY_SIZE(models); i++) {
char *path;
- path = g_strdup_printf("/%s/e1000/%s", qtest_get_arch(), models[i]);
- g_test_add_data_func(path, models[i], test_device);
+ path = g_strdup_printf("e1000/%s", models[i]);
+ qtest_add_data_func(path, models[i], test_device);
}
return g_test_run();
diff --git a/tests/eepro100-test.c b/tests/eepro100-test.c
index bf8252627..8bfaccdcb 100644
--- a/tests/eepro100-test.c
+++ b/tests/eepro100-test.c
@@ -54,9 +54,8 @@ int main(int argc, char **argv)
for (i = 0; i < ARRAY_SIZE(models); i++) {
char *path;
- path = g_strdup_printf("/%s/eepro100/%s",
- qtest_get_arch(), models[i]);
- g_test_add_data_func(path, models[i], test_device);
+ path = g_strdup_printf("eepro100/%s", models[i]);
+ qtest_add_data_func(path, models[i], test_device);
}
return g_test_run();
diff --git a/tests/endianness-test.c b/tests/endianness-test.c
index 92e17d251..2054338e1 100644
--- a/tests/endianness-test.c
+++ b/tests/endianness-test.c
@@ -31,8 +31,6 @@ struct TestCase {
static const TestCase test_cases[] = {
{ "i386", "pc", -1 },
- { "mips", "magnum", 0x90000000, .bswap = true },
- { "mips", "pica61", 0x90000000, .bswap = true },
{ "mips", "mips", 0x14000000, .bswap = true },
{ "mips", "malta", 0x10000000, .bswap = true },
{ "mips64", "magnum", 0x90000000, .bswap = true },
@@ -298,17 +296,17 @@ int main(int argc, char **argv)
if (strcmp(test_cases[i].arch, arch) != 0) {
continue;
}
- path = g_strdup_printf("/%s/endianness/%s",
- arch, test_cases[i].machine);
- g_test_add_data_func(path, &test_cases[i], test_endianness);
+ path = g_strdup_printf("endianness/%s",
+ test_cases[i].machine);
+ qtest_add_data_func(path, &test_cases[i], test_endianness);
- path = g_strdup_printf("/%s/endianness/split/%s",
- arch, test_cases[i].machine);
- g_test_add_data_func(path, &test_cases[i], test_endianness_split);
+ path = g_strdup_printf("endianness/split/%s",
+ test_cases[i].machine);
+ qtest_add_data_func(path, &test_cases[i], test_endianness_split);
- path = g_strdup_printf("/%s/endianness/combine/%s",
- arch, test_cases[i].machine);
- g_test_add_data_func(path, &test_cases[i], test_endianness_combine);
+ path = g_strdup_printf("endianness/combine/%s",
+ test_cases[i].machine);
+ qtest_add_data_func(path, &test_cases[i], test_endianness_combine);
}
ret = g_test_run();
diff --git a/tests/fdc-test.c b/tests/fdc-test.c
index 3c6c83cac..416394fc7 100644
--- a/tests/fdc-test.c
+++ b/tests/fdc-test.c
@@ -218,6 +218,10 @@ static uint8_t send_read_no_dma_command(int nb_sect, uint8_t expected_st0)
inb(FLOPPY_BASE + reg_fifo);
}
+ msr = inb(FLOPPY_BASE + reg_msr);
+ assert_bit_set(msr, BUSY | RQM | DIO);
+ g_assert(get_irq(FLOPPY_IRQ));
+
st0 = floppy_recv();
if (st0 != expected_st0) {
ret = 1;
@@ -228,8 +232,15 @@ static uint8_t send_read_no_dma_command(int nb_sect, uint8_t expected_st0)
floppy_recv();
floppy_recv();
floppy_recv();
+ g_assert(get_irq(FLOPPY_IRQ));
floppy_recv();
+ /* Check that we're back in command phase */
+ msr = inb(FLOPPY_BASE + reg_msr);
+ assert_bit_clear(msr, BUSY | DIO);
+ assert_bit_set(msr, RQM);
+ g_assert(!get_irq(FLOPPY_IRQ));
+
return ret;
}
@@ -403,6 +414,7 @@ static void test_read_id(void)
uint8_t head = 0;
uint8_t cyl;
uint8_t st0;
+ uint8_t msr;
/* Seek to track 0 and check with READ ID */
send_seek(0);
@@ -411,18 +423,29 @@ static void test_read_id(void)
g_assert(!get_irq(FLOPPY_IRQ));
floppy_send(head << 2 | drive);
+ msr = inb(FLOPPY_BASE + reg_msr);
+ if (!get_irq(FLOPPY_IRQ)) {
+ assert_bit_set(msr, BUSY);
+ assert_bit_clear(msr, RQM);
+ }
+
while (!get_irq(FLOPPY_IRQ)) {
/* qemu involves a timer with READ ID... */
clock_step(1000000000LL / 50);
}
+ msr = inb(FLOPPY_BASE + reg_msr);
+ assert_bit_set(msr, BUSY | RQM | DIO);
+
st0 = floppy_recv();
floppy_recv();
floppy_recv();
cyl = floppy_recv();
head = floppy_recv();
floppy_recv();
+ g_assert(get_irq(FLOPPY_IRQ));
floppy_recv();
+ g_assert(!get_irq(FLOPPY_IRQ));
g_assert_cmpint(cyl, ==, 0);
g_assert_cmpint(head, ==, 0);
@@ -443,18 +466,29 @@ static void test_read_id(void)
g_assert(!get_irq(FLOPPY_IRQ));
floppy_send(head << 2 | drive);
+ msr = inb(FLOPPY_BASE + reg_msr);
+ if (!get_irq(FLOPPY_IRQ)) {
+ assert_bit_set(msr, BUSY);
+ assert_bit_clear(msr, RQM);
+ }
+
while (!get_irq(FLOPPY_IRQ)) {
/* qemu involves a timer with READ ID... */
clock_step(1000000000LL / 50);
}
+ msr = inb(FLOPPY_BASE + reg_msr);
+ assert_bit_set(msr, BUSY | RQM | DIO);
+
st0 = floppy_recv();
floppy_recv();
floppy_recv();
cyl = floppy_recv();
head = floppy_recv();
floppy_recv();
+ g_assert(get_irq(FLOPPY_IRQ));
floppy_recv();
+ g_assert(!get_irq(FLOPPY_IRQ));
g_assert_cmpint(cyl, ==, 8);
g_assert_cmpint(head, ==, 1);
diff --git a/tests/ide-test.c b/tests/ide-test.c
index b28a3023c..ef0a47343 100644
--- a/tests/ide-test.c
+++ b/tests/ide-test.c
@@ -29,6 +29,7 @@
#include <glib.h>
#include "libqtest.h"
+#include "libqos/libqos.h"
#include "libqos/pci-pc.h"
#include "libqos/malloc-pc.h"
@@ -338,6 +339,31 @@ static void test_bmdma_short_prdt(void)
assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
}
+static void test_bmdma_one_sector_short_prdt(void)
+{
+ uint8_t status;
+
+ /* Read 2 sectors but only give 1 sector in PRDT */
+ PrdtEntry prdt[] = {
+ {
+ .addr = 0,
+ .size = cpu_to_le32(0x200 | PRDT_EOT),
+ },
+ };
+
+ /* Normal request */
+ status = send_dma_request(CMD_READ_DMA, 0, 2,
+ prdt, ARRAY_SIZE(prdt));
+ g_assert_cmphex(status, ==, 0);
+ assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
+
+ /* Abort the request before it completes */
+ status = send_dma_request(CMD_READ_DMA | CMDF_ABORT, 0, 2,
+ prdt, ARRAY_SIZE(prdt));
+ g_assert_cmphex(status, ==, 0);
+ assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
+}
+
static void test_bmdma_long_prdt(void)
{
uint8_t status;
@@ -462,9 +488,7 @@ static void test_flush(void)
tmp_path);
/* Delay the completion of the flush request until we explicitly do it */
- qmp_discard_response("{'execute':'human-monitor-command', 'arguments': {"
- " 'command-line':"
- " 'qemu-io ide0-hd0 \"break flush_to_os A\"'} }");
+ g_free(hmp("qemu-io ide0-hd0 \"break flush_to_os A\""));
/* FLUSH CACHE command on device 0*/
outb(IDE_BASE + reg_device, 0);
@@ -476,9 +500,7 @@ static void test_flush(void)
assert_bit_clear(data, DF | ERR | DRQ);
/* Complete the command */
- qmp_discard_response("{'execute':'human-monitor-command', 'arguments': {"
- " 'command-line':"
- " 'qemu-io ide0-hd0 \"resume A\"'} }");
+ g_free(hmp("qemu-io ide0-hd0 \"resume A\""));
/* Check registers */
data = inb(IDE_BASE + reg_device);
@@ -494,33 +516,10 @@ static void test_flush(void)
ide_test_quit();
}
-static void prepare_blkdebug_script(const char *debug_fn, const char *event)
-{
- FILE *debug_file = fopen(debug_fn, "w");
- int ret;
-
- fprintf(debug_file, "[inject-error]\n");
- fprintf(debug_file, "event = \"%s\"\n", event);
- fprintf(debug_file, "errno = \"5\"\n");
- fprintf(debug_file, "state = \"1\"\n");
- fprintf(debug_file, "immediately = \"off\"\n");
- fprintf(debug_file, "once = \"on\"\n");
-
- fprintf(debug_file, "[set-state]\n");
- fprintf(debug_file, "event = \"%s\"\n", event);
- fprintf(debug_file, "new_state = \"2\"\n");
- fflush(debug_file);
- g_assert(!ferror(debug_file));
-
- ret = fclose(debug_file);
- g_assert(ret == 0);
-}
-
static void test_retry_flush(const char *machine)
{
uint8_t data;
const char *s;
- QDict *response;
prepare_blkdebug_script(debug_path, "flush_to_disk");
@@ -539,15 +538,7 @@ static void test_retry_flush(const char *machine)
assert_bit_set(data, BSY | DRDY);
assert_bit_clear(data, DF | ERR | DRQ);
- for (;; response = NULL) {
- response = qmp_receive();
- if ((qdict_haskey(response, "event")) &&
- (strcmp(qdict_get_str(response, "event"), "STOP") == 0)) {
- QDECREF(response);
- break;
- }
- QDECREF(response);
- }
+ qmp_eventwait("STOP");
/* Complete the command */
s = "{'execute':'cont' }";
@@ -622,6 +613,8 @@ int main(int argc, char **argv)
qtest_add_func("/ide/bmdma/setup", test_bmdma_setup);
qtest_add_func("/ide/bmdma/simple_rw", test_bmdma_simple_rw);
qtest_add_func("/ide/bmdma/short_prdt", test_bmdma_short_prdt);
+ qtest_add_func("/ide/bmdma/one_sector_short_prdt",
+ test_bmdma_one_sector_short_prdt);
qtest_add_func("/ide/bmdma/long_prdt", test_bmdma_long_prdt);
qtest_add_func("/ide/bmdma/no_busmaster", test_bmdma_no_busmaster);
qtest_add_func("/ide/bmdma/teardown", test_bmdma_teardown);
diff --git a/tests/libqos/ahci.c b/tests/libqos/ahci.c
index b0f39a5e3..cf66b3e32 100644
--- a/tests/libqos/ahci.c
+++ b/tests/libqos/ahci.c
@@ -50,27 +50,47 @@ typedef struct AHCICommandProp {
} AHCICommandProp;
AHCICommandProp ahci_command_properties[] = {
- { .cmd = CMD_READ_PIO, .data = true, .pio = true,
- .lba28 = true, .read = true },
- { .cmd = CMD_WRITE_PIO, .data = true, .pio = true,
- .lba28 = true, .write = true },
- { .cmd = CMD_READ_PIO_EXT, .data = true, .pio = true,
- .lba48 = true, .read = true },
- { .cmd = CMD_WRITE_PIO_EXT, .data = true, .pio = true,
- .lba48 = true, .write = true },
- { .cmd = CMD_READ_DMA, .data = true, .dma = true,
- .lba28 = true, .read = true },
- { .cmd = CMD_WRITE_DMA, .data = true, .dma = true,
- .lba28 = true, .write = true },
- { .cmd = CMD_READ_DMA_EXT, .data = true, .dma = true,
- .lba48 = true, .read = true },
- { .cmd = CMD_WRITE_DMA_EXT, .data = true, .dma = true,
- .lba48 = true, .write = true },
- { .cmd = CMD_IDENTIFY, .data = true, .pio = true,
- .size = 512, .read = true },
- { .cmd = CMD_READ_MAX, .lba28 = true },
- { .cmd = CMD_READ_MAX_EXT, .lba48 = true },
- { .cmd = CMD_FLUSH_CACHE, .data = false }
+ { .cmd = CMD_READ_PIO, .data = true, .pio = true,
+ .lba28 = true, .read = true },
+ { .cmd = CMD_WRITE_PIO, .data = true, .pio = true,
+ .lba28 = true, .write = true },
+ { .cmd = CMD_READ_PIO_EXT, .data = true, .pio = true,
+ .lba48 = true, .read = true },
+ { .cmd = CMD_WRITE_PIO_EXT, .data = true, .pio = true,
+ .lba48 = true, .write = true },
+ { .cmd = CMD_READ_DMA, .data = true, .dma = true,
+ .lba28 = true, .read = true },
+ { .cmd = CMD_WRITE_DMA, .data = true, .dma = true,
+ .lba28 = true, .write = true },
+ { .cmd = CMD_READ_DMA_EXT, .data = true, .dma = true,
+ .lba48 = true, .read = true },
+ { .cmd = CMD_WRITE_DMA_EXT, .data = true, .dma = true,
+ .lba48 = true, .write = true },
+ { .cmd = CMD_IDENTIFY, .data = true, .pio = true,
+ .size = 512, .read = true },
+ { .cmd = READ_FPDMA_QUEUED, .data = true, .dma = true,
+ .lba48 = true, .read = true, .ncq = true },
+ { .cmd = WRITE_FPDMA_QUEUED, .data = true, .dma = true,
+ .lba48 = true, .write = true, .ncq = true },
+ { .cmd = CMD_READ_MAX, .lba28 = true },
+ { .cmd = CMD_READ_MAX_EXT, .lba48 = true },
+ { .cmd = CMD_FLUSH_CACHE, .data = false }
+};
+
+struct AHCICommand {
+ /* Test Management Data */
+ uint8_t name;
+ uint8_t port;
+ uint8_t slot;
+ uint32_t interrupts;
+ uint64_t xbytes;
+ uint32_t prd_size;
+ uint64_t buffer;
+ AHCICommandProp *props;
+ /* Data to be transferred to the guest */
+ AHCICommandHeader header;
+ RegH2DFIS fis;
+ void *atapi_cmd;
};
/**
@@ -138,12 +158,14 @@ void ahci_clean_mem(AHCIQState *ahci)
for (port = 0; port < 32; ++port) {
if (ahci->port[port].fb) {
ahci_free(ahci, ahci->port[port].fb);
+ ahci->port[port].fb = 0;
}
if (ahci->port[port].clb) {
for (slot = 0; slot < 32; slot++) {
ahci_destroy_command(ahci, port, slot);
}
ahci_free(ahci, ahci->port[port].clb);
+ ahci->port[port].clb = 0;
}
}
}
@@ -252,7 +274,7 @@ void ahci_hba_enable(AHCIQState *ahci)
/* Allocate Memory for the Command List Buffer & FIS Buffer */
/* PxCLB space ... 0x20 per command, as in 4.2.2 p 36 */
ahci->port[i].clb = ahci_alloc(ahci, num_cmd_slots * 0x20);
- qmemset(ahci->port[i].clb, 0x00, 0x100);
+ qmemset(ahci->port[i].clb, 0x00, num_cmd_slots * 0x20);
g_test_message("CLB: 0x%08" PRIx64, ahci->port[i].clb);
ahci_px_wreg(ahci, i, AHCI_PX_CLB, ahci->port[i].clb);
g_assert_cmphex(ahci->port[i].clb, ==,
@@ -364,7 +386,7 @@ void ahci_port_clear(AHCIQState *ahci, uint8_t port)
ahci_px_wreg(ahci, port, AHCI_PX_IS, reg);
g_assert_cmphex(ahci_px_rreg(ahci, port, AHCI_PX_IS), ==, 0);
- /* Wipe the FIS-Recieve Buffer */
+ /* Wipe the FIS-Receive Buffer */
qmemset(ahci->port[port].fb, 0x00, 0x100);
}
@@ -442,7 +464,7 @@ void ahci_port_check_pio_sanity(AHCIQState *ahci, uint8_t port,
{
PIOSetupFIS *pio = g_malloc0(0x20);
- /* We cannot check the Status or E_Status registers, becuase
+ /* We cannot check the Status or E_Status registers, because
* the status may have again changed between the PIO Setup FIS
* and the conclusion of the command with the D2H Register FIS. */
memread(ahci->port[port].fb + 0x20, pio, 0x20);
@@ -460,13 +482,15 @@ void ahci_port_check_pio_sanity(AHCIQState *ahci, uint8_t port,
g_free(pio);
}
-void ahci_port_check_cmd_sanity(AHCIQState *ahci, uint8_t port,
- uint8_t slot, size_t buffsize)
+void ahci_port_check_cmd_sanity(AHCIQState *ahci, AHCICommand *cmd)
{
- AHCICommandHeader cmd;
+ AHCICommandHeader cmdh;
- ahci_get_command_header(ahci, port, slot, &cmd);
- g_assert_cmphex(buffsize, ==, cmd.prdbc);
+ ahci_get_command_header(ahci, cmd->port, cmd->slot, &cmdh);
+ /* Physical Region Descriptor Byte Count is not required to work for NCQ. */
+ if (!cmd->props->ncq) {
+ g_assert_cmphex(cmd->xbytes, ==, cmdh.prdbc);
+ }
}
/* Get the command in #slot of port #port. */
@@ -521,16 +545,18 @@ void ahci_destroy_command(AHCIQState *ahci, uint8_t port, uint8_t slot)
ahci->port[port].prdtl[slot] = 0;
}
-void ahci_write_fis(AHCIQState *ahci, RegH2DFIS *fis, uint64_t addr)
+void ahci_write_fis(AHCIQState *ahci, AHCICommand *cmd)
{
- RegH2DFIS tmp = *fis;
-
- /* The auxiliary FIS fields are defined per-command and are not
- * currently implemented in libqos/ahci.o, but may or may not need
- * to be flipped. */
+ RegH2DFIS tmp = cmd->fis;
+ uint64_t addr = cmd->header.ctba;
- /* All other FIS fields are 8 bit and do not need to be flipped. */
- tmp.count = cpu_to_le16(tmp.count);
+ /* NCQ commands use exclusively 8 bit fields and needs no adjustment.
+ * Only the count field needs to be adjusted for non-NCQ commands.
+ * The auxiliary FIS fields are defined per-command and are not currently
+ * implemented in libqos/ahci.o, but may or may not need to be flipped. */
+ if (!cmd->props->ncq) {
+ tmp.count = cpu_to_le16(tmp.count);
+ }
memwrite(addr, &tmp, sizeof(tmp));
}
@@ -549,7 +575,7 @@ unsigned ahci_pick_cmd(AHCIQState *ahci, uint8_t port)
if (reg & (1 << j)) {
continue;
}
- ahci_destroy_command(ahci, port, i);
+ ahci_destroy_command(ahci, port, j);
ahci->port[port].next = (j + 1) % 32;
return j;
}
@@ -566,37 +592,50 @@ inline unsigned size_to_prdtl(unsigned bytes, unsigned bytes_per_prd)
return (bytes + bytes_per_prd - 1) / bytes_per_prd;
}
+/* Issue a command, expecting it to fail and STOP the VM */
+AHCICommand *ahci_guest_io_halt(AHCIQState *ahci, uint8_t port,
+ uint8_t ide_cmd, uint64_t buffer,
+ size_t bufsize, uint64_t sector)
+{
+ AHCICommand *cmd;
+
+ cmd = ahci_command_create(ide_cmd);
+ ahci_command_adjust(cmd, sector, buffer, bufsize, 0);
+ ahci_command_commit(ahci, cmd, port);
+ ahci_command_issue_async(ahci, cmd);
+ qmp_eventwait("STOP");
+
+ return cmd;
+}
+
+/* Resume a previously failed command and verify/finalize */
+void ahci_guest_io_resume(AHCIQState *ahci, AHCICommand *cmd)
+{
+ /* Complete the command */
+ qmp_async("{'execute':'cont' }");
+ qmp_eventwait("RESUME");
+ ahci_command_wait(ahci, cmd);
+ ahci_command_verify(ahci, cmd);
+ ahci_command_free(cmd);
+}
+
/* Given a guest buffer address, perform an IO operation */
void ahci_guest_io(AHCIQState *ahci, uint8_t port, uint8_t ide_cmd,
- uint64_t buffer, size_t bufsize)
+ uint64_t buffer, size_t bufsize, uint64_t sector)
{
AHCICommand *cmd;
-
cmd = ahci_command_create(ide_cmd);
ahci_command_set_buffer(cmd, buffer);
ahci_command_set_size(cmd, bufsize);
+ if (sector) {
+ ahci_command_set_offset(cmd, sector);
+ }
ahci_command_commit(ahci, cmd, port);
ahci_command_issue(ahci, cmd);
ahci_command_verify(ahci, cmd);
ahci_command_free(cmd);
}
-struct AHCICommand {
- /* Test Management Data */
- uint8_t name;
- uint8_t port;
- uint8_t slot;
- uint32_t interrupts;
- uint64_t xbytes;
- uint32_t prd_size;
- uint64_t buffer;
- AHCICommandProp *props;
- /* Data to be transferred to the guest */
- AHCICommandHeader header;
- RegH2DFIS fis;
- void *atapi_cmd;
-};
-
static AHCICommandProp *ahci_command_find(uint8_t command_name)
{
int i;
@@ -612,7 +651,7 @@ static AHCICommandProp *ahci_command_find(uint8_t command_name)
/* Given a HOST buffer, create a buffer address and perform an IO operation. */
void ahci_io(AHCIQState *ahci, uint8_t port, uint8_t ide_cmd,
- void *buffer, size_t bufsize)
+ void *buffer, size_t bufsize, uint64_t sector)
{
uint64_t ptr;
AHCICommandProp *props;
@@ -621,15 +660,16 @@ void ahci_io(AHCIQState *ahci, uint8_t port, uint8_t ide_cmd,
g_assert(props);
ptr = ahci_alloc(ahci, bufsize);
g_assert(ptr);
+ qmemset(ptr, 0x00, bufsize);
if (props->write) {
- memwrite(ptr, buffer, bufsize);
+ bufwrite(ptr, buffer, bufsize);
}
- ahci_guest_io(ahci, port, ide_cmd, ptr, bufsize);
+ ahci_guest_io(ahci, port, ide_cmd, ptr, bufsize, sector);
if (props->read) {
- memread(ptr, buffer, bufsize);
+ bufread(ptr, buffer, bufsize);
}
ahci_free(ahci, ptr);
@@ -661,19 +701,34 @@ static void command_header_init(AHCICommand *cmd)
static void command_table_init(AHCICommand *cmd)
{
RegH2DFIS *fis = &(cmd->fis);
+ uint16_t sect_count = (cmd->xbytes / AHCI_SECTOR_SIZE);
fis->fis_type = REG_H2D_FIS;
fis->flags = REG_H2D_FIS_CMD; /* "Command" bit */
fis->command = cmd->name;
- cmd->fis.feature_low = 0x00;
- cmd->fis.feature_high = 0x00;
- if (cmd->props->lba28 || cmd->props->lba48) {
- cmd->fis.device = ATA_DEVICE_LBA;
+
+ if (cmd->props->ncq) {
+ NCQFIS *ncqfis = (NCQFIS *)fis;
+ /* NCQ is weird and re-uses FIS frames for unrelated data.
+ * See SATA 3.2, 13.6.4.1 READ FPDMA QUEUED for an example. */
+ ncqfis->sector_low = sect_count & 0xFF;
+ ncqfis->sector_hi = (sect_count >> 8) & 0xFF;
+ ncqfis->device = NCQ_DEVICE_MAGIC;
+ /* Force Unit Access is bit 7 in the device register */
+ ncqfis->tag = 0; /* bits 3-7 are the NCQ tag */
+ ncqfis->prio = 0; /* bits 6,7 are a prio tag */
+ /* RARC bit is bit 0 of TAG field */
+ } else {
+ fis->feature_low = 0x00;
+ fis->feature_high = 0x00;
+ if (cmd->props->lba28 || cmd->props->lba48) {
+ fis->device = ATA_DEVICE_LBA;
+ }
+ fis->count = (cmd->xbytes / AHCI_SECTOR_SIZE);
}
- cmd->fis.count = (cmd->xbytes / AHCI_SECTOR_SIZE);
- cmd->fis.icc = 0x00;
- cmd->fis.control = 0x00;
- memset(cmd->fis.aux, 0x00, ARRAY_SIZE(cmd->fis.aux));
+ fis->icc = 0x00;
+ fis->control = 0x00;
+ memset(fis->aux, 0x00, ARRAY_SIZE(fis->aux));
}
AHCICommand *ahci_command_create(uint8_t command_name)
@@ -687,6 +742,7 @@ AHCICommand *ahci_command_create(uint8_t command_name)
g_assert(!(props->lba28 && props->lba48));
g_assert(!(props->read && props->write));
g_assert(!props->size || props->data);
+ g_assert(!props->ncq || (props->ncq && props->lba48));
/* Defaults and book-keeping */
cmd->props = props;
@@ -695,12 +751,15 @@ AHCICommand *ahci_command_create(uint8_t command_name)
cmd->prd_size = 4096;
cmd->buffer = 0xabad1dea;
- cmd->interrupts = AHCI_PX_IS_DHRS;
+ if (!cmd->props->ncq) {
+ cmd->interrupts = AHCI_PX_IS_DHRS;
+ }
/* BUG: We expect the DPS interrupt for data commands */
/* cmd->interrupts |= props->data ? AHCI_PX_IS_DPS : 0; */
/* BUG: We expect the DMA Setup interrupt for DMA commands */
/* cmd->interrupts |= props->dma ? AHCI_PX_IS_DSS : 0; */
cmd->interrupts |= props->pio ? AHCI_PX_IS_PSS : 0;
+ cmd->interrupts |= props->ncq ? AHCI_PX_IS_SDBS : 0;
command_header_init(cmd);
command_table_init(cmd);
@@ -728,7 +787,7 @@ void ahci_command_set_offset(AHCICommand *cmd, uint64_t lba_sect)
RegH2DFIS *fis = &(cmd->fis);
if (cmd->props->lba28) {
g_assert_cmphex(lba_sect, <=, 0xFFFFFFF);
- } else if (cmd->props->lba48) {
+ } else if (cmd->props->lba48 || cmd->props->ncq) {
g_assert_cmphex(lba_sect, <=, 0xFFFFFFFFFFFF);
} else {
/* Can't set offset if we don't know the format. */
@@ -740,7 +799,7 @@ void ahci_command_set_offset(AHCICommand *cmd, uint64_t lba_sect)
fis->lba_lo[1] = (lba_sect >> 8) & 0xFF;
fis->lba_lo[2] = (lba_sect >> 16) & 0xFF;
if (cmd->props->lba28) {
- fis->device = (fis->device & 0xF0) || (lba_sect >> 24) & 0x0F;
+ fis->device = (fis->device & 0xF0) | ((lba_sect >> 24) & 0x0F);
}
fis->lba_hi[0] = (lba_sect >> 24) & 0xFF;
fis->lba_hi[1] = (lba_sect >> 32) & 0xFF;
@@ -755,12 +814,24 @@ void ahci_command_set_buffer(AHCICommand *cmd, uint64_t buffer)
void ahci_command_set_sizes(AHCICommand *cmd, uint64_t xbytes,
unsigned prd_size)
{
+ uint16_t sect_count;
+
/* Each PRD can describe up to 4MiB, and must not be odd. */
g_assert_cmphex(prd_size, <=, 4096 * 1024);
g_assert_cmphex(prd_size & 0x01, ==, 0x00);
- cmd->prd_size = prd_size;
+ if (prd_size) {
+ cmd->prd_size = prd_size;
+ }
cmd->xbytes = xbytes;
- cmd->fis.count = (cmd->xbytes / AHCI_SECTOR_SIZE);
+ sect_count = (cmd->xbytes / AHCI_SECTOR_SIZE);
+
+ if (cmd->props->ncq) {
+ NCQFIS *nfis = (NCQFIS *)&(cmd->fis);
+ nfis->sector_low = sect_count & 0xFF;
+ nfis->sector_hi = (sect_count >> 8) & 0xFF;
+ } else {
+ cmd->fis.count = sect_count;
+ }
cmd->header.prdtl = size_to_prdtl(cmd->xbytes, cmd->prd_size);
}
@@ -792,6 +863,11 @@ void ahci_command_commit(AHCIQState *ahci, AHCICommand *cmd, uint8_t port)
cmd->port = port;
cmd->slot = ahci_pick_cmd(ahci, port);
+ if (cmd->props->ncq) {
+ NCQFIS *nfis = (NCQFIS *)&cmd->fis;
+ nfis->tag = (cmd->slot << 3) & 0xFC;
+ }
+
/* Create a buffer for the command table */
prdtl = size_to_prdtl(cmd->xbytes, cmd->prd_size);
table_size = CMD_TBL_SIZ(prdtl);
@@ -803,7 +879,7 @@ void ahci_command_commit(AHCIQState *ahci, AHCICommand *cmd, uint8_t port)
/* Commit the command header and command FIS */
ahci_set_command_header(ahci, port, cmd->slot, &(cmd->header));
- ahci_write_fis(ahci, &(cmd->fis), table_ptr);
+ ahci_write_fis(ahci, cmd);
/* Construct and write the PRDs to the command table */
g_assert_cmphex(prdtl, ==, cmd->header.prdtl);
@@ -846,11 +922,15 @@ void ahci_command_wait(AHCIQState *ahci, AHCICommand *cmd)
/* We can't rely on STS_BSY until the command has started processing.
* Therefore, we also use the Command Issue bit as indication of
* a command in-flight. */
- while (BITSET(ahci_px_rreg(ahci, cmd->port, AHCI_PX_TFD),
- AHCI_PX_TFD_STS_BSY) ||
- BITSET(ahci_px_rreg(ahci, cmd->port, AHCI_PX_CI), (1 << cmd->slot))) {
+
+#define RSET(REG, MASK) (BITSET(ahci_px_rreg(ahci, cmd->port, (REG)), (MASK)))
+
+ while (RSET(AHCI_PX_TFD, AHCI_PX_TFD_STS_BSY) ||
+ RSET(AHCI_PX_CI, 1 << cmd->slot) ||
+ (cmd->props->ncq && RSET(AHCI_PX_SACT, 1 << cmd->slot))) {
usleep(50);
}
+
}
void ahci_command_issue(AHCIQState *ahci, AHCICommand *cmd)
@@ -867,8 +947,10 @@ void ahci_command_verify(AHCIQState *ahci, AHCICommand *cmd)
ahci_port_check_error(ahci, port);
ahci_port_check_interrupts(ahci, port, cmd->interrupts);
ahci_port_check_nonbusy(ahci, port, slot);
- ahci_port_check_cmd_sanity(ahci, port, slot, cmd->xbytes);
- ahci_port_check_d2h_sanity(ahci, port, slot);
+ ahci_port_check_cmd_sanity(ahci, cmd);
+ if (cmd->interrupts & AHCI_PX_IS_DHRS) {
+ ahci_port_check_d2h_sanity(ahci, port, slot);
+ }
if (cmd->props->pio) {
ahci_port_check_pio_sanity(ahci, port, slot, cmd->xbytes);
}
diff --git a/tests/libqos/ahci.h b/tests/libqos/ahci.h
index 888545d5a..cffc2c351 100644
--- a/tests/libqos/ahci.h
+++ b/tests/libqos/ahci.h
@@ -263,20 +263,23 @@ enum {
/* ATA Commands */
enum {
/* DMA */
- CMD_READ_DMA = 0xC8,
- CMD_READ_DMA_EXT = 0x25,
- CMD_WRITE_DMA = 0xCA,
- CMD_WRITE_DMA_EXT = 0x35,
+ CMD_READ_DMA = 0xC8,
+ CMD_READ_DMA_EXT = 0x25,
+ CMD_WRITE_DMA = 0xCA,
+ CMD_WRITE_DMA_EXT = 0x35,
/* PIO */
- CMD_READ_PIO = 0x20,
- CMD_READ_PIO_EXT = 0x24,
- CMD_WRITE_PIO = 0x30,
- CMD_WRITE_PIO_EXT = 0x34,
+ CMD_READ_PIO = 0x20,
+ CMD_READ_PIO_EXT = 0x24,
+ CMD_WRITE_PIO = 0x30,
+ CMD_WRITE_PIO_EXT = 0x34,
/* Misc */
- CMD_READ_MAX = 0xF8,
- CMD_READ_MAX_EXT = 0x27,
- CMD_FLUSH_CACHE = 0xE7,
- CMD_IDENTIFY = 0xEC
+ CMD_READ_MAX = 0xF8,
+ CMD_READ_MAX_EXT = 0x27,
+ CMD_FLUSH_CACHE = 0xE7,
+ CMD_IDENTIFY = 0xEC,
+ /* NCQ */
+ READ_FPDMA_QUEUED = 0x60,
+ WRITE_FPDMA_QUEUED = 0x61,
};
/* AHCI Command Header Flags & Masks*/
@@ -291,8 +294,9 @@ enum {
#define CMDH_PMP (0xF000)
/* ATA device register masks */
-#define ATA_DEVICE_MAGIC 0xA0
+#define ATA_DEVICE_MAGIC 0xA0 /* used in ata1-3 */
#define ATA_DEVICE_LBA 0x40
+#define NCQ_DEVICE_MAGIC 0x40 /* for ncq device registers */
#define ATA_DEVICE_DRIVE 0x10
#define ATA_DEVICE_HEAD 0x0F
@@ -397,6 +401,32 @@ typedef struct RegH2DFIS {
} __attribute__((__packed__)) RegH2DFIS;
/**
+ * Register host-to-device FIS structure, for NCQ commands.
+ * Actually just a RegH2DFIS, but with fields repurposed.
+ * Repurposed fields are annotated below.
+ */
+typedef struct NCQFIS {
+ /* DW0 */
+ uint8_t fis_type;
+ uint8_t flags;
+ uint8_t command;
+ uint8_t sector_low; /* H2D: Feature 7:0 */
+ /* DW1 */
+ uint8_t lba_lo[3];
+ uint8_t device;
+ /* DW2 */
+ uint8_t lba_hi[3];
+ uint8_t sector_hi; /* H2D: Feature 15:8 */
+ /* DW3 */
+ uint8_t tag; /* H2D: Count 0:7 */
+ uint8_t prio; /* H2D: Count 15:8 */
+ uint8_t icc;
+ uint8_t control;
+ /* DW4 */
+ uint8_t aux[4];
+} __attribute__((__packed__)) NCQFIS;
+
+/**
* Command List entry structure.
* The command list contains between 1-32 of these structures.
*/
@@ -512,20 +542,22 @@ void ahci_port_check_nonbusy(AHCIQState *ahci, uint8_t port, uint8_t slot);
void ahci_port_check_d2h_sanity(AHCIQState *ahci, uint8_t port, uint8_t slot);
void ahci_port_check_pio_sanity(AHCIQState *ahci, uint8_t port,
uint8_t slot, size_t buffsize);
-void ahci_port_check_cmd_sanity(AHCIQState *ahci, uint8_t port,
- uint8_t slot, size_t buffsize);
+void ahci_port_check_cmd_sanity(AHCIQState *ahci, AHCICommand *cmd);
void ahci_get_command_header(AHCIQState *ahci, uint8_t port,
uint8_t slot, AHCICommandHeader *cmd);
void ahci_set_command_header(AHCIQState *ahci, uint8_t port,
uint8_t slot, AHCICommandHeader *cmd);
void ahci_destroy_command(AHCIQState *ahci, uint8_t port, uint8_t slot);
-void ahci_write_fis(AHCIQState *ahci, RegH2DFIS *fis, uint64_t addr);
+void ahci_write_fis(AHCIQState *ahci, AHCICommand *cmd);
unsigned ahci_pick_cmd(AHCIQState *ahci, uint8_t port);
unsigned size_to_prdtl(unsigned bytes, unsigned bytes_per_prd);
void ahci_guest_io(AHCIQState *ahci, uint8_t port, uint8_t ide_cmd,
- uint64_t gbuffer, size_t size);
+ uint64_t gbuffer, size_t size, uint64_t sector);
+AHCICommand *ahci_guest_io_halt(AHCIQState *ahci, uint8_t port, uint8_t ide_cmd,
+ uint64_t gbuffer, size_t size, uint64_t sector);
+void ahci_guest_io_resume(AHCIQState *ahci, AHCICommand *cmd);
void ahci_io(AHCIQState *ahci, uint8_t port, uint8_t ide_cmd,
- void *buffer, size_t bufsize);
+ void *buffer, size_t bufsize, uint64_t sector);
/* Command Lifecycle */
AHCICommand *ahci_command_create(uint8_t command_name);
diff --git a/tests/libqos/libqos-pc.c b/tests/libqos/libqos-pc.c
index bbace893f..140369937 100644
--- a/tests/libqos/libqos-pc.c
+++ b/tests/libqos/libqos-pc.c
@@ -6,6 +6,11 @@ static QOSOps qos_ops = {
.uninit_allocator = pc_alloc_uninit
};
+QOSState *qtest_pc_vboot(const char *cmdline_fmt, va_list ap)
+{
+ return qtest_vboot(&qos_ops, cmdline_fmt, ap);
+}
+
QOSState *qtest_pc_boot(const char *cmdline_fmt, ...)
{
QOSState *qs;
diff --git a/tests/libqos/libqos-pc.h b/tests/libqos/libqos-pc.h
index 316857d32..b1820c573 100644
--- a/tests/libqos/libqos-pc.h
+++ b/tests/libqos/libqos-pc.h
@@ -3,6 +3,7 @@
#include "libqos/libqos.h"
+QOSState *qtest_pc_vboot(const char *cmdline_fmt, va_list ap);
QOSState *qtest_pc_boot(const char *cmdline_fmt, ...);
void qtest_pc_shutdown(QOSState *qs);
diff --git a/tests/libqos/libqos.c b/tests/libqos/libqos.c
index bc8beb281..fce625b18 100644
--- a/tests/libqos/libqos.c
+++ b/tests/libqos/libqos.c
@@ -1,5 +1,6 @@
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include <glib.h>
#include <unistd.h>
#include <fcntl.h>
@@ -61,3 +62,153 @@ void qtest_shutdown(QOSState *qs)
qtest_quit(qs->qts);
g_free(qs);
}
+
+void set_context(QOSState *s)
+{
+ global_qtest = s->qts;
+}
+
+static QDict *qmp_execute(const char *command)
+{
+ char *fmt;
+ QDict *rsp;
+
+ fmt = g_strdup_printf("{ 'execute': '%s' }", command);
+ rsp = qmp(fmt);
+ g_free(fmt);
+
+ return rsp;
+}
+
+void migrate(QOSState *from, QOSState *to, const char *uri)
+{
+ const char *st;
+ char *s;
+ QDict *rsp, *sub;
+ bool running;
+
+ set_context(from);
+
+ /* Is the machine currently running? */
+ rsp = qmp_execute("query-status");
+ g_assert(qdict_haskey(rsp, "return"));
+ sub = qdict_get_qdict(rsp, "return");
+ g_assert(qdict_haskey(sub, "running"));
+ running = qdict_get_bool(sub, "running");
+ QDECREF(rsp);
+
+ /* Issue the migrate command. */
+ s = g_strdup_printf("{ 'execute': 'migrate',"
+ "'arguments': { 'uri': '%s' } }",
+ uri);
+ rsp = qmp(s);
+ g_free(s);
+ g_assert(qdict_haskey(rsp, "return"));
+ QDECREF(rsp);
+
+ /* Wait for STOP event, but only if we were running: */
+ if (running) {
+ qmp_eventwait("STOP");
+ }
+
+ /* If we were running, we can wait for an event. */
+ if (running) {
+ migrate_allocator(from->alloc, to->alloc);
+ set_context(to);
+ qmp_eventwait("RESUME");
+ return;
+ }
+
+ /* Otherwise, we need to wait: poll until migration is completed. */
+ while (1) {
+ rsp = qmp_execute("query-migrate");
+ g_assert(qdict_haskey(rsp, "return"));
+ sub = qdict_get_qdict(rsp, "return");
+ g_assert(qdict_haskey(sub, "status"));
+ st = qdict_get_str(sub, "status");
+
+ /* "setup", "active", "completed", "failed", "cancelled" */
+ if (strcmp(st, "completed") == 0) {
+ QDECREF(rsp);
+ break;
+ }
+
+ if ((strcmp(st, "setup") == 0) || (strcmp(st, "active") == 0)) {
+ QDECREF(rsp);
+ g_usleep(5000);
+ continue;
+ }
+
+ fprintf(stderr, "Migration did not complete, status: %s\n", st);
+ g_assert_not_reached();
+ }
+
+ migrate_allocator(from->alloc, to->alloc);
+ set_context(to);
+}
+
+void mkimg(const char *file, const char *fmt, unsigned size_mb)
+{
+ gchar *cli;
+ bool ret;
+ int rc;
+ GError *err = NULL;
+ char *qemu_img_path;
+ gchar *out, *out2;
+ char *abs_path;
+
+ qemu_img_path = getenv("QTEST_QEMU_IMG");
+ abs_path = realpath(qemu_img_path, NULL);
+ assert(qemu_img_path);
+
+ cli = g_strdup_printf("%s create -f %s %s %uM", abs_path,
+ fmt, file, size_mb);
+ ret = g_spawn_command_line_sync(cli, &out, &out2, &rc, &err);
+ if (err) {
+ fprintf(stderr, "%s\n", err->message);
+ g_error_free(err);
+ }
+ g_assert(ret && !err);
+
+ /* In glib 2.34, we have g_spawn_check_exit_status. in 2.12, we don't.
+ * glib 2.43.91 implementation assumes that any non-zero is an error for
+ * windows, but uses extra precautions for Linux. However,
+ * 0 is only possible if the program exited normally, so that should be
+ * sufficient for our purposes on all platforms, here. */
+ if (rc) {
+ fprintf(stderr, "qemu-img returned status code %d\n", rc);
+ }
+ g_assert(!rc);
+
+ g_free(out);
+ g_free(out2);
+ g_free(cli);
+ free(abs_path);
+}
+
+void mkqcow2(const char *file, unsigned size_mb)
+{
+ return mkimg(file, "qcow2", size_mb);
+}
+
+void prepare_blkdebug_script(const char *debug_fn, const char *event)
+{
+ FILE *debug_file = fopen(debug_fn, "w");
+ int ret;
+
+ fprintf(debug_file, "[inject-error]\n");
+ fprintf(debug_file, "event = \"%s\"\n", event);
+ fprintf(debug_file, "errno = \"5\"\n");
+ fprintf(debug_file, "state = \"1\"\n");
+ fprintf(debug_file, "immediately = \"off\"\n");
+ fprintf(debug_file, "once = \"on\"\n");
+
+ fprintf(debug_file, "[set-state]\n");
+ fprintf(debug_file, "event = \"%s\"\n", event);
+ fprintf(debug_file, "new_state = \"2\"\n");
+ fflush(debug_file);
+ g_assert(!ferror(debug_file));
+
+ ret = fclose(debug_file);
+ g_assert(ret == 0);
+}
diff --git a/tests/libqos/libqos.h b/tests/libqos/libqos.h
index 612d41e5e..e1f14ea6f 100644
--- a/tests/libqos/libqos.h
+++ b/tests/libqos/libqos.h
@@ -19,6 +19,11 @@ typedef struct QOSState {
QOSState *qtest_vboot(QOSOps *ops, const char *cmdline_fmt, va_list ap);
QOSState *qtest_boot(QOSOps *ops, const char *cmdline_fmt, ...);
void qtest_shutdown(QOSState *qs);
+void mkimg(const char *file, const char *fmt, unsigned size_mb);
+void mkqcow2(const char *file, unsigned size_mb);
+void set_context(QOSState *s);
+void migrate(QOSState *from, QOSState *to, const char *uri);
+void prepare_blkdebug_script(const char *debug_fn, const char *event);
static inline uint64_t qmalloc(QOSState *q, size_t bytes)
{
diff --git a/tests/libqos/malloc.c b/tests/libqos/malloc.c
index 67f31902f..82b9df537 100644
--- a/tests/libqos/malloc.c
+++ b/tests/libqos/malloc.c
@@ -30,8 +30,8 @@ struct QGuestAllocator {
uint64_t end;
uint32_t page_size;
- MemList used;
- MemList free;
+ MemList *used;
+ MemList *free;
};
#define DEFAULT_PAGE_SIZE 4096
@@ -150,7 +150,7 @@ static uint64_t mlist_fulfill(QGuestAllocator *s, MemBlock *freenode,
addr = freenode->addr;
if (freenode->size == size) {
/* re-use this freenode as our used node */
- QTAILQ_REMOVE(&s->free, freenode, MLIST_ENTNAME);
+ QTAILQ_REMOVE(s->free, freenode, MLIST_ENTNAME);
usednode = freenode;
} else {
/* adjust the free node and create a new used node */
@@ -159,7 +159,7 @@ static uint64_t mlist_fulfill(QGuestAllocator *s, MemBlock *freenode,
usednode = mlist_new(addr, size);
}
- mlist_sort_insert(&s->used, usednode);
+ mlist_sort_insert(s->used, usednode);
return addr;
}
@@ -171,7 +171,7 @@ static void mlist_check(QGuestAllocator *s)
uint64_t addr = s->start > 0 ? s->start - 1 : 0;
uint64_t next = s->start;
- QTAILQ_FOREACH(node, &s->free, MLIST_ENTNAME) {
+ QTAILQ_FOREACH(node, s->free, MLIST_ENTNAME) {
g_assert_cmpint(node->addr, >, addr);
g_assert_cmpint(node->addr, >=, next);
addr = node->addr;
@@ -180,7 +180,7 @@ static void mlist_check(QGuestAllocator *s)
addr = s->start > 0 ? s->start - 1 : 0;
next = s->start;
- QTAILQ_FOREACH(node, &s->used, MLIST_ENTNAME) {
+ QTAILQ_FOREACH(node, s->used, MLIST_ENTNAME) {
g_assert_cmpint(node->addr, >, addr);
g_assert_cmpint(node->addr, >=, next);
addr = node->addr;
@@ -192,7 +192,7 @@ static uint64_t mlist_alloc(QGuestAllocator *s, uint64_t size)
{
MemBlock *node;
- node = mlist_find_space(&s->free, size);
+ node = mlist_find_space(s->free, size);
if (!node) {
fprintf(stderr, "Out of guest memory.\n");
g_assert_not_reached();
@@ -208,7 +208,7 @@ static void mlist_free(QGuestAllocator *s, uint64_t addr)
return;
}
- node = mlist_find_key(&s->used, addr);
+ node = mlist_find_key(s->used, addr);
if (!node) {
fprintf(stderr, "Error: no record found for an allocation at "
"0x%016" PRIx64 ".\n",
@@ -217,9 +217,9 @@ static void mlist_free(QGuestAllocator *s, uint64_t addr)
}
/* Rip it out of the used list and re-insert back into the free list. */
- QTAILQ_REMOVE(&s->used, node, MLIST_ENTNAME);
- mlist_sort_insert(&s->free, node);
- mlist_coalesce(&s->free, node);
+ QTAILQ_REMOVE(s->used, node, MLIST_ENTNAME);
+ mlist_sort_insert(s->free, node);
+ mlist_coalesce(s->free, node);
}
/*
@@ -233,7 +233,7 @@ void alloc_uninit(QGuestAllocator *allocator)
QAllocOpts mask;
/* Check for guest leaks, and destroy the list. */
- QTAILQ_FOREACH_SAFE(node, &allocator->used, MLIST_ENTNAME, tmp) {
+ QTAILQ_FOREACH_SAFE(node, allocator->used, MLIST_ENTNAME, tmp) {
if (allocator->opts & (ALLOC_LEAK_WARN | ALLOC_LEAK_ASSERT)) {
fprintf(stderr, "guest malloc leak @ 0x%016" PRIx64 "; "
"size 0x%016" PRIx64 ".\n",
@@ -248,7 +248,7 @@ void alloc_uninit(QGuestAllocator *allocator)
/* If we have previously asserted that there are no leaks, then there
* should be only one node here with a specific address and size. */
mask = ALLOC_LEAK_ASSERT | ALLOC_PARANOID;
- QTAILQ_FOREACH_SAFE(node, &allocator->free, MLIST_ENTNAME, tmp) {
+ QTAILQ_FOREACH_SAFE(node, allocator->free, MLIST_ENTNAME, tmp) {
if ((allocator->opts & mask) == mask) {
if ((node->addr != allocator->start) ||
(node->size != allocator->end - allocator->start)) {
@@ -260,6 +260,8 @@ void alloc_uninit(QGuestAllocator *allocator)
g_free(node);
}
+ g_free(allocator->used);
+ g_free(allocator->free);
g_free(allocator);
}
@@ -283,6 +285,9 @@ uint64_t guest_alloc(QGuestAllocator *allocator, size_t size)
void guest_free(QGuestAllocator *allocator, uint64_t addr)
{
+ if (!addr) {
+ return;
+ }
mlist_free(allocator, addr);
if (allocator->opts & ALLOC_PARANOID) {
mlist_check(allocator);
@@ -297,11 +302,13 @@ QGuestAllocator *alloc_init(uint64_t start, uint64_t end)
s->start = start;
s->end = end;
- QTAILQ_INIT(&s->used);
- QTAILQ_INIT(&s->free);
+ s->used = g_malloc(sizeof(MemList));
+ s->free = g_malloc(sizeof(MemList));
+ QTAILQ_INIT(s->used);
+ QTAILQ_INIT(s->free);
node = mlist_new(s->start, s->end - s->start);
- QTAILQ_INSERT_HEAD(&s->free, node, MLIST_ENTNAME);
+ QTAILQ_INSERT_HEAD(s->free, node, MLIST_ENTNAME);
s->page_size = DEFAULT_PAGE_SIZE;
@@ -319,7 +326,7 @@ QGuestAllocator *alloc_init_flags(QAllocOpts opts,
void alloc_set_page_size(QGuestAllocator *allocator, size_t page_size)
{
/* Can't alter the page_size for an allocator in-use */
- g_assert(QTAILQ_EMPTY(&allocator->used));
+ g_assert(QTAILQ_EMPTY(allocator->used));
g_assert(is_power_of_2(page_size));
allocator->page_size = page_size;
@@ -329,3 +336,39 @@ void alloc_set_flags(QGuestAllocator *allocator, QAllocOpts opts)
{
allocator->opts |= opts;
}
+
+void migrate_allocator(QGuestAllocator *src,
+ QGuestAllocator *dst)
+{
+ MemBlock *node, *tmp;
+ MemList *tmpused, *tmpfree;
+
+ /* The general memory layout should be equivalent,
+ * though opts can differ. */
+ g_assert_cmphex(src->start, ==, dst->start);
+ g_assert_cmphex(src->end, ==, dst->end);
+
+ /* Destroy (silently, regardless of options) the dest-list: */
+ QTAILQ_FOREACH_SAFE(node, dst->used, MLIST_ENTNAME, tmp) {
+ g_free(node);
+ }
+ QTAILQ_FOREACH_SAFE(node, dst->free, MLIST_ENTNAME, tmp) {
+ g_free(node);
+ }
+
+ tmpused = dst->used;
+ tmpfree = dst->free;
+
+ /* Inherit the lists of the source allocator: */
+ dst->used = src->used;
+ dst->free = src->free;
+
+ /* Source is now re-initialized, the source memory is 'invalid' now: */
+ src->used = tmpused;
+ src->free = tmpfree;
+ QTAILQ_INIT(src->used);
+ QTAILQ_INIT(src->free);
+ node = mlist_new(src->start, src->end - src->start);
+ QTAILQ_INSERT_HEAD(src->free, node, MLIST_ENTNAME);
+ return;
+}
diff --git a/tests/libqos/malloc.h b/tests/libqos/malloc.h
index 71ac407dc..0c6c9b7f3 100644
--- a/tests/libqos/malloc.h
+++ b/tests/libqos/malloc.h
@@ -31,6 +31,7 @@ void alloc_uninit(QGuestAllocator *allocator);
/* Always returns page aligned values */
uint64_t guest_alloc(QGuestAllocator *allocator, size_t size);
void guest_free(QGuestAllocator *allocator, uint64_t addr);
+void migrate_allocator(QGuestAllocator *src, QGuestAllocator *dst);
QGuestAllocator *alloc_init(uint64_t start, uint64_t end);
QGuestAllocator *alloc_init_flags(QAllocOpts flags,
diff --git a/tests/libqos/virtio.h b/tests/libqos/virtio.h
index 2449feec5..01012787b 100644
--- a/tests/libqos/virtio.h
+++ b/tests/libqos/virtio.h
@@ -19,8 +19,14 @@
#define QVIRTIO_DRIVER 0x2
#define QVIRTIO_DRIVER_OK 0x4
-#define QVIRTIO_NET_DEVICE_ID 0x1
-#define QVIRTIO_BLK_DEVICE_ID 0x2
+#define QVIRTIO_NET_DEVICE_ID 0x1
+#define QVIRTIO_BLK_DEVICE_ID 0x2
+#define QVIRTIO_CONSOLE_DEVICE_ID 0x3
+#define QVIRTIO_RNG_DEVICE_ID 0x4
+#define QVIRTIO_BALLOON_DEVICE_ID 0x5
+#define QVIRTIO_RPMSG_DEVICE_ID 0x7
+#define QVIRTIO_SCSI_DEVICE_ID 0x8
+#define QVIRTIO_9P_DEVICE_ID 0x9
#define QVIRTIO_F_NOTIFY_ON_EMPTY 0x01000000
#define QVIRTIO_F_ANY_LAYOUT 0x08000000
diff --git a/tests/libqtest.c b/tests/libqtest.c
index 12d65bd1e..f1b8206c3 100644
--- a/tests/libqtest.c
+++ b/tests/libqtest.c
@@ -388,7 +388,12 @@ QDict *qtest_qmp_receive(QTestState *s)
return qmp.response;
}
-QDict *qtest_qmpv(QTestState *s, const char *fmt, va_list ap)
+/**
+ * Allow users to send a message without waiting for the reply,
+ * in the case that they choose to discard all replies up until
+ * a particular EVENT is received.
+ */
+void qtest_async_qmpv(QTestState *s, const char *fmt, va_list ap)
{
va_list ap_copy;
QObject *qobj;
@@ -417,6 +422,11 @@ QDict *qtest_qmpv(QTestState *s, const char *fmt, va_list ap)
QDECREF(qstr);
qobject_decref(qobj);
}
+}
+
+QDict *qtest_qmpv(QTestState *s, const char *fmt, va_list ap)
+{
+ qtest_async_qmpv(s, fmt, ap);
/* Receive reply */
return qtest_qmp_receive(s);
@@ -433,6 +443,15 @@ QDict *qtest_qmp(QTestState *s, const char *fmt, ...)
return response;
}
+void qtest_async_qmp(QTestState *s, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ qtest_async_qmpv(s, fmt, ap);
+ va_end(ap);
+}
+
void qtest_qmpv_discard_response(QTestState *s, const char *fmt, va_list ap)
{
QDict *response = qtest_qmpv(s, fmt, ap);
@@ -450,9 +469,53 @@ void qtest_qmp_discard_response(QTestState *s, const char *fmt, ...)
QDECREF(response);
}
+void qtest_qmp_eventwait(QTestState *s, const char *event)
+{
+ QDict *response;
+
+ for (;;) {
+ response = qtest_qmp_receive(s);
+ if ((qdict_haskey(response, "event")) &&
+ (strcmp(qdict_get_str(response, "event"), event) == 0)) {
+ QDECREF(response);
+ break;
+ }
+ QDECREF(response);
+ }
+}
+
+char *qtest_hmpv(QTestState *s, const char *fmt, va_list ap)
+{
+ char *cmd;
+ QDict *resp;
+ char *ret;
+
+ cmd = g_strdup_vprintf(fmt, ap);
+ resp = qtest_qmp(s, "{'execute': 'human-monitor-command',"
+ " 'arguments': {'command-line': %s}}",
+ cmd);
+ ret = g_strdup(qdict_get_try_str(resp, "return"));
+ g_assert(ret);
+ QDECREF(resp);
+ g_free(cmd);
+ return ret;
+}
+
+char *qtest_hmp(QTestState *s, const char *fmt, ...)
+{
+ va_list ap;
+ char *ret;
+
+ va_start(ap, fmt);
+ ret = qtest_hmpv(s, fmt, ap);
+ va_end(ap);
+ return ret;
+}
+
const char *qtest_get_arch(void)
{
const char *qemu = getenv("QTEST_QEMU_BINARY");
+ g_assert(qemu != NULL);
const char *end = strrchr(qemu, '/');
return end + strlen("/qemu-system-");
@@ -659,28 +722,55 @@ void qtest_add_data_func(const char *str, const void *data, void (*fn))
g_free(path);
}
+void qtest_bufwrite(QTestState *s, uint64_t addr, const void *data, size_t size)
+{
+ gchar *bdata;
+
+ bdata = g_base64_encode(data, size);
+ qtest_sendf(s, "b64write 0x%" PRIx64 " 0x%zx ", addr, size);
+ socket_send(s->fd, bdata, strlen(bdata));
+ socket_send(s->fd, "\n", 1);
+ qtest_rsp(s, 0);
+ g_free(bdata);
+}
+
+void qtest_bufread(QTestState *s, uint64_t addr, void *data, size_t size)
+{
+ gchar **args;
+ size_t len;
+
+ qtest_sendf(s, "b64read 0x%" PRIx64 " 0x%zx\n", addr, size);
+ args = qtest_rsp(s, 2);
+
+ g_base64_decode_inplace(args[1], &len);
+ if (size != len) {
+ fprintf(stderr, "bufread: asked for %zu bytes but decoded %zu\n",
+ size, len);
+ len = MIN(len, size);
+ }
+
+ memcpy(data, args[1], len);
+ g_strfreev(args);
+}
+
void qtest_memwrite(QTestState *s, uint64_t addr, const void *data, size_t size)
{
const uint8_t *ptr = data;
size_t i;
+ char *enc = g_malloc(2 * size + 1);
- qtest_sendf(s, "write 0x%" PRIx64 " 0x%zx 0x", addr, size);
for (i = 0; i < size; i++) {
- qtest_sendf(s, "%02x", ptr[i]);
+ sprintf(&enc[i * 2], "%02x", ptr[i]);
}
- qtest_sendf(s, "\n");
+
+ qtest_sendf(s, "write 0x%" PRIx64 " 0x%zx 0x%s\n", addr, size, enc);
qtest_rsp(s, 0);
+ g_free(enc);
}
void qtest_memset(QTestState *s, uint64_t addr, uint8_t pattern, size_t size)
{
- size_t i;
-
- qtest_sendf(s, "write 0x%" PRIx64 " 0x%zx 0x", addr, size);
- for (i = 0; i < size; i++) {
- qtest_sendf(s, "%02x", pattern);
- }
- qtest_sendf(s, "\n");
+ qtest_sendf(s, "memset 0x%" PRIx64 " 0x%zx 0x%02x\n", addr, size, pattern);
qtest_rsp(s, 0);
}
@@ -695,6 +785,15 @@ QDict *qmp(const char *fmt, ...)
return response;
}
+void qmp_async(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ qtest_async_qmpv(global_qtest, fmt, ap);
+ va_end(ap);
+}
+
void qmp_discard_response(const char *fmt, ...)
{
va_list ap;
@@ -703,6 +802,16 @@ void qmp_discard_response(const char *fmt, ...)
qtest_qmpv_discard_response(global_qtest, fmt, ap);
va_end(ap);
}
+char *hmp(const char *fmt, ...)
+{
+ va_list ap;
+ char *ret;
+
+ va_start(ap, fmt);
+ ret = qtest_hmpv(global_qtest, fmt, ap);
+ va_end(ap);
+ return ret;
+}
bool qtest_big_endian(void)
{
diff --git a/tests/libqtest.h b/tests/libqtest.h
index 03469b878..55bccbf0e 100644
--- a/tests/libqtest.h
+++ b/tests/libqtest.h
@@ -64,6 +64,15 @@ void qtest_qmp_discard_response(QTestState *s, const char *fmt, ...);
QDict *qtest_qmp(QTestState *s, const char *fmt, ...);
/**
+ * qtest_async_qmp:
+ * @s: #QTestState instance to operate on.
+ * @fmt...: QMP message to send to qemu
+ *
+ * Sends a QMP message to QEMU and leaves the response in the stream.
+ */
+void qtest_async_qmp(QTestState *s, const char *fmt, ...);
+
+/**
* qtest_qmpv_discard_response:
* @s: #QTestState instance to operate on.
* @fmt: QMP message to send to QEMU
@@ -84,6 +93,16 @@ void qtest_qmpv_discard_response(QTestState *s, const char *fmt, va_list ap);
QDict *qtest_qmpv(QTestState *s, const char *fmt, va_list ap);
/**
+ * qtest_async_qmpv:
+ * @s: #QTestState instance to operate on.
+ * @fmt: QMP message to send to QEMU
+ * @ap: QMP message arguments
+ *
+ * Sends a QMP message to QEMU and leaves the response in the stream.
+ */
+void qtest_async_qmpv(QTestState *s, const char *fmt, va_list ap);
+
+/**
* qtest_receive:
* @s: #QTestState instance to operate on.
*
@@ -92,6 +111,38 @@ QDict *qtest_qmpv(QTestState *s, const char *fmt, va_list ap);
QDict *qtest_qmp_receive(QTestState *s);
/**
+ * qtest_qmp_eventwait:
+ * @s: #QTestState instance to operate on.
+ * @s: #event event to wait for.
+ *
+ * Continuosly polls for QMP responses until it receives the desired event.
+ */
+void qtest_qmp_eventwait(QTestState *s, const char *event);
+
+/**
+ * qtest_hmpv:
+ * @s: #QTestState instance to operate on.
+ * @fmt...: HMP command to send to QEMU
+ *
+ * Send HMP command to QEMU via QMP's human-monitor-command.
+ *
+ * Returns: the command's output. The caller should g_free() it.
+ */
+char *qtest_hmp(QTestState *s, const char *fmt, ...);
+
+/**
+ * qtest_hmpv:
+ * @s: #QTestState instance to operate on.
+ * @fmt: HMP command to send to QEMU
+ * @ap: HMP command arguments
+ *
+ * Send HMP command to QEMU via QMP's human-monitor-command.
+ *
+ * Returns: the command's output. The caller should g_free() it.
+ */
+char *qtest_hmpv(QTestState *s, const char *fmt, va_list ap);
+
+/**
* qtest_get_irq:
* @s: #QTestState instance to operate on.
* @num: Interrupt to observe.
@@ -273,6 +324,17 @@ uint64_t qtest_readq(QTestState *s, uint64_t addr);
void qtest_memread(QTestState *s, uint64_t addr, void *data, size_t size);
/**
+ * qtest_bufread:
+ * @s: #QTestState instance to operate on.
+ * @addr: Guest address to read from.
+ * @data: Pointer to where memory contents will be stored.
+ * @size: Number of bytes to read.
+ *
+ * Read guest memory into a buffer and receive using a base64 encoding.
+ */
+void qtest_bufread(QTestState *s, uint64_t addr, void *data, size_t size);
+
+/**
* qtest_memwrite:
* @s: #QTestState instance to operate on.
* @addr: Guest address to write to.
@@ -284,6 +346,18 @@ void qtest_memread(QTestState *s, uint64_t addr, void *data, size_t size);
void qtest_memwrite(QTestState *s, uint64_t addr, const void *data, size_t size);
/**
+ * qtest_bufwrite:
+ * @s: #QTestState instance to operate on.
+ * @addr: Guest address to write to.
+ * @data: Pointer to the bytes that will be written to guest memory.
+ * @size: Number of bytes to write.
+ *
+ * Write a buffer to guest memory and transmit using a base64 encoding.
+ */
+void qtest_bufwrite(QTestState *s, uint64_t addr,
+ const void *data, size_t size);
+
+/**
* qtest_memset:
* @s: #QTestState instance to operate on.
* @addr: Guest address to write to.
@@ -411,6 +485,14 @@ static inline void qtest_end(void)
QDict *qmp(const char *fmt, ...);
/**
+ * qmp_async:
+ * @fmt...: QMP message to send to qemu
+ *
+ * Sends a QMP message to QEMU and leaves the response in the stream.
+ */
+void qmp_async(const char *fmt, ...);
+
+/**
* qmp_discard_response:
* @fmt...: QMP message to send to qemu
*
@@ -429,6 +511,27 @@ static inline QDict *qmp_receive(void)
}
/**
+ * qmp_eventwait:
+ * @s: #event event to wait for.
+ *
+ * Continuosly polls for QMP responses until it receives the desired event.
+ */
+static inline void qmp_eventwait(const char *event)
+{
+ return qtest_qmp_eventwait(global_qtest, event);
+}
+
+/**
+ * hmp:
+ * @fmt...: HMP command to send to QEMU
+ *
+ * Send HMP command to QEMU via QMP's human-monitor-command.
+ *
+ * Returns: the command's output. The caller should g_free() it.
+ */
+char *hmp(const char *fmt, ...);
+
+/**
* get_irq:
* @num: Interrupt to observe.
*
@@ -652,6 +755,19 @@ static inline void memread(uint64_t addr, void *data, size_t size)
}
/**
+ * bufread:
+ * @addr: Guest address to read from.
+ * @data: Pointer to where memory contents will be stored.
+ * @size: Number of bytes to read.
+ *
+ * Read guest memory into a buffer, receive using a base64 encoding.
+ */
+static inline void bufread(uint64_t addr, void *data, size_t size)
+{
+ qtest_bufread(global_qtest, addr, data, size);
+}
+
+/**
* memwrite:
* @addr: Guest address to write to.
* @data: Pointer to the bytes that will be written to guest memory.
@@ -665,6 +781,19 @@ static inline void memwrite(uint64_t addr, const void *data, size_t size)
}
/**
+ * bufwrite:
+ * @addr: Guest address to write to.
+ * @data: Pointer to the bytes that will be written to guest memory.
+ * @size: Number of bytes to write.
+ *
+ * Write a buffer to guest memory, transmit using a base64 encoding.
+ */
+static inline void bufwrite(uint64_t addr, const void *data, size_t size)
+{
+ qtest_bufwrite(global_qtest, addr, data, size);
+}
+
+/**
* qmemset:
* @addr: Guest address to write to.
* @patt: Byte pattern to fill the guest memory region with.
diff --git a/tests/pc-cpu-test.c b/tests/pc-cpu-test.c
index a0122d3d6..3505c7c43 100644
--- a/tests/pc-cpu-test.c
+++ b/tests/pc-cpu-test.c
@@ -75,7 +75,6 @@ static void test_pc_without_cpu_add(gconstpointer data)
static void add_pc_test_cases(void)
{
- const char *arch = qtest_get_arch();
QDict *response, *minfo;
QList *list;
const QListEntry *p;
@@ -119,15 +118,15 @@ static void add_pc_test_cases(void)
(strcmp(mname, "pc-0.12") == 0) ||
(strcmp(mname, "pc-0.11") == 0) ||
(strcmp(mname, "pc-0.10") == 0)) {
- path = g_strdup_printf("/%s/cpu/%s/init/%ux%ux%u&maxcpus=%u",
- arch, mname, data->sockets, data->cores,
+ path = g_strdup_printf("cpu/%s/init/%ux%ux%u&maxcpus=%u",
+ mname, data->sockets, data->cores,
data->threads, data->maxcpus);
- g_test_add_data_func(path, data, test_pc_without_cpu_add);
+ qtest_add_data_func(path, data, test_pc_without_cpu_add);
} else {
- path = g_strdup_printf("/%s/cpu/%s/add/%ux%ux%u&maxcpus=%u",
- arch, mname, data->sockets, data->cores,
+ path = g_strdup_printf("cpu/%s/add/%ux%ux%u&maxcpus=%u",
+ mname, data->sockets, data->cores,
data->threads, data->maxcpus);
- g_test_add_data_func(path, data, test_pc_with_cpu_add);
+ qtest_add_data_func(path, data, test_pc_with_cpu_add);
}
}
qtest_end();
diff --git a/tests/q35-test.c b/tests/q35-test.c
new file mode 100644
index 000000000..812abe548
--- /dev/null
+++ b/tests/q35-test.c
@@ -0,0 +1,91 @@
+/*
+ * QTest testcase for Q35 northbridge
+ *
+ * Copyright (c) 2015 Red Hat, Inc.
+ *
+ * Author: Gerd Hoffmann <kraxel@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 <glib.h>
+#include <string.h>
+#include "libqtest.h"
+#include "libqos/pci.h"
+#include "libqos/pci-pc.h"
+#include "qemu/osdep.h"
+#include "hw/pci-host/q35.h"
+
+static void smram_set_bit(QPCIDevice *pcidev, uint8_t mask, bool enabled)
+{
+ uint8_t smram;
+
+ smram = qpci_config_readb(pcidev, MCH_HOST_BRIDGE_SMRAM);
+ if (enabled) {
+ smram |= mask;
+ } else {
+ smram &= ~mask;
+ }
+ qpci_config_writeb(pcidev, MCH_HOST_BRIDGE_SMRAM, smram);
+}
+
+static bool smram_test_bit(QPCIDevice *pcidev, uint8_t mask)
+{
+ uint8_t smram;
+
+ smram = qpci_config_readb(pcidev, MCH_HOST_BRIDGE_SMRAM);
+ return smram & mask;
+}
+
+static void test_smram_lock(void)
+{
+ QPCIBus *pcibus;
+ QPCIDevice *pcidev;
+ QDict *response;
+
+ pcibus = qpci_init_pc();
+ g_assert(pcibus != NULL);
+
+ pcidev = qpci_device_find(pcibus, 0);
+ g_assert(pcidev != NULL);
+
+ /* check open is settable */
+ smram_set_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN, false);
+ g_assert(smram_test_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN) == false);
+ smram_set_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN, true);
+ g_assert(smram_test_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN) == true);
+
+ /* lock, check open is cleared & not settable */
+ smram_set_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_LCK, true);
+ g_assert(smram_test_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN) == false);
+ smram_set_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN, true);
+ g_assert(smram_test_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN) == false);
+
+ /* reset */
+ response = qmp("{'execute': 'system_reset', 'arguments': {} }");
+ g_assert(response);
+ g_assert(!qdict_haskey(response, "error"));
+ QDECREF(response);
+
+ /* check open is settable again */
+ smram_set_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN, false);
+ g_assert(smram_test_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN) == false);
+ smram_set_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN, true);
+ g_assert(smram_test_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN) == true);
+}
+
+int main(int argc, char **argv)
+{
+ int ret;
+
+ g_test_init(&argc, &argv, NULL);
+
+ qtest_add_func("/q35/smram/lock", test_smram_lock);
+
+ qtest_start("-M q35");
+ ret = g_test_run();
+ qtest_end();
+
+ return ret;
+}
diff --git a/tests/qapi-schema/alternate-array.err b/tests/qapi-schema/alternate-array.err
new file mode 100644
index 000000000..7b930c64a
--- /dev/null
+++ b/tests/qapi-schema/alternate-array.err
@@ -0,0 +1 @@
+tests/qapi-schema/alternate-array.json:5: Member 'two' of alternate 'Alt' cannot be an array
diff --git a/tests/qapi-schema/alternate-array.exit b/tests/qapi-schema/alternate-array.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/alternate-array.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/alternate-array.json b/tests/qapi-schema/alternate-array.json
new file mode 100644
index 000000000..f241aac12
--- /dev/null
+++ b/tests/qapi-schema/alternate-array.json
@@ -0,0 +1,7 @@
+# we do not allow array branches in alternates
+# TODO: should we support this?
+{ 'struct': 'One',
+ 'data': { 'name': 'str' } }
+{ 'alternate': 'Alt',
+ 'data': { 'one': 'One',
+ 'two': [ 'int' ] } }
diff --git a/tests/qapi-schema/alternate-array.out b/tests/qapi-schema/alternate-array.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/alternate-array.out
diff --git a/tests/qapi-schema/alternate-base.err b/tests/qapi-schema/alternate-base.err
new file mode 100644
index 000000000..30d8a3437
--- /dev/null
+++ b/tests/qapi-schema/alternate-base.err
@@ -0,0 +1 @@
+tests/qapi-schema/alternate-base.json:4: Unknown key 'base' in alternate 'Alt'
diff --git a/tests/qapi-schema/alternate-base.exit b/tests/qapi-schema/alternate-base.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/alternate-base.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/alternate-base.json b/tests/qapi-schema/alternate-base.json
new file mode 100644
index 000000000..529430ecf
--- /dev/null
+++ b/tests/qapi-schema/alternate-base.json
@@ -0,0 +1,6 @@
+# we reject alternate with base type
+{ 'struct': 'Base',
+ 'data': { 'string': 'str' } }
+{ 'alternate': 'Alt',
+ 'base': 'Base',
+ 'data': { 'number': 'int' } }
diff --git a/tests/qapi-schema/alternate-base.out b/tests/qapi-schema/alternate-base.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/alternate-base.out
diff --git a/tests/qapi-schema/alternate-clash.err b/tests/qapi-schema/alternate-clash.err
new file mode 100644
index 000000000..51bea3e27
--- /dev/null
+++ b/tests/qapi-schema/alternate-clash.err
@@ -0,0 +1 @@
+tests/qapi-schema/alternate-clash.json:2: Alternate 'Alt1' member 'ONE' clashes with 'one'
diff --git a/tests/qapi-schema/alternate-clash.exit b/tests/qapi-schema/alternate-clash.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/alternate-clash.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/alternate-clash.json b/tests/qapi-schema/alternate-clash.json
new file mode 100644
index 000000000..39479353b
--- /dev/null
+++ b/tests/qapi-schema/alternate-clash.json
@@ -0,0 +1,3 @@
+# we detect C enum collisions in an alternate
+{ 'alternate': 'Alt1',
+ 'data': { 'one': 'str', 'ONE': 'int' } }
diff --git a/tests/qapi-schema/alternate-clash.out b/tests/qapi-schema/alternate-clash.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/alternate-clash.out
diff --git a/tests/qapi-schema/alternate-conflict-dict.err b/tests/qapi-schema/alternate-conflict-dict.err
new file mode 100644
index 000000000..0f411f4fa
--- /dev/null
+++ b/tests/qapi-schema/alternate-conflict-dict.err
@@ -0,0 +1 @@
+tests/qapi-schema/alternate-conflict-dict.json:6: Alternate 'Alt' member 'two' can't be distinguished from member 'one'
diff --git a/tests/qapi-schema/alternate-conflict-dict.exit b/tests/qapi-schema/alternate-conflict-dict.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/alternate-conflict-dict.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/alternate-conflict-dict.json b/tests/qapi-schema/alternate-conflict-dict.json
new file mode 100644
index 000000000..d566cca81
--- /dev/null
+++ b/tests/qapi-schema/alternate-conflict-dict.json
@@ -0,0 +1,8 @@
+# we reject alternates with multiple object branches
+{ 'struct': 'One',
+ 'data': { 'name': 'str' } }
+{ 'struct': 'Two',
+ 'data': { 'value': 'int' } }
+{ 'alternate': 'Alt',
+ 'data': { 'one': 'One',
+ 'two': 'Two' } }
diff --git a/tests/qapi-schema/alternate-conflict-dict.out b/tests/qapi-schema/alternate-conflict-dict.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/alternate-conflict-dict.out
diff --git a/tests/qapi-schema/alternate-conflict-string.err b/tests/qapi-schema/alternate-conflict-string.err
new file mode 100644
index 000000000..fc523b087
--- /dev/null
+++ b/tests/qapi-schema/alternate-conflict-string.err
@@ -0,0 +1 @@
+tests/qapi-schema/alternate-conflict-string.json:4: Alternate 'Alt' member 'two' can't be distinguished from member 'one'
diff --git a/tests/qapi-schema/alternate-conflict-string.exit b/tests/qapi-schema/alternate-conflict-string.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/alternate-conflict-string.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/alternate-conflict-string.json b/tests/qapi-schema/alternate-conflict-string.json
new file mode 100644
index 000000000..72f04a820
--- /dev/null
+++ b/tests/qapi-schema/alternate-conflict-string.json
@@ -0,0 +1,6 @@
+# we reject alternates with multiple string-like branches
+{ 'enum': 'Enum',
+ 'data': [ 'hello', 'world' ] }
+{ 'alternate': 'Alt',
+ 'data': { 'one': 'str',
+ 'two': 'Enum' } }
diff --git a/tests/qapi-schema/alternate-conflict-string.out b/tests/qapi-schema/alternate-conflict-string.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/alternate-conflict-string.out
diff --git a/tests/qapi-schema/alternate-good.err b/tests/qapi-schema/alternate-good.err
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/alternate-good.err
diff --git a/tests/qapi-schema/alternate-good.exit b/tests/qapi-schema/alternate-good.exit
new file mode 100644
index 000000000..573541ac9
--- /dev/null
+++ b/tests/qapi-schema/alternate-good.exit
@@ -0,0 +1 @@
+0
diff --git a/tests/qapi-schema/alternate-good.json b/tests/qapi-schema/alternate-good.json
new file mode 100644
index 000000000..33717704c
--- /dev/null
+++ b/tests/qapi-schema/alternate-good.json
@@ -0,0 +1,9 @@
+# Working example of alternate
+{ 'struct': 'Data',
+ 'data': { '*number': 'int', '*name': 'str' } }
+{ 'enum': 'Enum',
+ 'data': [ 'hello', 'world' ] }
+{ 'alternate': 'Alt',
+ 'data': { 'value': 'int',
+ 'string': 'Enum',
+ 'struct': 'Data' } }
diff --git a/tests/qapi-schema/alternate-good.out b/tests/qapi-schema/alternate-good.out
new file mode 100644
index 000000000..99848eefb
--- /dev/null
+++ b/tests/qapi-schema/alternate-good.out
@@ -0,0 +1,6 @@
+[OrderedDict([('struct', 'Data'), ('data', OrderedDict([('*number', 'int'), ('*name', 'str')]))]),
+ OrderedDict([('enum', 'Enum'), ('data', ['hello', 'world'])]),
+ OrderedDict([('alternate', 'Alt'), ('data', OrderedDict([('value', 'int'), ('string', 'Enum'), ('struct', 'Data')]))])]
+[{'enum_name': 'Enum', 'enum_values': ['hello', 'world']},
+ {'enum_name': 'AltKind', 'enum_values': None}]
+[OrderedDict([('struct', 'Data'), ('data', OrderedDict([('*number', 'int'), ('*name', 'str')]))])]
diff --git a/tests/qapi-schema/alternate-nested.err b/tests/qapi-schema/alternate-nested.err
new file mode 100644
index 000000000..4d1187e60
--- /dev/null
+++ b/tests/qapi-schema/alternate-nested.err
@@ -0,0 +1 @@
+tests/qapi-schema/alternate-nested.json:4: Member 'nested' of alternate 'Alt2' cannot use alternate type 'Alt1'
diff --git a/tests/qapi-schema/alternate-nested.exit b/tests/qapi-schema/alternate-nested.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/alternate-nested.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/alternate-nested.json b/tests/qapi-schema/alternate-nested.json
new file mode 100644
index 000000000..c4233b9f3
--- /dev/null
+++ b/tests/qapi-schema/alternate-nested.json
@@ -0,0 +1,5 @@
+# we reject a nested alternate branch
+{ 'alternate': 'Alt1',
+ 'data': { 'name': 'str', 'value': 'int' } }
+{ 'alternate': 'Alt2',
+ 'data': { 'nested': 'Alt1' } }
diff --git a/tests/qapi-schema/alternate-nested.out b/tests/qapi-schema/alternate-nested.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/alternate-nested.out
diff --git a/tests/qapi-schema/alternate-unknown.err b/tests/qapi-schema/alternate-unknown.err
new file mode 100644
index 000000000..dea45dc73
--- /dev/null
+++ b/tests/qapi-schema/alternate-unknown.err
@@ -0,0 +1 @@
+tests/qapi-schema/alternate-unknown.json:2: Member 'unknown' of alternate 'Alt' uses unknown type 'MissingType'
diff --git a/tests/qapi-schema/alternate-unknown.exit b/tests/qapi-schema/alternate-unknown.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/alternate-unknown.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/alternate-unknown.json b/tests/qapi-schema/alternate-unknown.json
new file mode 100644
index 000000000..ad5c10302
--- /dev/null
+++ b/tests/qapi-schema/alternate-unknown.json
@@ -0,0 +1,3 @@
+# we reject an alternate with unknown type in branch
+{ 'alternate': 'Alt',
+ 'data': { 'unknown': 'MissingType' } }
diff --git a/tests/qapi-schema/alternate-unknown.out b/tests/qapi-schema/alternate-unknown.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/alternate-unknown.out
diff --git a/tests/qapi-schema/bad-base.err b/tests/qapi-schema/bad-base.err
new file mode 100644
index 000000000..154274bdd
--- /dev/null
+++ b/tests/qapi-schema/bad-base.err
@@ -0,0 +1 @@
+tests/qapi-schema/bad-base.json:3: 'base' for struct 'MyType' cannot use union type 'Union'
diff --git a/tests/qapi-schema/bad-base.exit b/tests/qapi-schema/bad-base.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/bad-base.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/bad-base.json b/tests/qapi-schema/bad-base.json
new file mode 100644
index 000000000..a634331cd
--- /dev/null
+++ b/tests/qapi-schema/bad-base.json
@@ -0,0 +1,3 @@
+# we reject a base that is not a struct
+{ 'union': 'Union', 'data': { 'a': 'int', 'b': 'str' } }
+{ 'struct': 'MyType', 'base': 'Union', 'data': { 'c': 'int' } }
diff --git a/tests/qapi-schema/bad-base.out b/tests/qapi-schema/bad-base.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/bad-base.out
diff --git a/tests/qapi-schema/bad-data.err b/tests/qapi-schema/bad-data.err
new file mode 100644
index 000000000..8523ac4f4
--- /dev/null
+++ b/tests/qapi-schema/bad-data.err
@@ -0,0 +1 @@
+tests/qapi-schema/bad-data.json:2: 'data' for command 'oops' cannot be an array
diff --git a/tests/qapi-schema/bad-data.exit b/tests/qapi-schema/bad-data.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/bad-data.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/bad-data.json b/tests/qapi-schema/bad-data.json
new file mode 100644
index 000000000..832eeb76f
--- /dev/null
+++ b/tests/qapi-schema/bad-data.json
@@ -0,0 +1,2 @@
+# we ensure 'data' is a dictionary for all but enums
+{ 'command': 'oops', 'data': [ ] }
diff --git a/tests/qapi-schema/bad-data.out b/tests/qapi-schema/bad-data.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/bad-data.out
diff --git a/tests/qapi-schema/bad-ident.err b/tests/qapi-schema/bad-ident.err
new file mode 100644
index 000000000..c4190602b
--- /dev/null
+++ b/tests/qapi-schema/bad-ident.err
@@ -0,0 +1 @@
+tests/qapi-schema/bad-ident.json:2: 'struct' does not allow optional name '*oops'
diff --git a/tests/qapi-schema/bad-ident.exit b/tests/qapi-schema/bad-ident.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/bad-ident.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/bad-ident.json b/tests/qapi-schema/bad-ident.json
new file mode 100644
index 000000000..763627ad2
--- /dev/null
+++ b/tests/qapi-schema/bad-ident.json
@@ -0,0 +1,2 @@
+# we reject creating a type name with bad name
+{ 'struct': '*oops', 'data': { 'i': 'int' } }
diff --git a/tests/qapi-schema/bad-ident.out b/tests/qapi-schema/bad-ident.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/bad-ident.out
diff --git a/tests/qapi-schema/bad-type-bool.err b/tests/qapi-schema/bad-type-bool.err
new file mode 100644
index 000000000..62fd70baa
--- /dev/null
+++ b/tests/qapi-schema/bad-type-bool.err
@@ -0,0 +1 @@
+tests/qapi-schema/bad-type-bool.json:2: 'struct' key must have a string value
diff --git a/tests/qapi-schema/bad-type-bool.exit b/tests/qapi-schema/bad-type-bool.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/bad-type-bool.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/bad-type-bool.json b/tests/qapi-schema/bad-type-bool.json
new file mode 100644
index 000000000..bde17b56c
--- /dev/null
+++ b/tests/qapi-schema/bad-type-bool.json
@@ -0,0 +1,2 @@
+# we reject an expression with a metatype that is not a string
+{ 'struct': true, 'data': { } }
diff --git a/tests/qapi-schema/bad-type-bool.out b/tests/qapi-schema/bad-type-bool.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/bad-type-bool.out
diff --git a/tests/qapi-schema/bad-type-dict.err b/tests/qapi-schema/bad-type-dict.err
new file mode 100644
index 000000000..0b2a2aeac
--- /dev/null
+++ b/tests/qapi-schema/bad-type-dict.err
@@ -0,0 +1 @@
+tests/qapi-schema/bad-type-dict.json:2: 'command' key must have a string value
diff --git a/tests/qapi-schema/bad-type-dict.exit b/tests/qapi-schema/bad-type-dict.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/bad-type-dict.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/bad-type-dict.json b/tests/qapi-schema/bad-type-dict.json
new file mode 100644
index 000000000..2a91b241f
--- /dev/null
+++ b/tests/qapi-schema/bad-type-dict.json
@@ -0,0 +1,2 @@
+# we reject an expression with a metatype that is not a string
+{ 'command': { } }
diff --git a/tests/qapi-schema/bad-type-dict.out b/tests/qapi-schema/bad-type-dict.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/bad-type-dict.out
diff --git a/tests/qapi-schema/bad-type-int.err b/tests/qapi-schema/bad-type-int.err
new file mode 100644
index 000000000..da8989540
--- /dev/null
+++ b/tests/qapi-schema/bad-type-int.err
@@ -0,0 +1 @@
+tests/qapi-schema/bad-type-int.json:3:13: Stray "1"
diff --git a/tests/qapi-schema/bad-type-int.exit b/tests/qapi-schema/bad-type-int.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/bad-type-int.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/bad-type-int.json b/tests/qapi-schema/bad-type-int.json
new file mode 100644
index 000000000..56fc6f812
--- /dev/null
+++ b/tests/qapi-schema/bad-type-int.json
@@ -0,0 +1,3 @@
+# we reject an expression with a metatype that is not a string
+# FIXME: once the parser understands integer inputs, improve the error message
+{ 'struct': 1, 'data': { } }
diff --git a/tests/qapi-schema/bad-type-int.out b/tests/qapi-schema/bad-type-int.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/bad-type-int.out
diff --git a/tests/qapi-schema/command-int.err b/tests/qapi-schema/command-int.err
new file mode 100644
index 000000000..0f9300679
--- /dev/null
+++ b/tests/qapi-schema/command-int.err
@@ -0,0 +1 @@
+tests/qapi-schema/command-int.json:2: built-in 'int' is already defined
diff --git a/tests/qapi-schema/command-int.exit b/tests/qapi-schema/command-int.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/command-int.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/command-int.json b/tests/qapi-schema/command-int.json
new file mode 100644
index 000000000..c90d408ab
--- /dev/null
+++ b/tests/qapi-schema/command-int.json
@@ -0,0 +1,3 @@
+# we reject collisions between commands and types
+{ 'command': 'int', 'data': { 'character': 'str' },
+ 'returns': { 'value': 'int' } }
diff --git a/tests/qapi-schema/command-int.out b/tests/qapi-schema/command-int.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/command-int.out
diff --git a/tests/qapi-schema/data-array-empty.err b/tests/qapi-schema/data-array-empty.err
new file mode 100644
index 000000000..f713f1489
--- /dev/null
+++ b/tests/qapi-schema/data-array-empty.err
@@ -0,0 +1 @@
+tests/qapi-schema/data-array-empty.json:2: Member 'empty' of 'data' for command 'oops': array type must contain single type name
diff --git a/tests/qapi-schema/data-array-empty.exit b/tests/qapi-schema/data-array-empty.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/data-array-empty.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/data-array-empty.json b/tests/qapi-schema/data-array-empty.json
new file mode 100644
index 000000000..652dcfb24
--- /dev/null
+++ b/tests/qapi-schema/data-array-empty.json
@@ -0,0 +1,2 @@
+# we reject an array for data if it does not contain a known type
+{ 'command': 'oops', 'data': { 'empty': [ ] } }
diff --git a/tests/qapi-schema/data-array-empty.out b/tests/qapi-schema/data-array-empty.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/data-array-empty.out
diff --git a/tests/qapi-schema/data-array-unknown.err b/tests/qapi-schema/data-array-unknown.err
new file mode 100644
index 000000000..8b731bbcc
--- /dev/null
+++ b/tests/qapi-schema/data-array-unknown.err
@@ -0,0 +1 @@
+tests/qapi-schema/data-array-unknown.json:2: Member 'array' of 'data' for command 'oops' uses unknown type 'array of NoSuchType'
diff --git a/tests/qapi-schema/data-array-unknown.exit b/tests/qapi-schema/data-array-unknown.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/data-array-unknown.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/data-array-unknown.json b/tests/qapi-schema/data-array-unknown.json
new file mode 100644
index 000000000..6f3e88331
--- /dev/null
+++ b/tests/qapi-schema/data-array-unknown.json
@@ -0,0 +1,2 @@
+# we reject an array for data if it does not contain a known type
+{ 'command': 'oops', 'data': { 'array': [ 'NoSuchType' ] } }
diff --git a/tests/qapi-schema/data-array-unknown.out b/tests/qapi-schema/data-array-unknown.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/data-array-unknown.out
diff --git a/tests/qapi-schema/data-int.err b/tests/qapi-schema/data-int.err
new file mode 100644
index 000000000..1a9b077c0
--- /dev/null
+++ b/tests/qapi-schema/data-int.err
@@ -0,0 +1 @@
+tests/qapi-schema/data-int.json:2: 'data' for command 'oops' cannot use built-in type 'int'
diff --git a/tests/qapi-schema/data-int.exit b/tests/qapi-schema/data-int.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/data-int.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/data-int.json b/tests/qapi-schema/data-int.json
new file mode 100644
index 000000000..a334d92e8
--- /dev/null
+++ b/tests/qapi-schema/data-int.json
@@ -0,0 +1,2 @@
+# we reject commands where data is not an array or complex type
+{ 'command': 'oops', 'data': 'int' }
diff --git a/tests/qapi-schema/data-int.out b/tests/qapi-schema/data-int.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/data-int.out
diff --git a/tests/qapi-schema/data-member-array-bad.err b/tests/qapi-schema/data-member-array-bad.err
new file mode 100644
index 000000000..2c072d598
--- /dev/null
+++ b/tests/qapi-schema/data-member-array-bad.err
@@ -0,0 +1 @@
+tests/qapi-schema/data-member-array-bad.json:2: Member 'member' of 'data' for command 'oops': array type must contain single type name
diff --git a/tests/qapi-schema/data-member-array-bad.exit b/tests/qapi-schema/data-member-array-bad.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/data-member-array-bad.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/data-member-array-bad.json b/tests/qapi-schema/data-member-array-bad.json
new file mode 100644
index 000000000..b2ff144ec
--- /dev/null
+++ b/tests/qapi-schema/data-member-array-bad.json
@@ -0,0 +1,2 @@
+# we reject data if it does not contain a valid array type
+{ 'command': 'oops', 'data': { 'member': [ { 'nested': 'str' } ] } }
diff --git a/tests/qapi-schema/data-member-array-bad.out b/tests/qapi-schema/data-member-array-bad.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/data-member-array-bad.out
diff --git a/tests/qapi-schema/data-member-array.err b/tests/qapi-schema/data-member-array.err
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/data-member-array.err
diff --git a/tests/qapi-schema/data-member-array.exit b/tests/qapi-schema/data-member-array.exit
new file mode 100644
index 000000000..573541ac9
--- /dev/null
+++ b/tests/qapi-schema/data-member-array.exit
@@ -0,0 +1 @@
+0
diff --git a/tests/qapi-schema/data-member-array.json b/tests/qapi-schema/data-member-array.json
new file mode 100644
index 000000000..e6f7f5da1
--- /dev/null
+++ b/tests/qapi-schema/data-member-array.json
@@ -0,0 +1,4 @@
+# valid array members
+{ 'enum': 'abc', 'data': [ 'a', 'b', 'c' ] }
+{ 'struct': 'def', 'data': { 'array': [ 'abc' ] } }
+{ 'command': 'okay', 'data': { 'member1': [ 'int' ], 'member2': [ 'def' ] } }
diff --git a/tests/qapi-schema/data-member-array.out b/tests/qapi-schema/data-member-array.out
new file mode 100644
index 000000000..c39fa2548
--- /dev/null
+++ b/tests/qapi-schema/data-member-array.out
@@ -0,0 +1,5 @@
+[OrderedDict([('enum', 'abc'), ('data', ['a', 'b', 'c'])]),
+ OrderedDict([('struct', 'def'), ('data', OrderedDict([('array', ['abc'])]))]),
+ OrderedDict([('command', 'okay'), ('data', OrderedDict([('member1', ['int']), ('member2', ['def'])]))])]
+[{'enum_name': 'abc', 'enum_values': ['a', 'b', 'c']}]
+[OrderedDict([('struct', 'def'), ('data', OrderedDict([('array', ['abc'])]))])]
diff --git a/tests/qapi-schema/data-member-unknown.err b/tests/qapi-schema/data-member-unknown.err
new file mode 100644
index 000000000..ab905db80
--- /dev/null
+++ b/tests/qapi-schema/data-member-unknown.err
@@ -0,0 +1 @@
+tests/qapi-schema/data-member-unknown.json:2: Member 'member' of 'data' for command 'oops' uses unknown type 'NoSuchType'
diff --git a/tests/qapi-schema/data-member-unknown.exit b/tests/qapi-schema/data-member-unknown.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/data-member-unknown.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/data-member-unknown.json b/tests/qapi-schema/data-member-unknown.json
new file mode 100644
index 000000000..342a41ec9
--- /dev/null
+++ b/tests/qapi-schema/data-member-unknown.json
@@ -0,0 +1,2 @@
+# we reject data if it does not contain a known type
+{ 'command': 'oops', 'data': { 'member': 'NoSuchType' } }
diff --git a/tests/qapi-schema/data-member-unknown.out b/tests/qapi-schema/data-member-unknown.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/data-member-unknown.out
diff --git a/tests/qapi-schema/data-unknown.err b/tests/qapi-schema/data-unknown.err
new file mode 100644
index 000000000..5b07277a9
--- /dev/null
+++ b/tests/qapi-schema/data-unknown.err
@@ -0,0 +1 @@
+tests/qapi-schema/data-unknown.json:2: 'data' for command 'oops' uses unknown type 'NoSuchType'
diff --git a/tests/qapi-schema/data-unknown.exit b/tests/qapi-schema/data-unknown.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/data-unknown.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/data-unknown.json b/tests/qapi-schema/data-unknown.json
new file mode 100644
index 000000000..32aba43b3
--- /dev/null
+++ b/tests/qapi-schema/data-unknown.json
@@ -0,0 +1,2 @@
+# we reject data if it does not contain a known type
+{ 'command': 'oops', 'data': 'NoSuchType' }
diff --git a/tests/qapi-schema/data-unknown.out b/tests/qapi-schema/data-unknown.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/data-unknown.out
diff --git a/tests/qapi-schema/double-data.err b/tests/qapi-schema/double-data.err
new file mode 100644
index 000000000..cc765c4ff
--- /dev/null
+++ b/tests/qapi-schema/double-data.err
@@ -0,0 +1 @@
+tests/qapi-schema/double-data.json:2:41: Duplicate key "data"
diff --git a/tests/qapi-schema/double-data.exit b/tests/qapi-schema/double-data.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/double-data.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/double-data.json b/tests/qapi-schema/double-data.json
new file mode 100644
index 000000000..e76b51953
--- /dev/null
+++ b/tests/qapi-schema/double-data.json
@@ -0,0 +1,2 @@
+# we reject an expression with duplicate top-level keys
+{ 'struct': 'bar', 'data': { }, 'data': { 'string': 'str'} }
diff --git a/tests/qapi-schema/double-data.out b/tests/qapi-schema/double-data.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/double-data.out
diff --git a/tests/qapi-schema/double-type.err b/tests/qapi-schema/double-type.err
new file mode 100644
index 000000000..f9613c6d6
--- /dev/null
+++ b/tests/qapi-schema/double-type.err
@@ -0,0 +1 @@
+tests/qapi-schema/double-type.json:2: Unknown key 'command' in struct 'bar'
diff --git a/tests/qapi-schema/double-type.exit b/tests/qapi-schema/double-type.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/double-type.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/double-type.json b/tests/qapi-schema/double-type.json
new file mode 100644
index 000000000..911fa7af5
--- /dev/null
+++ b/tests/qapi-schema/double-type.json
@@ -0,0 +1,2 @@
+# we reject an expression with ambiguous metatype
+{ 'command': 'foo', 'struct': 'bar', 'data': { } }
diff --git a/tests/qapi-schema/double-type.out b/tests/qapi-schema/double-type.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/double-type.out
diff --git a/tests/qapi-schema/enum-bad-name.err b/tests/qapi-schema/enum-bad-name.err
new file mode 100644
index 000000000..9c3c1002b
--- /dev/null
+++ b/tests/qapi-schema/enum-bad-name.err
@@ -0,0 +1 @@
+tests/qapi-schema/enum-bad-name.json:2: Member of enum 'MyEnum' uses invalid name 'not^possible'
diff --git a/tests/qapi-schema/enum-bad-name.exit b/tests/qapi-schema/enum-bad-name.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/enum-bad-name.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/enum-bad-name.json b/tests/qapi-schema/enum-bad-name.json
new file mode 100644
index 000000000..8506562b3
--- /dev/null
+++ b/tests/qapi-schema/enum-bad-name.json
@@ -0,0 +1,2 @@
+# we ensure all enum names can map to C
+{ 'enum': 'MyEnum', 'data': [ 'not^possible' ] }
diff --git a/tests/qapi-schema/enum-bad-name.out b/tests/qapi-schema/enum-bad-name.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/enum-bad-name.out
diff --git a/tests/qapi-schema/enum-clash-member.err b/tests/qapi-schema/enum-clash-member.err
new file mode 100644
index 000000000..48bd1360e
--- /dev/null
+++ b/tests/qapi-schema/enum-clash-member.err
@@ -0,0 +1 @@
+tests/qapi-schema/enum-clash-member.json:2: Enum 'MyEnum' member 'ONE' clashes with 'one'
diff --git a/tests/qapi-schema/enum-clash-member.exit b/tests/qapi-schema/enum-clash-member.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/enum-clash-member.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/enum-clash-member.json b/tests/qapi-schema/enum-clash-member.json
new file mode 100644
index 000000000..b7dc02a28
--- /dev/null
+++ b/tests/qapi-schema/enum-clash-member.json
@@ -0,0 +1,2 @@
+# we reject enums where members will clash when mapped to C enum
+{ 'enum': 'MyEnum', 'data': [ 'one', 'ONE' ] }
diff --git a/tests/qapi-schema/enum-clash-member.out b/tests/qapi-schema/enum-clash-member.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/enum-clash-member.out
diff --git a/tests/qapi-schema/enum-dict-member.err b/tests/qapi-schema/enum-dict-member.err
new file mode 100644
index 000000000..8ca146ea5
--- /dev/null
+++ b/tests/qapi-schema/enum-dict-member.err
@@ -0,0 +1 @@
+tests/qapi-schema/enum-dict-member.json:2: Member of enum 'MyEnum' requires a string name
diff --git a/tests/qapi-schema/enum-dict-member.exit b/tests/qapi-schema/enum-dict-member.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/enum-dict-member.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/enum-dict-member.json b/tests/qapi-schema/enum-dict-member.json
new file mode 100644
index 000000000..79672e0f0
--- /dev/null
+++ b/tests/qapi-schema/enum-dict-member.json
@@ -0,0 +1,2 @@
+# we reject any enum member that is not a string
+{ 'enum': 'MyEnum', 'data': [ { 'value': 'str' } ] }
diff --git a/tests/qapi-schema/enum-dict-member.out b/tests/qapi-schema/enum-dict-member.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/enum-dict-member.out
diff --git a/tests/qapi-schema/enum-empty.err b/tests/qapi-schema/enum-empty.err
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/enum-empty.err
diff --git a/tests/qapi-schema/enum-empty.exit b/tests/qapi-schema/enum-empty.exit
new file mode 100644
index 000000000..573541ac9
--- /dev/null
+++ b/tests/qapi-schema/enum-empty.exit
@@ -0,0 +1 @@
+0
diff --git a/tests/qapi-schema/enum-empty.json b/tests/qapi-schema/enum-empty.json
new file mode 100644
index 000000000..40d4e85a2
--- /dev/null
+++ b/tests/qapi-schema/enum-empty.json
@@ -0,0 +1,2 @@
+# An empty enum, although unusual, is currently acceptable
+{ 'enum': 'MyEnum', 'data': [ ] }
diff --git a/tests/qapi-schema/enum-empty.out b/tests/qapi-schema/enum-empty.out
new file mode 100644
index 000000000..3b75c1613
--- /dev/null
+++ b/tests/qapi-schema/enum-empty.out
@@ -0,0 +1,3 @@
+[OrderedDict([('enum', 'MyEnum'), ('data', [])])]
+[{'enum_name': 'MyEnum', 'enum_values': []}]
+[]
diff --git a/tests/qapi-schema/enum-int-member.err b/tests/qapi-schema/enum-int-member.err
new file mode 100644
index 000000000..071c5213d
--- /dev/null
+++ b/tests/qapi-schema/enum-int-member.err
@@ -0,0 +1 @@
+tests/qapi-schema/enum-int-member.json:3:31: Stray "1"
diff --git a/tests/qapi-schema/enum-int-member.exit b/tests/qapi-schema/enum-int-member.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/enum-int-member.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/enum-int-member.json b/tests/qapi-schema/enum-int-member.json
new file mode 100644
index 000000000..6c9c32e14
--- /dev/null
+++ b/tests/qapi-schema/enum-int-member.json
@@ -0,0 +1,3 @@
+# we reject any enum member that is not a string
+# FIXME: once the parser understands integer inputs, improve the error message
+{ 'enum': 'MyEnum', 'data': [ 1 ] }
diff --git a/tests/qapi-schema/enum-int-member.out b/tests/qapi-schema/enum-int-member.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/enum-int-member.out
diff --git a/tests/qapi-schema/enum-max-member.err b/tests/qapi-schema/enum-max-member.err
new file mode 100644
index 000000000..f77837fb4
--- /dev/null
+++ b/tests/qapi-schema/enum-max-member.err
@@ -0,0 +1 @@
+tests/qapi-schema/enum-max-member.json:3: Enum 'MyEnum' member 'max' clashes with '(automatic)'
diff --git a/tests/qapi-schema/enum-max-member.exit b/tests/qapi-schema/enum-max-member.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/enum-max-member.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/enum-max-member.json b/tests/qapi-schema/enum-max-member.json
new file mode 100644
index 000000000..4bcda0bf0
--- /dev/null
+++ b/tests/qapi-schema/enum-max-member.json
@@ -0,0 +1,3 @@
+# we reject user-supplied 'max' for clashing with implicit enum end
+# TODO: should we instead munge the implicit value to avoid the clash?
+{ 'enum': 'MyEnum', 'data': [ 'max' ] }
diff --git a/tests/qapi-schema/enum-max-member.out b/tests/qapi-schema/enum-max-member.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/enum-max-member.out
diff --git a/tests/qapi-schema/enum-missing-data.err b/tests/qapi-schema/enum-missing-data.err
new file mode 100644
index 000000000..ba4873ae6
--- /dev/null
+++ b/tests/qapi-schema/enum-missing-data.err
@@ -0,0 +1 @@
+tests/qapi-schema/enum-missing-data.json:2: Key 'data' is missing from enum 'MyEnum'
diff --git a/tests/qapi-schema/enum-missing-data.exit b/tests/qapi-schema/enum-missing-data.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/enum-missing-data.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/enum-missing-data.json b/tests/qapi-schema/enum-missing-data.json
new file mode 100644
index 000000000..558fd35e9
--- /dev/null
+++ b/tests/qapi-schema/enum-missing-data.json
@@ -0,0 +1,2 @@
+# we require that all QAPI enums have a data array
+{ 'enum': 'MyEnum' }
diff --git a/tests/qapi-schema/enum-missing-data.out b/tests/qapi-schema/enum-missing-data.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/enum-missing-data.out
diff --git a/tests/qapi-schema/enum-union-clash.err b/tests/qapi-schema/enum-union-clash.err
new file mode 100644
index 000000000..c04e1a806
--- /dev/null
+++ b/tests/qapi-schema/enum-union-clash.err
@@ -0,0 +1 @@
+tests/qapi-schema/enum-union-clash.json:2: enum 'UnionKind' should not end in 'Kind'
diff --git a/tests/qapi-schema/enum-union-clash.exit b/tests/qapi-schema/enum-union-clash.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/enum-union-clash.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/enum-union-clash.json b/tests/qapi-schema/enum-union-clash.json
new file mode 100644
index 000000000..593282b6c
--- /dev/null
+++ b/tests/qapi-schema/enum-union-clash.json
@@ -0,0 +1,4 @@
+# we reject types that would conflict with implicit union enum
+{ 'enum': 'UnionKind', 'data': [ 'oops' ] }
+{ 'union': 'Union',
+ 'data': { 'a': 'int' } }
diff --git a/tests/qapi-schema/enum-union-clash.out b/tests/qapi-schema/enum-union-clash.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/enum-union-clash.out
diff --git a/tests/qapi-schema/enum-wrong-data.err b/tests/qapi-schema/enum-wrong-data.err
new file mode 100644
index 000000000..11b43471c
--- /dev/null
+++ b/tests/qapi-schema/enum-wrong-data.err
@@ -0,0 +1 @@
+tests/qapi-schema/enum-wrong-data.json:2: Enum 'MyEnum' requires an array for 'data'
diff --git a/tests/qapi-schema/enum-wrong-data.exit b/tests/qapi-schema/enum-wrong-data.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/enum-wrong-data.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/enum-wrong-data.json b/tests/qapi-schema/enum-wrong-data.json
new file mode 100644
index 000000000..7b3e255c1
--- /dev/null
+++ b/tests/qapi-schema/enum-wrong-data.json
@@ -0,0 +1,2 @@
+# we require that all qapi enums have an array for data
+{ 'enum': 'MyEnum', 'data': { 'value': 'str' } }
diff --git a/tests/qapi-schema/enum-wrong-data.out b/tests/qapi-schema/enum-wrong-data.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/enum-wrong-data.out
diff --git a/tests/qapi-schema/escape-outside-string.err b/tests/qapi-schema/escape-outside-string.err
new file mode 100644
index 000000000..b9b8837fd
--- /dev/null
+++ b/tests/qapi-schema/escape-outside-string.err
@@ -0,0 +1 @@
+tests/qapi-schema/escape-outside-string.json:3:27: Stray "\"
diff --git a/tests/qapi-schema/escape-outside-string.exit b/tests/qapi-schema/escape-outside-string.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/escape-outside-string.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/escape-outside-string.json b/tests/qapi-schema/escape-outside-string.json
new file mode 100644
index 000000000..482f79554
--- /dev/null
+++ b/tests/qapi-schema/escape-outside-string.json
@@ -0,0 +1,3 @@
+# escape sequences are permitted only inside strings
+# { 'command': 'foo', 'data': {} }
+{ 'command': 'foo', 'data'\u003a{} }
diff --git a/tests/qapi-schema/escape-outside-string.out b/tests/qapi-schema/escape-outside-string.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/escape-outside-string.out
diff --git a/tests/qapi-schema/escape-too-big.err b/tests/qapi-schema/escape-too-big.err
new file mode 100644
index 000000000..d9aeb5dc3
--- /dev/null
+++ b/tests/qapi-schema/escape-too-big.err
@@ -0,0 +1 @@
+tests/qapi-schema/escape-too-big.json:3:14: For now, \u escape only supports non-zero values up to \u007f
diff --git a/tests/qapi-schema/escape-too-big.exit b/tests/qapi-schema/escape-too-big.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/escape-too-big.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/escape-too-big.json b/tests/qapi-schema/escape-too-big.json
new file mode 100644
index 000000000..62bcecd55
--- /dev/null
+++ b/tests/qapi-schema/escape-too-big.json
@@ -0,0 +1,3 @@
+# we don't support full Unicode strings, yet
+# { 'command': 'é' }
+{ 'command': '\u00e9' }
diff --git a/tests/qapi-schema/escape-too-big.out b/tests/qapi-schema/escape-too-big.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/escape-too-big.out
diff --git a/tests/qapi-schema/escape-too-short.err b/tests/qapi-schema/escape-too-short.err
new file mode 100644
index 000000000..934de598e
--- /dev/null
+++ b/tests/qapi-schema/escape-too-short.err
@@ -0,0 +1 @@
+tests/qapi-schema/escape-too-short.json:3:14: \u escape needs 4 hex digits
diff --git a/tests/qapi-schema/escape-too-short.exit b/tests/qapi-schema/escape-too-short.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/escape-too-short.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/escape-too-short.json b/tests/qapi-schema/escape-too-short.json
new file mode 100644
index 000000000..6cb1dec8f
--- /dev/null
+++ b/tests/qapi-schema/escape-too-short.json
@@ -0,0 +1,3 @@
+# the \u escape requires 4 hex digits
+# { 'command': 'a' }
+{ 'command': '\u61' }
diff --git a/tests/qapi-schema/escape-too-short.out b/tests/qapi-schema/escape-too-short.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/escape-too-short.out
diff --git a/tests/qapi-schema/event-case.err b/tests/qapi-schema/event-case.err
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/event-case.err
diff --git a/tests/qapi-schema/event-case.exit b/tests/qapi-schema/event-case.exit
new file mode 100644
index 000000000..573541ac9
--- /dev/null
+++ b/tests/qapi-schema/event-case.exit
@@ -0,0 +1 @@
+0
diff --git a/tests/qapi-schema/event-case.json b/tests/qapi-schema/event-case.json
new file mode 100644
index 000000000..3a92d8b61
--- /dev/null
+++ b/tests/qapi-schema/event-case.json
@@ -0,0 +1,3 @@
+# TODO: might be nice to enforce naming conventions; but until then this works
+# even though events should usually be ALL_CAPS
+{ 'event': 'oops' }
diff --git a/tests/qapi-schema/event-case.out b/tests/qapi-schema/event-case.out
new file mode 100644
index 000000000..3764bc781
--- /dev/null
+++ b/tests/qapi-schema/event-case.out
@@ -0,0 +1,3 @@
+[OrderedDict([('event', 'oops')])]
+[]
+[]
diff --git a/tests/qapi-schema/event-max.err b/tests/qapi-schema/event-max.err
new file mode 100644
index 000000000..c85653437
--- /dev/null
+++ b/tests/qapi-schema/event-max.err
@@ -0,0 +1 @@
+tests/qapi-schema/event-max.json:2: Event name 'MAX' cannot be created
diff --git a/tests/qapi-schema/event-max.exit b/tests/qapi-schema/event-max.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/event-max.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/event-max.json b/tests/qapi-schema/event-max.json
new file mode 100644
index 000000000..f3d7de2a3
--- /dev/null
+++ b/tests/qapi-schema/event-max.json
@@ -0,0 +1,2 @@
+# an event named 'MAX' would conflict with implicit C enum
+{ 'event': 'MAX' }
diff --git a/tests/qapi-schema/event-max.out b/tests/qapi-schema/event-max.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/event-max.out
diff --git a/tests/qapi-schema/event-nest-struct.err b/tests/qapi-schema/event-nest-struct.err
index 91bde1c96..5a42701b8 100644
--- a/tests/qapi-schema/event-nest-struct.err
+++ b/tests/qapi-schema/event-nest-struct.err
@@ -1 +1 @@
-tests/qapi-schema/event-nest-struct.json:1: Nested structure define in event is not supported, event 'EVENT_A', argname 'a'
+tests/qapi-schema/event-nest-struct.json:1: Member 'a' of 'data' for event 'EVENT_A' should be a type name
diff --git a/tests/qapi-schema/flat-union-array-branch.err b/tests/qapi-schema/flat-union-array-branch.err
new file mode 100644
index 000000000..8ea91eadb
--- /dev/null
+++ b/tests/qapi-schema/flat-union-array-branch.err
@@ -0,0 +1 @@
+tests/qapi-schema/flat-union-array-branch.json:8: Member 'value1' of union 'TestUnion' cannot be an array
diff --git a/tests/qapi-schema/flat-union-array-branch.exit b/tests/qapi-schema/flat-union-array-branch.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/flat-union-array-branch.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/flat-union-array-branch.json b/tests/qapi-schema/flat-union-array-branch.json
new file mode 100644
index 000000000..0b98820a8
--- /dev/null
+++ b/tests/qapi-schema/flat-union-array-branch.json
@@ -0,0 +1,12 @@
+# we require flat union branches to be a struct
+{ 'enum': 'TestEnum',
+ 'data': [ 'value1', 'value2' ] }
+{ 'struct': 'Base',
+ 'data': { 'enum1': 'TestEnum' } }
+{ 'struct': 'TestTypeB',
+ 'data': { 'integer': 'int' } }
+{ 'union': 'TestUnion',
+ 'base': 'Base',
+ 'discriminator': 'enum1',
+ 'data': { 'value1': ['TestTypeB'],
+ 'value2': 'TestTypeB' } }
diff --git a/tests/qapi-schema/flat-union-array-branch.out b/tests/qapi-schema/flat-union-array-branch.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/flat-union-array-branch.out
diff --git a/tests/qapi-schema/flat-union-bad-base.err b/tests/qapi-schema/flat-union-bad-base.err
new file mode 100644
index 000000000..f9c31b2bf
--- /dev/null
+++ b/tests/qapi-schema/flat-union-bad-base.err
@@ -0,0 +1 @@
+tests/qapi-schema/flat-union-bad-base.json:9: Flat union 'TestUnion' must have a string base field
diff --git a/tests/qapi-schema/flat-union-bad-base.exit b/tests/qapi-schema/flat-union-bad-base.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/flat-union-bad-base.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/flat-union-bad-base.json b/tests/qapi-schema/flat-union-bad-base.json
new file mode 100644
index 000000000..e2e622bb6
--- /dev/null
+++ b/tests/qapi-schema/flat-union-bad-base.json
@@ -0,0 +1,13 @@
+# we require the base to be an existing struct
+# TODO: should we allow an anonymous inline base type?
+{ 'enum': 'TestEnum',
+ 'data': [ 'value1', 'value2' ] }
+{ 'struct': 'TestTypeA',
+ 'data': { 'string': 'str' } }
+{ 'struct': 'TestTypeB',
+ 'data': { 'integer': 'int' } }
+{ 'union': 'TestUnion',
+ 'base': { 'enum1': 'TestEnum', 'kind': 'str' },
+ 'discriminator': 'enum1',
+ 'data': { 'value1': 'TestTypeA',
+ 'value2': 'TestTypeB' } }
diff --git a/tests/qapi-schema/flat-union-bad-base.out b/tests/qapi-schema/flat-union-bad-base.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/flat-union-bad-base.out
diff --git a/tests/qapi-schema/flat-union-bad-discriminator.err b/tests/qapi-schema/flat-union-bad-discriminator.err
new file mode 100644
index 000000000..c38cc8e4d
--- /dev/null
+++ b/tests/qapi-schema/flat-union-bad-discriminator.err
@@ -0,0 +1 @@
+tests/qapi-schema/flat-union-bad-discriminator.json:11: Discriminator of flat union 'TestUnion' requires a string name
diff --git a/tests/qapi-schema/flat-union-bad-discriminator.exit b/tests/qapi-schema/flat-union-bad-discriminator.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/flat-union-bad-discriminator.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/flat-union-bad-discriminator.json b/tests/qapi-schema/flat-union-bad-discriminator.json
new file mode 100644
index 000000000..cd10b9d90
--- /dev/null
+++ b/tests/qapi-schema/flat-union-bad-discriminator.json
@@ -0,0 +1,15 @@
+# we require the discriminator to be a string naming a base-type member
+# this tests the old syntax for anonymous unions before we added alternates
+{ 'enum': 'TestEnum',
+ 'data': [ 'value1', 'value2' ] }
+{ 'struct': 'TestBase',
+ 'data': { 'enum1': 'TestEnum', 'kind': 'str' } }
+{ 'struct': 'TestTypeA',
+ 'data': { 'string': 'str' } }
+{ 'struct': 'TestTypeB',
+ 'data': { 'integer': 'int' } }
+{ 'union': 'TestUnion',
+ 'base': 'TestBase',
+ 'discriminator': {},
+ 'data': { 'kind1': 'TestTypeA',
+ 'kind2': 'TestTypeB' } }
diff --git a/tests/qapi-schema/flat-union-bad-discriminator.out b/tests/qapi-schema/flat-union-bad-discriminator.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/flat-union-bad-discriminator.out
diff --git a/tests/qapi-schema/flat-union-base-star.err b/tests/qapi-schema/flat-union-base-star.err
new file mode 100644
index 000000000..b7748f08b
--- /dev/null
+++ b/tests/qapi-schema/flat-union-base-star.err
@@ -0,0 +1 @@
+tests/qapi-schema/flat-union-base-star.json:8: Base '**' is not a valid struct
diff --git a/tests/qapi-schema/flat-union-base-star.exit b/tests/qapi-schema/flat-union-base-star.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/flat-union-base-star.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/flat-union-base-star.json b/tests/qapi-schema/flat-union-base-star.json
new file mode 100644
index 000000000..5099439a9
--- /dev/null
+++ b/tests/qapi-schema/flat-union-base-star.json
@@ -0,0 +1,12 @@
+# we require the base to be an existing struct
+{ 'enum': 'TestEnum',
+ 'data': [ 'value1', 'value2' ] }
+{ 'struct': 'TestTypeA',
+ 'data': { 'string': 'str' } }
+{ 'struct': 'TestTypeB',
+ 'data': { 'integer': 'int' } }
+{ 'union': 'TestUnion',
+ 'base': '**',
+ 'discriminator': 'enum1',
+ 'data': { 'value1': 'TestTypeA',
+ 'value2': 'TestTypeB' } }
diff --git a/tests/qapi-schema/flat-union-base-star.out b/tests/qapi-schema/flat-union-base-star.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/flat-union-base-star.out
diff --git a/tests/qapi-schema/flat-union-base-union.err b/tests/qapi-schema/flat-union-base-union.err
new file mode 100644
index 000000000..ede9859a3
--- /dev/null
+++ b/tests/qapi-schema/flat-union-base-union.err
@@ -0,0 +1 @@
+tests/qapi-schema/flat-union-base-union.json:11: Base 'UnionBase' is not a valid struct
diff --git a/tests/qapi-schema/flat-union-base-union.exit b/tests/qapi-schema/flat-union-base-union.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/flat-union-base-union.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/flat-union-base-union.json b/tests/qapi-schema/flat-union-base-union.json
new file mode 100644
index 000000000..6a8ea687a
--- /dev/null
+++ b/tests/qapi-schema/flat-union-base-union.json
@@ -0,0 +1,15 @@
+# we require the base to be a struct
+{ 'enum': 'TestEnum',
+ 'data': [ 'value1', 'value2' ] }
+{ 'struct': 'TestTypeA',
+ 'data': { 'string': 'str' } }
+{ 'struct': 'TestTypeB',
+ 'data': { 'integer': 'int' } }
+{ 'union': 'UnionBase',
+ 'data': { 'kind1': 'TestTypeA',
+ 'kind2': 'TestTypeB' } }
+{ 'union': 'TestUnion',
+ 'base': 'UnionBase',
+ 'discriminator': 'type',
+ 'data': { 'kind1': 'TestTypeA',
+ 'kind2': 'TestTypeB' } }
diff --git a/tests/qapi-schema/flat-union-base-union.out b/tests/qapi-schema/flat-union-base-union.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/flat-union-base-union.out
diff --git a/tests/qapi-schema/flat-union-branch-clash.err b/tests/qapi-schema/flat-union-branch-clash.err
new file mode 100644
index 000000000..f11276688
--- /dev/null
+++ b/tests/qapi-schema/flat-union-branch-clash.err
@@ -0,0 +1 @@
+tests/qapi-schema/flat-union-branch-clash.json:10: Member name 'name' of branch 'value1' clashes with base 'Base'
diff --git a/tests/qapi-schema/flat-union-branch-clash.exit b/tests/qapi-schema/flat-union-branch-clash.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/flat-union-branch-clash.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/flat-union-branch-clash.json b/tests/qapi-schema/flat-union-branch-clash.json
new file mode 100644
index 000000000..8fb054f00
--- /dev/null
+++ b/tests/qapi-schema/flat-union-branch-clash.json
@@ -0,0 +1,14 @@
+# we check for no duplicate keys between branches and base
+{ 'enum': 'TestEnum',
+ 'data': [ 'value1', 'value2' ] }
+{ 'struct': 'Base',
+ 'data': { 'enum1': 'TestEnum', '*name': 'str' } }
+{ 'struct': 'Branch1',
+ 'data': { 'name': 'str' } }
+{ 'struct': 'Branch2',
+ 'data': { 'value': 'int' } }
+{ 'union': 'TestUnion',
+ 'base': 'Base',
+ 'discriminator': 'enum1',
+ 'data': { 'value1': 'Branch1',
+ 'value2': 'Branch2' } }
diff --git a/tests/qapi-schema/flat-union-branch-clash.out b/tests/qapi-schema/flat-union-branch-clash.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/flat-union-branch-clash.out
diff --git a/tests/qapi-schema/flat-union-inline.err b/tests/qapi-schema/flat-union-inline.err
new file mode 100644
index 000000000..ec586277b
--- /dev/null
+++ b/tests/qapi-schema/flat-union-inline.err
@@ -0,0 +1 @@
+tests/qapi-schema/flat-union-inline.json:7: Flat union 'TestUnion' must have a string base field
diff --git a/tests/qapi-schema/flat-union-inline.exit b/tests/qapi-schema/flat-union-inline.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/flat-union-inline.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/flat-union-inline.json b/tests/qapi-schema/flat-union-inline.json
new file mode 100644
index 000000000..6bfdd6581
--- /dev/null
+++ b/tests/qapi-schema/flat-union-inline.json
@@ -0,0 +1,11 @@
+# we require branches to be a struct name
+# TODO: should we allow anonymous inline types?
+{ 'enum': 'TestEnum',
+ 'data': [ 'value1', 'value2' ] }
+{ 'struct': 'Base',
+ 'data': { 'enum1': 'TestEnum', 'kind': 'str' } }
+{ 'union': 'TestUnion',
+ 'base': { 'enum1': 'TestEnum', 'kind': 'str' },
+ 'discriminator': 'enum1',
+ 'data': { 'value1': { 'string': 'str' },
+ 'value2': { 'integer': 'int' } } }
diff --git a/tests/qapi-schema/flat-union-inline.out b/tests/qapi-schema/flat-union-inline.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/flat-union-inline.out
diff --git a/tests/qapi-schema/flat-union-int-branch.err b/tests/qapi-schema/flat-union-int-branch.err
new file mode 100644
index 000000000..faf01573b
--- /dev/null
+++ b/tests/qapi-schema/flat-union-int-branch.err
@@ -0,0 +1 @@
+tests/qapi-schema/flat-union-int-branch.json:8: Member 'value1' of union 'TestUnion' cannot use built-in type 'int'
diff --git a/tests/qapi-schema/flat-union-int-branch.exit b/tests/qapi-schema/flat-union-int-branch.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/flat-union-int-branch.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/flat-union-int-branch.json b/tests/qapi-schema/flat-union-int-branch.json
new file mode 100644
index 000000000..9370c349e
--- /dev/null
+++ b/tests/qapi-schema/flat-union-int-branch.json
@@ -0,0 +1,12 @@
+# we require flat union branches to be a struct
+{ 'enum': 'TestEnum',
+ 'data': [ 'value1', 'value2' ] }
+{ 'struct': 'Base',
+ 'data': { 'enum1': 'TestEnum' } }
+{ 'struct': 'TestTypeB',
+ 'data': { 'integer': 'int' } }
+{ 'union': 'TestUnion',
+ 'base': 'Base',
+ 'discriminator': 'enum1',
+ 'data': { 'value1': 'int',
+ 'value2': 'TestTypeB' } }
diff --git a/tests/qapi-schema/flat-union-int-branch.out b/tests/qapi-schema/flat-union-int-branch.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/flat-union-int-branch.out
diff --git a/tests/qapi-schema/flat-union-invalid-branch-key.json b/tests/qapi-schema/flat-union-invalid-branch-key.json
index a6242823e..95ff7746b 100644
--- a/tests/qapi-schema/flat-union-invalid-branch-key.json
+++ b/tests/qapi-schema/flat-union-invalid-branch-key.json
@@ -1,13 +1,13 @@
{ 'enum': 'TestEnum',
'data': [ 'value1', 'value2' ] }
-{ 'type': 'TestBase',
+{ 'struct': 'TestBase',
'data': { 'enum1': 'TestEnum' } }
-{ 'type': 'TestTypeA',
+{ 'struct': 'TestTypeA',
'data': { 'string': 'str' } }
-{ 'type': 'TestTypeB',
+{ 'struct': 'TestTypeB',
'data': { 'integer': 'int' } }
{ 'union': 'TestUnion',
diff --git a/tests/qapi-schema/flat-union-invalid-discriminator.err b/tests/qapi-schema/flat-union-invalid-discriminator.err
index 790b6759b..5f4055614 100644
--- a/tests/qapi-schema/flat-union-invalid-discriminator.err
+++ b/tests/qapi-schema/flat-union-invalid-discriminator.err
@@ -1 +1 @@
-tests/qapi-schema/flat-union-invalid-discriminator.json:13: Discriminator 'enum_wrong' is not a member of base type 'TestBase'
+tests/qapi-schema/flat-union-invalid-discriminator.json:13: Discriminator 'enum_wrong' is not a member of base struct 'TestBase'
diff --git a/tests/qapi-schema/flat-union-invalid-discriminator.json b/tests/qapi-schema/flat-union-invalid-discriminator.json
index 887157e17..48b94c3a4 100644
--- a/tests/qapi-schema/flat-union-invalid-discriminator.json
+++ b/tests/qapi-schema/flat-union-invalid-discriminator.json
@@ -1,13 +1,13 @@
{ 'enum': 'TestEnum',
'data': [ 'value1', 'value2' ] }
-{ 'type': 'TestBase',
+{ 'struct': 'TestBase',
'data': { 'enum1': 'TestEnum' } }
-{ 'type': 'TestTypeA',
+{ 'struct': 'TestTypeA',
'data': { 'string': 'str' } }
-{ 'type': 'TestTypeB',
+{ 'struct': 'TestTypeB',
'data': { 'integer': 'int' } }
{ 'union': 'TestUnion',
diff --git a/tests/qapi-schema/flat-union-no-base.err b/tests/qapi-schema/flat-union-no-base.err
index a59749eb8..bb3f70874 100644
--- a/tests/qapi-schema/flat-union-no-base.err
+++ b/tests/qapi-schema/flat-union-no-base.err
@@ -1 +1 @@
-tests/qapi-schema/flat-union-no-base.json:7: Flat union 'TestUnion' must have a base field
+tests/qapi-schema/flat-union-no-base.json:9: Flat union 'TestUnion' must have a string base field
diff --git a/tests/qapi-schema/flat-union-no-base.json b/tests/qapi-schema/flat-union-no-base.json
index 50f267323..ffc4c6f0e 100644
--- a/tests/qapi-schema/flat-union-no-base.json
+++ b/tests/qapi-schema/flat-union-no-base.json
@@ -1,10 +1,12 @@
-{ 'type': 'TestTypeA',
+# flat unions require a base
+# TODO: simple unions should be able to use an enum discriminator
+{ 'struct': 'TestTypeA',
'data': { 'string': 'str' } }
-
-{ 'type': 'TestTypeB',
+{ 'struct': 'TestTypeB',
'data': { 'integer': 'int' } }
-
+{ 'enum': 'Enum',
+ 'data': [ 'value1', 'value2' ] }
{ 'union': 'TestUnion',
- 'discriminator': 'enum1',
+ 'discriminator': 'Enum',
'data': { 'value1': 'TestTypeA',
'value2': 'TestTypeB' } }
diff --git a/tests/qapi-schema/flat-union-optional-discriminator.err b/tests/qapi-schema/flat-union-optional-discriminator.err
new file mode 100644
index 000000000..aaabedb3b
--- /dev/null
+++ b/tests/qapi-schema/flat-union-optional-discriminator.err
@@ -0,0 +1 @@
+tests/qapi-schema/flat-union-optional-discriminator.json:6: Discriminator of flat union 'MyUnion' does not allow optional name '*switch'
diff --git a/tests/qapi-schema/flat-union-optional-discriminator.exit b/tests/qapi-schema/flat-union-optional-discriminator.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/flat-union-optional-discriminator.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/flat-union-optional-discriminator.json b/tests/qapi-schema/flat-union-optional-discriminator.json
new file mode 100644
index 000000000..08a8f7ef8
--- /dev/null
+++ b/tests/qapi-schema/flat-union-optional-discriminator.json
@@ -0,0 +1,10 @@
+# we require the discriminator to be non-optional
+{ 'enum': 'Enum', 'data': [ 'one', 'two' ] }
+{ 'struct': 'Base',
+ 'data': { '*switch': 'Enum' } }
+{ 'struct': 'Branch', 'data': { 'name': 'str' } }
+{ 'union': 'MyUnion',
+ 'base': 'Base',
+ 'discriminator': '*switch',
+ 'data': { 'one': 'Branch',
+ 'two': 'Branch' } }
diff --git a/tests/qapi-schema/flat-union-optional-discriminator.out b/tests/qapi-schema/flat-union-optional-discriminator.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/flat-union-optional-discriminator.out
diff --git a/tests/qapi-schema/flat-union-reverse-define.json b/tests/qapi-schema/flat-union-reverse-define.json
index 9ea7e7220..648bbfe2b 100644
--- a/tests/qapi-schema/flat-union-reverse-define.json
+++ b/tests/qapi-schema/flat-union-reverse-define.json
@@ -4,14 +4,14 @@
'data': { 'value1': 'TestTypeA',
'value2': 'TestTypeB' } }
-{ 'type': 'TestBase',
+{ 'struct': 'TestBase',
'data': { 'enum1': 'TestEnum' } }
{ 'enum': 'TestEnum',
'data': [ 'value1', 'value2' ] }
-{ 'type': 'TestTypeA',
+{ 'struct': 'TestTypeA',
'data': { 'string': 'str' } }
-{ 'type': 'TestTypeB',
+{ 'struct': 'TestTypeB',
'data': { 'integer': 'int' } }
diff --git a/tests/qapi-schema/flat-union-reverse-define.out b/tests/qapi-schema/flat-union-reverse-define.out
index 03c952e28..1ed7b8a51 100644
--- a/tests/qapi-schema/flat-union-reverse-define.out
+++ b/tests/qapi-schema/flat-union-reverse-define.out
@@ -1,9 +1,9 @@
[OrderedDict([('union', 'TestUnion'), ('base', 'TestBase'), ('discriminator', 'enum1'), ('data', OrderedDict([('value1', 'TestTypeA'), ('value2', 'TestTypeB')]))]),
- OrderedDict([('type', 'TestBase'), ('data', OrderedDict([('enum1', 'TestEnum')]))]),
+ OrderedDict([('struct', 'TestBase'), ('data', OrderedDict([('enum1', 'TestEnum')]))]),
OrderedDict([('enum', 'TestEnum'), ('data', ['value1', 'value2'])]),
- OrderedDict([('type', 'TestTypeA'), ('data', OrderedDict([('string', 'str')]))]),
- OrderedDict([('type', 'TestTypeB'), ('data', OrderedDict([('integer', 'int')]))])]
+ OrderedDict([('struct', 'TestTypeA'), ('data', OrderedDict([('string', 'str')]))]),
+ OrderedDict([('struct', 'TestTypeB'), ('data', OrderedDict([('integer', 'int')]))])]
[{'enum_name': 'TestEnum', 'enum_values': ['value1', 'value2']}]
-[OrderedDict([('type', 'TestBase'), ('data', OrderedDict([('enum1', 'TestEnum')]))]),
- OrderedDict([('type', 'TestTypeA'), ('data', OrderedDict([('string', 'str')]))]),
- OrderedDict([('type', 'TestTypeB'), ('data', OrderedDict([('integer', 'int')]))])]
+[OrderedDict([('struct', 'TestBase'), ('data', OrderedDict([('enum1', 'TestEnum')]))]),
+ OrderedDict([('struct', 'TestTypeA'), ('data', OrderedDict([('string', 'str')]))]),
+ OrderedDict([('struct', 'TestTypeB'), ('data', OrderedDict([('integer', 'int')]))])]
diff --git a/tests/qapi-schema/flat-union-string-discriminator.json b/tests/qapi-schema/flat-union-string-discriminator.json
index e966aeb39..8af60333b 100644
--- a/tests/qapi-schema/flat-union-string-discriminator.json
+++ b/tests/qapi-schema/flat-union-string-discriminator.json
@@ -1,13 +1,13 @@
{ 'enum': 'TestEnum',
'data': [ 'value1', 'value2' ] }
-{ 'type': 'TestBase',
+{ 'struct': 'TestBase',
'data': { 'enum1': 'TestEnum', 'kind': 'str' } }
-{ 'type': 'TestTypeA',
+{ 'struct': 'TestTypeA',
'data': { 'string': 'str' } }
-{ 'type': 'TestTypeB',
+{ 'struct': 'TestTypeB',
'data': { 'integer': 'int' } }
{ 'union': 'TestUnion',
diff --git a/tests/qapi-schema/ident-with-escape.err b/tests/qapi-schema/ident-with-escape.err
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/ident-with-escape.err
diff --git a/tests/qapi-schema/ident-with-escape.exit b/tests/qapi-schema/ident-with-escape.exit
new file mode 100644
index 000000000..573541ac9
--- /dev/null
+++ b/tests/qapi-schema/ident-with-escape.exit
@@ -0,0 +1 @@
+0
diff --git a/tests/qapi-schema/ident-with-escape.json b/tests/qapi-schema/ident-with-escape.json
new file mode 100644
index 000000000..56617501e
--- /dev/null
+++ b/tests/qapi-schema/ident-with-escape.json
@@ -0,0 +1,4 @@
+# we allow escape sequences in strings, if they map back to ASCII
+# { 'command': 'fooA', 'data': { 'bar1': 'str' } }
+{ 'c\u006fmmand': '\u0066\u006f\u006FA',
+ 'd\u0061ta': { '\u0062\u0061\u00721': '\u0073\u0074\u0072' } }
diff --git a/tests/qapi-schema/ident-with-escape.out b/tests/qapi-schema/ident-with-escape.out
new file mode 100644
index 000000000..402843081
--- /dev/null
+++ b/tests/qapi-schema/ident-with-escape.out
@@ -0,0 +1,3 @@
+[OrderedDict([('command', 'fooA'), ('data', OrderedDict([('bar1', 'str')]))])]
+[]
+[]
diff --git a/tests/qapi-schema/include-cycle.err b/tests/qapi-schema/include-cycle.err
index 602cf6232..bdcd07dce 100644
--- a/tests/qapi-schema/include-cycle.err
+++ b/tests/qapi-schema/include-cycle.err
@@ -1,3 +1,3 @@
In file included from tests/qapi-schema/include-cycle.json:1:
-In file included from include-cycle-b.json:1:
-include-cycle-c.json:1: Inclusion loop for include-cycle.json
+In file included from tests/qapi-schema/include-cycle-b.json:1:
+tests/qapi-schema/include-cycle-c.json:1: Inclusion loop for include-cycle.json
diff --git a/tests/qapi-schema/include-nested-err.err b/tests/qapi-schema/include-nested-err.err
index 1dacbda3b..1b7b22706 100644
--- a/tests/qapi-schema/include-nested-err.err
+++ b/tests/qapi-schema/include-nested-err.err
@@ -1,2 +1,2 @@
In file included from tests/qapi-schema/include-nested-err.json:1:
-missing-colon.json:1:10: Expected ":"
+tests/qapi-schema/missing-colon.json:1:10: Expected ":"
diff --git a/tests/qapi-schema/indented-expr.json b/tests/qapi-schema/indented-expr.json
index d80af6056..7115d3131 100644
--- a/tests/qapi-schema/indented-expr.json
+++ b/tests/qapi-schema/indented-expr.json
@@ -1,2 +1,2 @@
-{ 'id' : 'eins' }
- { 'id' : 'zwei' }
+{ 'command' : 'eins' }
+ { 'command' : 'zwei' }
diff --git a/tests/qapi-schema/indented-expr.out b/tests/qapi-schema/indented-expr.out
index 98af89aa1..b5ce9151b 100644
--- a/tests/qapi-schema/indented-expr.out
+++ b/tests/qapi-schema/indented-expr.out
@@ -1,3 +1,3 @@
-[OrderedDict([('id', 'eins')]), OrderedDict([('id', 'zwei')])]
+[OrderedDict([('command', 'eins')]), OrderedDict([('command', 'zwei')])]
[]
[]
diff --git a/tests/qapi-schema/missing-type.err b/tests/qapi-schema/missing-type.err
new file mode 100644
index 000000000..b3e7b14e4
--- /dev/null
+++ b/tests/qapi-schema/missing-type.err
@@ -0,0 +1 @@
+tests/qapi-schema/missing-type.json:2: Expression is missing metatype
diff --git a/tests/qapi-schema/missing-type.exit b/tests/qapi-schema/missing-type.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/missing-type.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/missing-type.json b/tests/qapi-schema/missing-type.json
new file mode 100644
index 000000000..ff5349d3f
--- /dev/null
+++ b/tests/qapi-schema/missing-type.json
@@ -0,0 +1,2 @@
+# we reject an expression with missing metatype
+{ 'data': { } }
diff --git a/tests/qapi-schema/missing-type.out b/tests/qapi-schema/missing-type.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/missing-type.out
diff --git a/tests/qapi-schema/nested-struct-data.err b/tests/qapi-schema/nested-struct-data.err
new file mode 100644
index 000000000..da767bade
--- /dev/null
+++ b/tests/qapi-schema/nested-struct-data.err
@@ -0,0 +1 @@
+tests/qapi-schema/nested-struct-data.json:2: Member 'a' of 'data' for command 'foo' should be a type name
diff --git a/tests/qapi-schema/nested-struct-data.exit b/tests/qapi-schema/nested-struct-data.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/nested-struct-data.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/nested-struct-data.json b/tests/qapi-schema/nested-struct-data.json
new file mode 100644
index 000000000..3d52d2b39
--- /dev/null
+++ b/tests/qapi-schema/nested-struct-data.json
@@ -0,0 +1,4 @@
+# inline subtypes collide with our desired future use of defaults
+{ 'command': 'foo',
+ 'data': { 'a' : { 'string' : 'str', 'integer': 'int' }, 'b' : 'str' },
+ 'returns': {} }
diff --git a/tests/qapi-schema/nested-struct-data.out b/tests/qapi-schema/nested-struct-data.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/nested-struct-data.out
diff --git a/tests/qapi-schema/nested-struct-returns.err b/tests/qapi-schema/nested-struct-returns.err
new file mode 100644
index 000000000..5238d075b
--- /dev/null
+++ b/tests/qapi-schema/nested-struct-returns.err
@@ -0,0 +1 @@
+tests/qapi-schema/nested-struct-returns.json:2: Member 'a' of 'returns' for command 'foo' should be a type name
diff --git a/tests/qapi-schema/nested-struct-returns.exit b/tests/qapi-schema/nested-struct-returns.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/nested-struct-returns.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/nested-struct-returns.json b/tests/qapi-schema/nested-struct-returns.json
new file mode 100644
index 000000000..d2cd047f0
--- /dev/null
+++ b/tests/qapi-schema/nested-struct-returns.json
@@ -0,0 +1,3 @@
+# inline subtypes collide with our desired future use of defaults
+{ 'command': 'foo',
+ 'returns': { 'a' : { 'string' : 'str', 'integer': 'int' }, 'b' : 'str' } }
diff --git a/tests/qapi-schema/nested-struct-returns.out b/tests/qapi-schema/nested-struct-returns.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/nested-struct-returns.out
diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
index d43b5fd2e..c7eaa865d 100644
--- a/tests/qapi-schema/qapi-schema-test.json
+++ b/tests/qapi-schema/qapi-schema-test.json
@@ -3,44 +3,40 @@
# for testing enums
{ 'enum': 'EnumOne',
'data': [ 'value1', 'value2', 'value3' ] }
-{ 'type': 'NestedEnumsOne',
+{ 'struct': 'NestedEnumsOne',
'data': { 'enum1': 'EnumOne', '*enum2': 'EnumOne', 'enum3': 'EnumOne', '*enum4': 'EnumOne' } }
# for testing nested structs
-{ 'type': 'UserDefZero',
+{ 'struct': 'UserDefZero',
'data': { 'integer': 'int' } }
-{ 'type': 'UserDefOne',
+{ 'struct': 'UserDefOne',
'base': 'UserDefZero',
'data': { 'string': 'str', '*enum1': 'EnumOne' } }
-{ 'type': 'UserDefTwo',
- 'data': { 'string': 'str',
- 'dict': { 'string': 'str',
- 'dict': { 'userdef': 'UserDefOne', 'string': 'str' },
- '*dict2': { 'userdef': 'UserDefOne', 'string': 'str' } } } }
+{ 'struct': 'UserDefTwoDictDict',
+ 'data': { 'userdef': 'UserDefOne', 'string': 'str' } }
-{ 'type': 'UserDefNested',
+{ 'struct': 'UserDefTwoDict',
+ 'data': { 'string1': 'str',
+ 'dict2': 'UserDefTwoDictDict',
+ '*dict3': 'UserDefTwoDictDict' } }
+
+{ 'struct': 'UserDefTwo',
'data': { 'string0': 'str',
- 'dict1': { 'string1': 'str',
- 'dict2': { 'userdef1': 'UserDefOne', 'string2': 'str' },
- '*dict3': { 'userdef2': 'UserDefOne', 'string3': 'str' } } } }
+ 'dict1': 'UserDefTwoDict' } }
# for testing unions
-{ 'type': 'UserDefA',
+{ 'struct': 'UserDefA',
'data': { 'boolean': 'bool' } }
-{ 'type': 'UserDefB',
+{ 'struct': 'UserDefB',
'data': { 'integer': 'int' } }
-{ 'type': 'UserDefC',
+{ 'struct': 'UserDefC',
'data': { 'string1': 'str', 'string2': 'str' } }
-{ 'union': 'UserDefUnion',
- 'base': 'UserDefZero',
- 'data': { 'a' : 'UserDefA', 'b' : 'UserDefB' } }
-
-{ 'type': 'UserDefUnionBase',
+{ 'struct': 'UserDefUnionBase',
'data': { 'string': 'str', 'enum1': 'EnumOne' } }
{ 'union': 'UserDefFlatUnion',
@@ -57,8 +53,7 @@
'discriminator': 'enum1',
'data': { 'value1' : 'UserDefC', 'value2' : 'UserDefB', 'value3' : 'UserDefA' } }
-{ 'union': 'UserDefAnonUnion',
- 'discriminator': {},
+{ 'alternate': 'UserDefAlternate',
'data': { 'uda': 'UserDefA', 's': 'str', 'i': 'int' } }
# for testing native lists
@@ -74,7 +69,8 @@
'u64': ['uint64'],
'number': ['number'],
'boolean': ['bool'],
- 'string': ['str'] } }
+ 'string': ['str'],
+ 'sizes': ['size'] } }
# testing commands
{ 'command': 'user_def_cmd', 'data': {} }
@@ -92,7 +88,7 @@
#
# For simplicity, this example doesn't use [type=]discriminator nor optargs
# specific to discriminator values.
-{ 'type': 'UserDefOptions',
+{ 'struct': 'UserDefOptions',
'data': {
'*i64' : [ 'int' ],
'*u64' : [ 'uint64' ],
@@ -101,7 +97,7 @@
'*u64x': 'uint64' } }
# testing event
-{ 'type': 'EventStructOne',
+{ 'struct': 'EventStructOne',
'data': { 'struct1': 'UserDefOne', 'string': 'str', '*enum2': 'EnumOne' } }
{ 'event': 'EVENT_A' }
@@ -111,3 +107,23 @@
'data': { '*a': 'int', '*b': 'UserDefOne', 'c': 'str' } }
{ 'event': 'EVENT_D',
'data': { 'a' : 'EventStructOne', 'b' : 'str', '*c': 'str', '*enum3': 'EnumOne' } }
+
+# test that we correctly compile downstream extensions
+{ 'enum': '__org.qemu_x-Enum', 'data': [ '__org.qemu_x-value' ] }
+{ 'struct': '__org.qemu_x-Base',
+ 'data': { '__org.qemu_x-member1': '__org.qemu_x-Enum' } }
+{ 'struct': '__org.qemu_x-Struct', 'base': '__org.qemu_x-Base',
+ 'data': { '__org.qemu_x-member2': 'str' } }
+{ 'union': '__org.qemu_x-Union1', 'data': { '__org.qemu_x-branch': 'str' } }
+{ 'struct': '__org.qemu_x-Struct2',
+ 'data': { 'array': ['__org.qemu_x-Union1'] } }
+{ 'union': '__org.qemu_x-Union2', 'base': '__org.qemu_x-Base',
+ 'discriminator': '__org.qemu_x-member1',
+ 'data': { '__org.qemu_x-value': '__org.qemu_x-Struct2' } }
+{ 'alternate': '__org.qemu_x-Alt',
+ 'data': { '__org.qemu_x-branch': 'str', 'b': '__org.qemu_x-Base' } }
+{ 'event': '__ORG.QEMU_X-EVENT', 'data': '__org.qemu_x-Struct' }
+{ 'command': '__org.qemu_x-command',
+ 'data': { 'a': ['__org.qemu_x-Enum'], 'b': ['__org.qemu_x-Struct'],
+ 'c': '__org.qemu_x-Union2', 'd': '__org.qemu_x-Alt' },
+ 'returns': '__org.qemu_x-Union1' }
diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out
index 08d7304df..cf0ccc402 100644
--- a/tests/qapi-schema/qapi-schema-test.out
+++ b/tests/qapi-schema/qapi-schema-test.out
@@ -1,40 +1,55 @@
[OrderedDict([('enum', 'EnumOne'), ('data', ['value1', 'value2', 'value3'])]),
- OrderedDict([('type', 'NestedEnumsOne'), ('data', OrderedDict([('enum1', 'EnumOne'), ('*enum2', 'EnumOne'), ('enum3', 'EnumOne'), ('*enum4', 'EnumOne')]))]),
- OrderedDict([('type', 'UserDefZero'), ('data', OrderedDict([('integer', 'int')]))]),
- OrderedDict([('type', 'UserDefOne'), ('base', 'UserDefZero'), ('data', OrderedDict([('string', 'str'), ('*enum1', 'EnumOne')]))]),
- OrderedDict([('type', 'UserDefTwo'), ('data', OrderedDict([('string', 'str'), ('dict', OrderedDict([('string', 'str'), ('dict', OrderedDict([('userdef', 'UserDefOne'), ('string', 'str')])), ('*dict2', OrderedDict([('userdef', 'UserDefOne'), ('string', 'str')]))]))]))]),
- OrderedDict([('type', 'UserDefNested'), ('data', OrderedDict([('string0', 'str'), ('dict1', OrderedDict([('string1', 'str'), ('dict2', OrderedDict([('userdef1', 'UserDefOne'), ('string2', 'str')])), ('*dict3', OrderedDict([('userdef2', 'UserDefOne'), ('string3', 'str')]))]))]))]),
- OrderedDict([('type', 'UserDefA'), ('data', OrderedDict([('boolean', 'bool')]))]),
- OrderedDict([('type', 'UserDefB'), ('data', OrderedDict([('integer', 'int')]))]),
- OrderedDict([('type', 'UserDefC'), ('data', OrderedDict([('string1', 'str'), ('string2', 'str')]))]),
- OrderedDict([('union', 'UserDefUnion'), ('base', 'UserDefZero'), ('data', OrderedDict([('a', 'UserDefA'), ('b', 'UserDefB')]))]),
- OrderedDict([('type', 'UserDefUnionBase'), ('data', OrderedDict([('string', 'str'), ('enum1', 'EnumOne')]))]),
+ OrderedDict([('struct', 'NestedEnumsOne'), ('data', OrderedDict([('enum1', 'EnumOne'), ('*enum2', 'EnumOne'), ('enum3', 'EnumOne'), ('*enum4', 'EnumOne')]))]),
+ OrderedDict([('struct', 'UserDefZero'), ('data', OrderedDict([('integer', 'int')]))]),
+ OrderedDict([('struct', 'UserDefOne'), ('base', 'UserDefZero'), ('data', OrderedDict([('string', 'str'), ('*enum1', 'EnumOne')]))]),
+ OrderedDict([('struct', 'UserDefTwoDictDict'), ('data', OrderedDict([('userdef', 'UserDefOne'), ('string', 'str')]))]),
+ OrderedDict([('struct', 'UserDefTwoDict'), ('data', OrderedDict([('string1', 'str'), ('dict2', 'UserDefTwoDictDict'), ('*dict3', 'UserDefTwoDictDict')]))]),
+ OrderedDict([('struct', 'UserDefTwo'), ('data', OrderedDict([('string0', 'str'), ('dict1', 'UserDefTwoDict')]))]),
+ OrderedDict([('struct', 'UserDefA'), ('data', OrderedDict([('boolean', 'bool')]))]),
+ OrderedDict([('struct', 'UserDefB'), ('data', OrderedDict([('integer', 'int')]))]),
+ OrderedDict([('struct', 'UserDefC'), ('data', OrderedDict([('string1', 'str'), ('string2', 'str')]))]),
+ OrderedDict([('struct', 'UserDefUnionBase'), ('data', OrderedDict([('string', 'str'), ('enum1', 'EnumOne')]))]),
OrderedDict([('union', 'UserDefFlatUnion'), ('base', 'UserDefUnionBase'), ('discriminator', 'enum1'), ('data', OrderedDict([('value1', 'UserDefA'), ('value2', 'UserDefB'), ('value3', 'UserDefB')]))]),
OrderedDict([('union', 'UserDefFlatUnion2'), ('base', 'UserDefUnionBase'), ('discriminator', 'enum1'), ('data', OrderedDict([('value1', 'UserDefC'), ('value2', 'UserDefB'), ('value3', 'UserDefA')]))]),
- OrderedDict([('union', 'UserDefAnonUnion'), ('discriminator', OrderedDict()), ('data', OrderedDict([('uda', 'UserDefA'), ('s', 'str'), ('i', 'int')]))]),
- OrderedDict([('union', 'UserDefNativeListUnion'), ('data', OrderedDict([('integer', ['int']), ('s8', ['int8']), ('s16', ['int16']), ('s32', ['int32']), ('s64', ['int64']), ('u8', ['uint8']), ('u16', ['uint16']), ('u32', ['uint32']), ('u64', ['uint64']), ('number', ['number']), ('boolean', ['bool']), ('string', ['str'])]))]),
+ OrderedDict([('alternate', 'UserDefAlternate'), ('data', OrderedDict([('uda', 'UserDefA'), ('s', 'str'), ('i', 'int')]))]),
+ OrderedDict([('union', 'UserDefNativeListUnion'), ('data', OrderedDict([('integer', ['int']), ('s8', ['int8']), ('s16', ['int16']), ('s32', ['int32']), ('s64', ['int64']), ('u8', ['uint8']), ('u16', ['uint16']), ('u32', ['uint32']), ('u64', ['uint64']), ('number', ['number']), ('boolean', ['bool']), ('string', ['str']), ('sizes', ['size'])]))]),
OrderedDict([('command', 'user_def_cmd'), ('data', OrderedDict())]),
OrderedDict([('command', 'user_def_cmd1'), ('data', OrderedDict([('ud1a', 'UserDefOne')]))]),
OrderedDict([('command', 'user_def_cmd2'), ('data', OrderedDict([('ud1a', 'UserDefOne'), ('*ud1b', 'UserDefOne')])), ('returns', 'UserDefTwo')]),
OrderedDict([('command', 'user_def_cmd3'), ('data', OrderedDict([('a', 'int'), ('*b', 'int')])), ('returns', 'int')]),
- OrderedDict([('type', 'UserDefOptions'), ('data', OrderedDict([('*i64', ['int']), ('*u64', ['uint64']), ('*u16', ['uint16']), ('*i64x', 'int'), ('*u64x', 'uint64')]))]),
- OrderedDict([('type', 'EventStructOne'), ('data', OrderedDict([('struct1', 'UserDefOne'), ('string', 'str'), ('*enum2', 'EnumOne')]))]),
+ OrderedDict([('struct', 'UserDefOptions'), ('data', OrderedDict([('*i64', ['int']), ('*u64', ['uint64']), ('*u16', ['uint16']), ('*i64x', 'int'), ('*u64x', 'uint64')]))]),
+ OrderedDict([('struct', 'EventStructOne'), ('data', OrderedDict([('struct1', 'UserDefOne'), ('string', 'str'), ('*enum2', 'EnumOne')]))]),
OrderedDict([('event', 'EVENT_A')]),
OrderedDict([('event', 'EVENT_B'), ('data', OrderedDict())]),
OrderedDict([('event', 'EVENT_C'), ('data', OrderedDict([('*a', 'int'), ('*b', 'UserDefOne'), ('c', 'str')]))]),
- OrderedDict([('event', 'EVENT_D'), ('data', OrderedDict([('a', 'EventStructOne'), ('b', 'str'), ('*c', 'str'), ('*enum3', 'EnumOne')]))])]
+ OrderedDict([('event', 'EVENT_D'), ('data', OrderedDict([('a', 'EventStructOne'), ('b', 'str'), ('*c', 'str'), ('*enum3', 'EnumOne')]))]),
+ OrderedDict([('enum', '__org.qemu_x-Enum'), ('data', ['__org.qemu_x-value'])]),
+ OrderedDict([('struct', '__org.qemu_x-Base'), ('data', OrderedDict([('__org.qemu_x-member1', '__org.qemu_x-Enum')]))]),
+ OrderedDict([('struct', '__org.qemu_x-Struct'), ('base', '__org.qemu_x-Base'), ('data', OrderedDict([('__org.qemu_x-member2', 'str')]))]),
+ OrderedDict([('union', '__org.qemu_x-Union1'), ('data', OrderedDict([('__org.qemu_x-branch', 'str')]))]),
+ OrderedDict([('struct', '__org.qemu_x-Struct2'), ('data', OrderedDict([('array', ['__org.qemu_x-Union1'])]))]),
+ OrderedDict([('union', '__org.qemu_x-Union2'), ('base', '__org.qemu_x-Base'), ('discriminator', '__org.qemu_x-member1'), ('data', OrderedDict([('__org.qemu_x-value', '__org.qemu_x-Struct2')]))]),
+ OrderedDict([('alternate', '__org.qemu_x-Alt'), ('data', OrderedDict([('__org.qemu_x-branch', 'str'), ('b', '__org.qemu_x-Base')]))]),
+ OrderedDict([('event', '__ORG.QEMU_X-EVENT'), ('data', '__org.qemu_x-Struct')]),
+ OrderedDict([('command', '__org.qemu_x-command'), ('data', OrderedDict([('a', ['__org.qemu_x-Enum']), ('b', ['__org.qemu_x-Struct']), ('c', '__org.qemu_x-Union2'), ('d', '__org.qemu_x-Alt')])), ('returns', '__org.qemu_x-Union1')])]
[{'enum_name': 'EnumOne', 'enum_values': ['value1', 'value2', 'value3']},
- {'enum_name': 'UserDefUnionKind', 'enum_values': None},
- {'enum_name': 'UserDefAnonUnionKind', 'enum_values': None},
- {'enum_name': 'UserDefNativeListUnionKind', 'enum_values': None}]
-[OrderedDict([('type', 'NestedEnumsOne'), ('data', OrderedDict([('enum1', 'EnumOne'), ('*enum2', 'EnumOne'), ('enum3', 'EnumOne'), ('*enum4', 'EnumOne')]))]),
- OrderedDict([('type', 'UserDefZero'), ('data', OrderedDict([('integer', 'int')]))]),
- OrderedDict([('type', 'UserDefOne'), ('base', 'UserDefZero'), ('data', OrderedDict([('string', 'str'), ('*enum1', 'EnumOne')]))]),
- OrderedDict([('type', 'UserDefTwo'), ('data', OrderedDict([('string', 'str'), ('dict', OrderedDict([('string', 'str'), ('dict', OrderedDict([('userdef', 'UserDefOne'), ('string', 'str')])), ('*dict2', OrderedDict([('userdef', 'UserDefOne'), ('string', 'str')]))]))]))]),
- OrderedDict([('type', 'UserDefNested'), ('data', OrderedDict([('string0', 'str'), ('dict1', OrderedDict([('string1', 'str'), ('dict2', OrderedDict([('userdef1', 'UserDefOne'), ('string2', 'str')])), ('*dict3', OrderedDict([('userdef2', 'UserDefOne'), ('string3', 'str')]))]))]))]),
- OrderedDict([('type', 'UserDefA'), ('data', OrderedDict([('boolean', 'bool')]))]),
- OrderedDict([('type', 'UserDefB'), ('data', OrderedDict([('integer', 'int')]))]),
- OrderedDict([('type', 'UserDefC'), ('data', OrderedDict([('string1', 'str'), ('string2', 'str')]))]),
- OrderedDict([('type', 'UserDefUnionBase'), ('data', OrderedDict([('string', 'str'), ('enum1', 'EnumOne')]))]),
- OrderedDict([('type', 'UserDefOptions'), ('data', OrderedDict([('*i64', ['int']), ('*u64', ['uint64']), ('*u16', ['uint16']), ('*i64x', 'int'), ('*u64x', 'uint64')]))]),
- OrderedDict([('type', 'EventStructOne'), ('data', OrderedDict([('struct1', 'UserDefOne'), ('string', 'str'), ('*enum2', 'EnumOne')]))])]
+ {'enum_name': '__org.qemu_x-Enum', 'enum_values': ['__org.qemu_x-value']},
+ {'enum_name': 'UserDefAlternateKind', 'enum_values': None},
+ {'enum_name': 'UserDefNativeListUnionKind', 'enum_values': None},
+ {'enum_name': '__org.qemu_x-Union1Kind', 'enum_values': None},
+ {'enum_name': '__org.qemu_x-AltKind', 'enum_values': None}]
+[OrderedDict([('struct', 'NestedEnumsOne'), ('data', OrderedDict([('enum1', 'EnumOne'), ('*enum2', 'EnumOne'), ('enum3', 'EnumOne'), ('*enum4', 'EnumOne')]))]),
+ OrderedDict([('struct', 'UserDefZero'), ('data', OrderedDict([('integer', 'int')]))]),
+ OrderedDict([('struct', 'UserDefOne'), ('base', 'UserDefZero'), ('data', OrderedDict([('string', 'str'), ('*enum1', 'EnumOne')]))]),
+ OrderedDict([('struct', 'UserDefTwoDictDict'), ('data', OrderedDict([('userdef', 'UserDefOne'), ('string', 'str')]))]),
+ OrderedDict([('struct', 'UserDefTwoDict'), ('data', OrderedDict([('string1', 'str'), ('dict2', 'UserDefTwoDictDict'), ('*dict3', 'UserDefTwoDictDict')]))]),
+ OrderedDict([('struct', 'UserDefTwo'), ('data', OrderedDict([('string0', 'str'), ('dict1', 'UserDefTwoDict')]))]),
+ OrderedDict([('struct', 'UserDefA'), ('data', OrderedDict([('boolean', 'bool')]))]),
+ OrderedDict([('struct', 'UserDefB'), ('data', OrderedDict([('integer', 'int')]))]),
+ OrderedDict([('struct', 'UserDefC'), ('data', OrderedDict([('string1', 'str'), ('string2', 'str')]))]),
+ OrderedDict([('struct', 'UserDefUnionBase'), ('data', OrderedDict([('string', 'str'), ('enum1', 'EnumOne')]))]),
+ OrderedDict([('struct', 'UserDefOptions'), ('data', OrderedDict([('*i64', ['int']), ('*u64', ['uint64']), ('*u16', ['uint16']), ('*i64x', 'int'), ('*u64x', 'uint64')]))]),
+ OrderedDict([('struct', 'EventStructOne'), ('data', OrderedDict([('struct1', 'UserDefOne'), ('string', 'str'), ('*enum2', 'EnumOne')]))]),
+ OrderedDict([('struct', '__org.qemu_x-Base'), ('data', OrderedDict([('__org.qemu_x-member1', '__org.qemu_x-Enum')]))]),
+ OrderedDict([('struct', '__org.qemu_x-Struct'), ('base', '__org.qemu_x-Base'), ('data', OrderedDict([('__org.qemu_x-member2', 'str')]))]),
+ OrderedDict([('struct', '__org.qemu_x-Struct2'), ('data', OrderedDict([('array', ['__org.qemu_x-Union1'])]))])]
diff --git a/tests/qapi-schema/redefined-builtin.err b/tests/qapi-schema/redefined-builtin.err
new file mode 100644
index 000000000..b2757225c
--- /dev/null
+++ b/tests/qapi-schema/redefined-builtin.err
@@ -0,0 +1 @@
+tests/qapi-schema/redefined-builtin.json:2: built-in 'size' is already defined
diff --git a/tests/qapi-schema/redefined-builtin.exit b/tests/qapi-schema/redefined-builtin.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/redefined-builtin.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/redefined-builtin.json b/tests/qapi-schema/redefined-builtin.json
new file mode 100644
index 000000000..45b8a550a
--- /dev/null
+++ b/tests/qapi-schema/redefined-builtin.json
@@ -0,0 +1,2 @@
+# we reject types that duplicate builtin names
+{ 'struct': 'size', 'data': { 'myint': 'size' } }
diff --git a/tests/qapi-schema/redefined-builtin.out b/tests/qapi-schema/redefined-builtin.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/redefined-builtin.out
diff --git a/tests/qapi-schema/redefined-command.err b/tests/qapi-schema/redefined-command.err
new file mode 100644
index 000000000..82ae256e6
--- /dev/null
+++ b/tests/qapi-schema/redefined-command.err
@@ -0,0 +1 @@
+tests/qapi-schema/redefined-command.json:3: command 'foo' is already defined
diff --git a/tests/qapi-schema/redefined-command.exit b/tests/qapi-schema/redefined-command.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/redefined-command.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/redefined-command.json b/tests/qapi-schema/redefined-command.json
new file mode 100644
index 000000000..247e40194
--- /dev/null
+++ b/tests/qapi-schema/redefined-command.json
@@ -0,0 +1,3 @@
+# we reject commands defined more than once
+{ 'command': 'foo', 'data': { 'one': 'str' } }
+{ 'command': 'foo', 'data': { '*two': 'str' } }
diff --git a/tests/qapi-schema/redefined-command.out b/tests/qapi-schema/redefined-command.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/redefined-command.out
diff --git a/tests/qapi-schema/redefined-event.err b/tests/qapi-schema/redefined-event.err
new file mode 100644
index 000000000..35429cb48
--- /dev/null
+++ b/tests/qapi-schema/redefined-event.err
@@ -0,0 +1 @@
+tests/qapi-schema/redefined-event.json:3: event 'EVENT_A' is already defined
diff --git a/tests/qapi-schema/redefined-event.exit b/tests/qapi-schema/redefined-event.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/redefined-event.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/redefined-event.json b/tests/qapi-schema/redefined-event.json
new file mode 100644
index 000000000..7717e91c1
--- /dev/null
+++ b/tests/qapi-schema/redefined-event.json
@@ -0,0 +1,3 @@
+# we reject duplicate events
+{ 'event': 'EVENT_A', 'data': { 'myint': 'int' } }
+{ 'event': 'EVENT_A', 'data': { 'myint': 'int' } }
diff --git a/tests/qapi-schema/redefined-event.out b/tests/qapi-schema/redefined-event.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/redefined-event.out
diff --git a/tests/qapi-schema/redefined-type.err b/tests/qapi-schema/redefined-type.err
new file mode 100644
index 000000000..06ea78c47
--- /dev/null
+++ b/tests/qapi-schema/redefined-type.err
@@ -0,0 +1 @@
+tests/qapi-schema/redefined-type.json:3: struct 'foo' is already defined
diff --git a/tests/qapi-schema/redefined-type.exit b/tests/qapi-schema/redefined-type.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/redefined-type.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/redefined-type.json b/tests/qapi-schema/redefined-type.json
new file mode 100644
index 000000000..a09e768ba
--- /dev/null
+++ b/tests/qapi-schema/redefined-type.json
@@ -0,0 +1,3 @@
+# we reject types defined more than once
+{ 'struct': 'foo', 'data': { 'one': 'str' } }
+{ 'enum': 'foo', 'data': [ 'two' ] }
diff --git a/tests/qapi-schema/redefined-type.out b/tests/qapi-schema/redefined-type.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/redefined-type.out
diff --git a/tests/qapi-schema/returns-alternate.err b/tests/qapi-schema/returns-alternate.err
new file mode 100644
index 000000000..dfbb419ca
--- /dev/null
+++ b/tests/qapi-schema/returns-alternate.err
@@ -0,0 +1 @@
+tests/qapi-schema/returns-alternate.json:3: 'returns' for command 'oops' cannot use alternate type 'Alt'
diff --git a/tests/qapi-schema/returns-alternate.exit b/tests/qapi-schema/returns-alternate.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/returns-alternate.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/returns-alternate.json b/tests/qapi-schema/returns-alternate.json
new file mode 100644
index 000000000..972390c06
--- /dev/null
+++ b/tests/qapi-schema/returns-alternate.json
@@ -0,0 +1,3 @@
+# we reject returns if it is an alternate type
+{ 'alternate': 'Alt', 'data': { 'a': 'int', 'b': 'str' } }
+{ 'command': 'oops', 'returns': 'Alt' }
diff --git a/tests/qapi-schema/returns-alternate.out b/tests/qapi-schema/returns-alternate.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/returns-alternate.out
diff --git a/tests/qapi-schema/returns-array-bad.err b/tests/qapi-schema/returns-array-bad.err
new file mode 100644
index 000000000..138095ccd
--- /dev/null
+++ b/tests/qapi-schema/returns-array-bad.err
@@ -0,0 +1 @@
+tests/qapi-schema/returns-array-bad.json:2: 'returns' for command 'oops': array type must contain single type name
diff --git a/tests/qapi-schema/returns-array-bad.exit b/tests/qapi-schema/returns-array-bad.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/returns-array-bad.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/returns-array-bad.json b/tests/qapi-schema/returns-array-bad.json
new file mode 100644
index 000000000..09b0b1f18
--- /dev/null
+++ b/tests/qapi-schema/returns-array-bad.json
@@ -0,0 +1,2 @@
+# we reject an array return that is not a single type
+{ 'command': 'oops', 'returns': [ 'str', 'str' ] }
diff --git a/tests/qapi-schema/returns-array-bad.out b/tests/qapi-schema/returns-array-bad.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/returns-array-bad.out
diff --git a/tests/qapi-schema/returns-int.err b/tests/qapi-schema/returns-int.err
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/returns-int.err
diff --git a/tests/qapi-schema/returns-int.exit b/tests/qapi-schema/returns-int.exit
new file mode 100644
index 000000000..573541ac9
--- /dev/null
+++ b/tests/qapi-schema/returns-int.exit
@@ -0,0 +1 @@
+0
diff --git a/tests/qapi-schema/returns-int.json b/tests/qapi-schema/returns-int.json
new file mode 100644
index 000000000..870ec6366
--- /dev/null
+++ b/tests/qapi-schema/returns-int.json
@@ -0,0 +1,3 @@
+# It is okay (although not extensible) to return a non-dictionary
+# But to make it work, the name must be in a whitelist
+{ 'command': 'guest-get-time', 'returns': 'int' }
diff --git a/tests/qapi-schema/returns-int.out b/tests/qapi-schema/returns-int.out
new file mode 100644
index 000000000..70b3ac5e6
--- /dev/null
+++ b/tests/qapi-schema/returns-int.out
@@ -0,0 +1,3 @@
+[OrderedDict([('command', 'guest-get-time'), ('returns', 'int')])]
+[]
+[]
diff --git a/tests/qapi-schema/returns-unknown.err b/tests/qapi-schema/returns-unknown.err
new file mode 100644
index 000000000..1f43e3ac9
--- /dev/null
+++ b/tests/qapi-schema/returns-unknown.err
@@ -0,0 +1 @@
+tests/qapi-schema/returns-unknown.json:2: 'returns' for command 'oops' uses unknown type 'NoSuchType'
diff --git a/tests/qapi-schema/returns-unknown.exit b/tests/qapi-schema/returns-unknown.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/returns-unknown.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/returns-unknown.json b/tests/qapi-schema/returns-unknown.json
new file mode 100644
index 000000000..25bd498bf
--- /dev/null
+++ b/tests/qapi-schema/returns-unknown.json
@@ -0,0 +1,2 @@
+# we reject returns if it does not contain a known type
+{ 'command': 'oops', 'returns': 'NoSuchType' }
diff --git a/tests/qapi-schema/returns-unknown.out b/tests/qapi-schema/returns-unknown.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/returns-unknown.out
diff --git a/tests/qapi-schema/returns-whitelist.err b/tests/qapi-schema/returns-whitelist.err
new file mode 100644
index 000000000..a41f019a5
--- /dev/null
+++ b/tests/qapi-schema/returns-whitelist.err
@@ -0,0 +1 @@
+tests/qapi-schema/returns-whitelist.json:10: 'returns' for command 'no-way-this-will-get-whitelisted' cannot use built-in type 'array of int'
diff --git a/tests/qapi-schema/returns-whitelist.exit b/tests/qapi-schema/returns-whitelist.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/returns-whitelist.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/returns-whitelist.json b/tests/qapi-schema/returns-whitelist.json
new file mode 100644
index 000000000..e8b3cea39
--- /dev/null
+++ b/tests/qapi-schema/returns-whitelist.json
@@ -0,0 +1,11 @@
+# we enforce that 'returns' be a dict or array of dict unless whitelisted
+{ 'command': 'human-monitor-command',
+ 'data': {'command-line': 'str', '*cpu-index': 'int'},
+ 'returns': 'str' }
+{ 'enum': 'TpmModel', 'data': [ 'tpm-tis' ] }
+{ 'command': 'query-tpm-models', 'returns': ['TpmModel'] }
+{ 'command': 'guest-get-time',
+ 'returns': 'int' }
+
+{ 'command': 'no-way-this-will-get-whitelisted',
+ 'returns': [ 'int' ] }
diff --git a/tests/qapi-schema/returns-whitelist.out b/tests/qapi-schema/returns-whitelist.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/returns-whitelist.out
diff --git a/tests/qapi-schema/struct-base-clash-deep.err b/tests/qapi-schema/struct-base-clash-deep.err
new file mode 100644
index 000000000..e3e9f8d28
--- /dev/null
+++ b/tests/qapi-schema/struct-base-clash-deep.err
@@ -0,0 +1 @@
+tests/qapi-schema/struct-base-clash-deep.json:7: Member name 'name' clashes with base 'Base'
diff --git a/tests/qapi-schema/struct-base-clash-deep.exit b/tests/qapi-schema/struct-base-clash-deep.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/struct-base-clash-deep.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/struct-base-clash-deep.json b/tests/qapi-schema/struct-base-clash-deep.json
new file mode 100644
index 000000000..552fe9431
--- /dev/null
+++ b/tests/qapi-schema/struct-base-clash-deep.json
@@ -0,0 +1,9 @@
+# we check for no duplicate keys with indirect base
+{ 'struct': 'Base',
+ 'data': { 'name': 'str' } }
+{ 'struct': 'Mid',
+ 'base': 'Base',
+ 'data': { 'value': 'int' } }
+{ 'struct': 'Sub',
+ 'base': 'Mid',
+ 'data': { '*name': 'str' } }
diff --git a/tests/qapi-schema/struct-base-clash-deep.out b/tests/qapi-schema/struct-base-clash-deep.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/struct-base-clash-deep.out
diff --git a/tests/qapi-schema/struct-base-clash.err b/tests/qapi-schema/struct-base-clash.err
new file mode 100644
index 000000000..3ac37fb26
--- /dev/null
+++ b/tests/qapi-schema/struct-base-clash.err
@@ -0,0 +1 @@
+tests/qapi-schema/struct-base-clash.json:4: Member name 'name' clashes with base 'Base'
diff --git a/tests/qapi-schema/struct-base-clash.exit b/tests/qapi-schema/struct-base-clash.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/struct-base-clash.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/struct-base-clash.json b/tests/qapi-schema/struct-base-clash.json
new file mode 100644
index 000000000..f2afc9b6f
--- /dev/null
+++ b/tests/qapi-schema/struct-base-clash.json
@@ -0,0 +1,6 @@
+# we check for no duplicate keys with base
+{ 'struct': 'Base',
+ 'data': { 'name': 'str' } }
+{ 'struct': 'Sub',
+ 'base': 'Base',
+ 'data': { 'name': 'str' } }
diff --git a/tests/qapi-schema/struct-base-clash.out b/tests/qapi-schema/struct-base-clash.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/struct-base-clash.out
diff --git a/tests/qapi-schema/type-bypass-bad-gen.err b/tests/qapi-schema/type-bypass-bad-gen.err
new file mode 100644
index 000000000..a83c3c655
--- /dev/null
+++ b/tests/qapi-schema/type-bypass-bad-gen.err
@@ -0,0 +1 @@
+tests/qapi-schema/type-bypass-bad-gen.json:2: 'gen' of command 'foo' should only use false value
diff --git a/tests/qapi-schema/type-bypass-bad-gen.exit b/tests/qapi-schema/type-bypass-bad-gen.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/type-bypass-bad-gen.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/type-bypass-bad-gen.json b/tests/qapi-schema/type-bypass-bad-gen.json
new file mode 100644
index 000000000..e8dec3424
--- /dev/null
+++ b/tests/qapi-schema/type-bypass-bad-gen.json
@@ -0,0 +1,2 @@
+# 'gen' should only appear with value false
+{ 'command': 'foo', 'gen': 'whatever' }
diff --git a/tests/qapi-schema/type-bypass-bad-gen.out b/tests/qapi-schema/type-bypass-bad-gen.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/type-bypass-bad-gen.out
diff --git a/tests/qapi-schema/type-bypass-no-gen.err b/tests/qapi-schema/type-bypass-no-gen.err
new file mode 100644
index 000000000..20cef0a8a
--- /dev/null
+++ b/tests/qapi-schema/type-bypass-no-gen.err
@@ -0,0 +1 @@
+tests/qapi-schema/type-bypass-no-gen.json:2: Member 'arg' of 'data' for command 'unsafe' uses '**' but did not request 'gen':false
diff --git a/tests/qapi-schema/type-bypass-no-gen.exit b/tests/qapi-schema/type-bypass-no-gen.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/type-bypass-no-gen.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/type-bypass-no-gen.json b/tests/qapi-schema/type-bypass-no-gen.json
new file mode 100644
index 000000000..4feae3719
--- /dev/null
+++ b/tests/qapi-schema/type-bypass-no-gen.json
@@ -0,0 +1,2 @@
+# type bypass only works with 'gen':false
+{ 'command': 'unsafe', 'data': { 'arg': '**' }, 'returns': '**' }
diff --git a/tests/qapi-schema/type-bypass-no-gen.out b/tests/qapi-schema/type-bypass-no-gen.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/type-bypass-no-gen.out
diff --git a/tests/qapi-schema/type-bypass.err b/tests/qapi-schema/type-bypass.err
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/type-bypass.err
diff --git a/tests/qapi-schema/type-bypass.exit b/tests/qapi-schema/type-bypass.exit
new file mode 100644
index 000000000..573541ac9
--- /dev/null
+++ b/tests/qapi-schema/type-bypass.exit
@@ -0,0 +1 @@
+0
diff --git a/tests/qapi-schema/type-bypass.json b/tests/qapi-schema/type-bypass.json
new file mode 100644
index 000000000..48b213783
--- /dev/null
+++ b/tests/qapi-schema/type-bypass.json
@@ -0,0 +1,2 @@
+# Use of 'gen':false allows bypassing type system
+{ 'command': 'unsafe', 'data': { 'arg': '**' }, 'returns': '**', 'gen': false }
diff --git a/tests/qapi-schema/type-bypass.out b/tests/qapi-schema/type-bypass.out
new file mode 100644
index 000000000..eaf20f834
--- /dev/null
+++ b/tests/qapi-schema/type-bypass.out
@@ -0,0 +1,3 @@
+[OrderedDict([('command', 'unsafe'), ('data', OrderedDict([('arg', '**')])), ('returns', '**'), ('gen', False)])]
+[]
+[]
diff --git a/tests/qapi-schema/unicode-str.err b/tests/qapi-schema/unicode-str.err
new file mode 100644
index 000000000..f621cd644
--- /dev/null
+++ b/tests/qapi-schema/unicode-str.err
@@ -0,0 +1 @@
+tests/qapi-schema/unicode-str.json:2: 'command' uses invalid name 'é'
diff --git a/tests/qapi-schema/unicode-str.exit b/tests/qapi-schema/unicode-str.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/unicode-str.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/unicode-str.json b/tests/qapi-schema/unicode-str.json
new file mode 100644
index 000000000..5253a1b9f
--- /dev/null
+++ b/tests/qapi-schema/unicode-str.json
@@ -0,0 +1,2 @@
+# we don't support full Unicode strings, yet
+{ 'command': 'é' }
diff --git a/tests/qapi-schema/unicode-str.out b/tests/qapi-schema/unicode-str.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/unicode-str.out
diff --git a/tests/qapi-schema/union-bad-branch.err b/tests/qapi-schema/union-bad-branch.err
new file mode 100644
index 000000000..882273556
--- /dev/null
+++ b/tests/qapi-schema/union-bad-branch.err
@@ -0,0 +1 @@
+tests/qapi-schema/union-bad-branch.json:6: Union 'MyUnion' member 'ONE' clashes with 'one'
diff --git a/tests/qapi-schema/union-bad-branch.exit b/tests/qapi-schema/union-bad-branch.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/union-bad-branch.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/union-bad-branch.json b/tests/qapi-schema/union-bad-branch.json
new file mode 100644
index 000000000..913aa38bc
--- /dev/null
+++ b/tests/qapi-schema/union-bad-branch.json
@@ -0,0 +1,8 @@
+# we reject normal unions where branches would collide in C
+{ 'struct': 'One',
+ 'data': { 'string': 'str' } }
+{ 'struct': 'Two',
+ 'data': { 'number': 'int' } }
+{ 'union': 'MyUnion',
+ 'data': { 'one': 'One',
+ 'ONE': 'Two' } }
diff --git a/tests/qapi-schema/union-bad-branch.out b/tests/qapi-schema/union-bad-branch.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/union-bad-branch.out
diff --git a/tests/qapi-schema/union-base-no-discriminator.err b/tests/qapi-schema/union-base-no-discriminator.err
new file mode 100644
index 000000000..fc8b79c45
--- /dev/null
+++ b/tests/qapi-schema/union-base-no-discriminator.err
@@ -0,0 +1 @@
+tests/qapi-schema/union-base-no-discriminator.json:11: Union 'TestUnion' requires a discriminator to go along with base
diff --git a/tests/qapi-schema/union-base-no-discriminator.exit b/tests/qapi-schema/union-base-no-discriminator.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/union-base-no-discriminator.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/union-base-no-discriminator.json b/tests/qapi-schema/union-base-no-discriminator.json
new file mode 100644
index 000000000..1409cf5c9
--- /dev/null
+++ b/tests/qapi-schema/union-base-no-discriminator.json
@@ -0,0 +1,14 @@
+# we reject simple unions with a base (or flat unions without discriminator)
+{ 'struct': 'TestTypeA',
+ 'data': { 'string': 'str' } }
+
+{ 'struct': 'TestTypeB',
+ 'data': { 'integer': 'int' } }
+
+{ 'struct': 'Base',
+ 'data': { 'string': 'str' } }
+
+{ 'union': 'TestUnion',
+ 'base': 'Base',
+ 'data': { 'value1': 'TestTypeA',
+ 'value2': 'TestTypeB' } }
diff --git a/tests/qapi-schema/union-base-no-discriminator.out b/tests/qapi-schema/union-base-no-discriminator.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/union-base-no-discriminator.out
diff --git a/tests/qapi-schema/union-invalid-base.err b/tests/qapi-schema/union-invalid-base.err
index 938f96962..9f637963e 100644
--- a/tests/qapi-schema/union-invalid-base.err
+++ b/tests/qapi-schema/union-invalid-base.err
@@ -1 +1 @@
-tests/qapi-schema/union-invalid-base.json:7: Base 'TestBaseWrong' is not a valid type
+tests/qapi-schema/union-invalid-base.json:8: Base 'int' is not a valid struct
diff --git a/tests/qapi-schema/union-invalid-base.json b/tests/qapi-schema/union-invalid-base.json
index 1fa493001..92be39df6 100644
--- a/tests/qapi-schema/union-invalid-base.json
+++ b/tests/qapi-schema/union-invalid-base.json
@@ -1,10 +1,12 @@
-{ 'type': 'TestTypeA',
+# a union base type must be a struct
+{ 'struct': 'TestTypeA',
'data': { 'string': 'str' } }
-{ 'type': 'TestTypeB',
+{ 'struct': 'TestTypeB',
'data': { 'integer': 'int' } }
{ 'union': 'TestUnion',
- 'base': 'TestBaseWrong',
+ 'base': 'int',
+ 'discriminator': 'int',
'data': { 'value1': 'TestTypeA',
'value2': 'TestTypeB' } }
diff --git a/tests/qapi-schema/union-max.err b/tests/qapi-schema/union-max.err
new file mode 100644
index 000000000..55ce4399d
--- /dev/null
+++ b/tests/qapi-schema/union-max.err
@@ -0,0 +1 @@
+tests/qapi-schema/union-max.json:2: Union 'Union' member 'max' clashes with '(automatic)'
diff --git a/tests/qapi-schema/union-max.exit b/tests/qapi-schema/union-max.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/union-max.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/union-max.json b/tests/qapi-schema/union-max.json
new file mode 100644
index 000000000..d6ad98699
--- /dev/null
+++ b/tests/qapi-schema/union-max.json
@@ -0,0 +1,3 @@
+# we reject 'max' branch in a union, for collision with C enum
+{ 'union': 'Union',
+ 'data': { 'max': 'int' } }
diff --git a/tests/qapi-schema/union-max.out b/tests/qapi-schema/union-max.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/union-max.out
diff --git a/tests/qapi-schema/union-optional-branch.err b/tests/qapi-schema/union-optional-branch.err
new file mode 100644
index 000000000..3ada1334d
--- /dev/null
+++ b/tests/qapi-schema/union-optional-branch.err
@@ -0,0 +1 @@
+tests/qapi-schema/union-optional-branch.json:2: Member of union 'Union' does not allow optional name '*a'
diff --git a/tests/qapi-schema/union-optional-branch.exit b/tests/qapi-schema/union-optional-branch.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/union-optional-branch.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/union-optional-branch.json b/tests/qapi-schema/union-optional-branch.json
new file mode 100644
index 000000000..591615fc6
--- /dev/null
+++ b/tests/qapi-schema/union-optional-branch.json
@@ -0,0 +1,2 @@
+# union branches cannot be optional
+{ 'union': 'Union', 'data': { '*a': 'int', 'b': 'str' } }
diff --git a/tests/qapi-schema/union-optional-branch.out b/tests/qapi-schema/union-optional-branch.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/union-optional-branch.out
diff --git a/tests/qapi-schema/union-unknown.err b/tests/qapi-schema/union-unknown.err
new file mode 100644
index 000000000..54fe456f9
--- /dev/null
+++ b/tests/qapi-schema/union-unknown.err
@@ -0,0 +1 @@
+tests/qapi-schema/union-unknown.json:2: Member 'unknown' of union 'Union' uses unknown type 'MissingType'
diff --git a/tests/qapi-schema/union-unknown.exit b/tests/qapi-schema/union-unknown.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/union-unknown.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/union-unknown.json b/tests/qapi-schema/union-unknown.json
new file mode 100644
index 000000000..aa7e8143d
--- /dev/null
+++ b/tests/qapi-schema/union-unknown.json
@@ -0,0 +1,3 @@
+# we reject a union with unknown type in branch
+{ 'union': 'Union',
+ 'data': { 'unknown': 'MissingType' } }
diff --git a/tests/qapi-schema/union-unknown.out b/tests/qapi-schema/union-unknown.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/union-unknown.out
diff --git a/tests/qapi-schema/unknown-escape.err b/tests/qapi-schema/unknown-escape.err
new file mode 100644
index 000000000..000e30ddf
--- /dev/null
+++ b/tests/qapi-schema/unknown-escape.err
@@ -0,0 +1 @@
+tests/qapi-schema/unknown-escape.json:3:21: Unknown escape \x
diff --git a/tests/qapi-schema/unknown-escape.exit b/tests/qapi-schema/unknown-escape.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/unknown-escape.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/unknown-escape.json b/tests/qapi-schema/unknown-escape.json
new file mode 100644
index 000000000..8e6891e52
--- /dev/null
+++ b/tests/qapi-schema/unknown-escape.json
@@ -0,0 +1,3 @@
+# we only recognize JSON escape sequences, plus our \' extension (no \x)
+# { 'command': 'foo', 'data': {} }
+{ 'command': 'foo', 'dat\x61':{} }
diff --git a/tests/qapi-schema/unknown-escape.out b/tests/qapi-schema/unknown-escape.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/unknown-escape.out
diff --git a/tests/qapi-schema/unknown-expr-key.err b/tests/qapi-schema/unknown-expr-key.err
new file mode 100644
index 000000000..12f5ed5b4
--- /dev/null
+++ b/tests/qapi-schema/unknown-expr-key.err
@@ -0,0 +1 @@
+tests/qapi-schema/unknown-expr-key.json:2: Unknown key 'bogus' in struct 'bar'
diff --git a/tests/qapi-schema/unknown-expr-key.exit b/tests/qapi-schema/unknown-expr-key.exit
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/qapi-schema/unknown-expr-key.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/unknown-expr-key.json b/tests/qapi-schema/unknown-expr-key.json
new file mode 100644
index 000000000..3b2be00cc
--- /dev/null
+++ b/tests/qapi-schema/unknown-expr-key.json
@@ -0,0 +1,2 @@
+# we reject an expression with unknown top-level keys
+{ 'struct': 'bar', 'data': { 'string': 'str'}, 'bogus': { } }
diff --git a/tests/qapi-schema/unknown-expr-key.out b/tests/qapi-schema/unknown-expr-key.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/qapi-schema/unknown-expr-key.out
diff --git a/tests/qemu-iotests/051 b/tests/qemu-iotests/051
index 0360f37e5..4a8055b67 100755
--- a/tests/qemu-iotests/051
+++ b/tests/qemu-iotests/051
@@ -194,7 +194,6 @@ echo === Specifying the protocol layer ===
echo
run_qemu -drive file="$TEST_IMG",file.driver=file
-run_qemu -drive file="$TEST_IMG",file.driver=qcow2
echo
echo === Leaving out required options ===
diff --git a/tests/qemu-iotests/051.out b/tests/qemu-iotests/051.out
index 2890eac08..23c282357 100644
--- a/tests/qemu-iotests/051.out
+++ b/tests/qemu-iotests/051.out
@@ -52,7 +52,6 @@ QEMU_PROG: -drive file=TEST_DIR/t.qcow2,driver=qcow2,format=qcow2: Cannot specif
Testing: -device virtio-scsi-pci -device scsi-hd
QEMU X.Y.Z monitor - type 'help' for more information
(qemu) QEMU_PROG: -device scsi-hd: drive property not set
-QEMU_PROG: -device scsi-hd: Device 'scsi-hd' could not be initialized
=== Overriding backing file ===
@@ -128,7 +127,6 @@ QEMU_PROG: Initialization of device ide-hd failed: Device initialization failed.
Testing: -drive if=virtio
QEMU X.Y.Z monitor - type 'help' for more information
(qemu) QEMU_PROG: -drive if=virtio: Device needs media, but drive is empty
-QEMU_PROG: -drive if=virtio: Device 'virtio-blk-pci' could not be initialized
Testing: -drive if=scsi
QEMU X.Y.Z monitor - type 'help' for more information
@@ -146,23 +144,19 @@ Testing: -drive if=none,id=disk -device ide-drive,drive=disk
QEMU X.Y.Z monitor - type 'help' for more information
(qemu) QEMU_PROG: -device ide-drive,drive=disk: Device needs media, but drive is empty
QEMU_PROG: -device ide-drive,drive=disk: Device initialization failed.
-QEMU_PROG: -device ide-drive,drive=disk: Device 'ide-drive' could not be initialized
Testing: -drive if=none,id=disk -device ide-hd,drive=disk
QEMU X.Y.Z monitor - type 'help' for more information
(qemu) QEMU_PROG: -device ide-hd,drive=disk: Device needs media, but drive is empty
QEMU_PROG: -device ide-hd,drive=disk: Device initialization failed.
-QEMU_PROG: -device ide-hd,drive=disk: Device 'ide-hd' could not be initialized
Testing: -drive if=none,id=disk -device lsi53c895a -device scsi-disk,drive=disk
QEMU X.Y.Z monitor - type 'help' for more information
(qemu) QEMU_PROG: -device scsi-disk,drive=disk: Device needs media, but drive is empty
-QEMU_PROG: -device scsi-disk,drive=disk: Device 'scsi-disk' could not be initialized
Testing: -drive if=none,id=disk -device lsi53c895a -device scsi-hd,drive=disk
QEMU X.Y.Z monitor - type 'help' for more information
(qemu) QEMU_PROG: -device scsi-hd,drive=disk: Device needs media, but drive is empty
-QEMU_PROG: -device scsi-hd,drive=disk: Device 'scsi-hd' could not be initialized
=== Read-only ===
@@ -204,13 +198,11 @@ Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=disk,readonly=on -device ide-dr
QEMU X.Y.Z monitor - type 'help' for more information
(qemu) QEMU_PROG: -device ide-drive,drive=disk: Can't use a read-only drive
QEMU_PROG: -device ide-drive,drive=disk: Device initialization failed.
-QEMU_PROG: -device ide-drive,drive=disk: Device 'ide-drive' could not be initialized
Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=disk,readonly=on -device ide-hd,drive=disk
QEMU X.Y.Z monitor - type 'help' for more information
(qemu) QEMU_PROG: -device ide-hd,drive=disk: Can't use a read-only drive
QEMU_PROG: -device ide-hd,drive=disk: Device initialization failed.
-QEMU_PROG: -device ide-hd,drive=disk: Device 'ide-hd' could not be initialized
Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=disk,readonly=on -device lsi53c895a -device scsi-disk,drive=disk
QEMU X.Y.Z monitor - type 'help' for more information
@@ -253,9 +245,6 @@ Testing: -drive file=TEST_DIR/t.qcow2,file.driver=file
QEMU X.Y.Z monitor - type 'help' for more information
(qemu) qququiquit
-Testing: -drive file=TEST_DIR/t.qcow2,file.driver=qcow2
-QEMU_PROG: -drive file=TEST_DIR/t.qcow2,file.driver=qcow2: Block format 'qcow2' used by device '' doesn't support the option 'filename'
-
=== Leaving out required options ===
diff --git a/tests/qemu-iotests/059 b/tests/qemu-iotests/059
index 50ca5ce68..0ded0c3da 100755
--- a/tests/qemu-iotests/059
+++ b/tests/qemu-iotests/059
@@ -132,6 +132,11 @@ _img_info
$QEMU_IO -c "write -P 0xa 900G 512" "$TEST_IMG" | _filter_qemu_io
$QEMU_IO -c "read -v 900G 1024" "$TEST_IMG" | _filter_qemu_io
+echo
+echo "=== Testing afl image with a very large capacity ==="
+_use_sample_img afl9.vmdk.bz2
+_img_info
+
# success, all done
echo "*** done"
rm -f $seq.full
diff --git a/tests/qemu-iotests/059.out b/tests/qemu-iotests/059.out
index cbb0de425..67e3cf57e 100644
--- a/tests/qemu-iotests/059.out
+++ b/tests/qemu-iotests/059.out
@@ -2336,4 +2336,7 @@ e1000003e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
e1000003f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
read 1024/1024 bytes at offset 966367641600
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+=== Testing afl image with a very large capacity ===
+qemu-img: Can't get size of device 'image': File too large
*** done
diff --git a/tests/qemu-iotests/076 b/tests/qemu-iotests/076
index ed2be3581..c9b55a980 100755
--- a/tests/qemu-iotests/076
+++ b/tests/qemu-iotests/076
@@ -49,31 +49,36 @@ nb_sectors_offset=$((0x24))
echo
echo "== Read from a valid v1 image =="
_use_sample_img parallels-v1.bz2
-{ $QEMU_IO -c "read -P 0x11 0 64k" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
+{ $QEMU_IO -c "read -P 0x11 0 64k" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
echo
echo "== Negative catalog size =="
_use_sample_img parallels-v1.bz2
poke_file "$TEST_IMG" "$catalog_entries_offset" "\xff\xff\xff\xff"
-{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
+{ $QEMU_IO -c "read 0 512" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
echo
echo "== Overflow in catalog allocation =="
_use_sample_img parallels-v1.bz2
poke_file "$TEST_IMG" "$nb_sectors_offset" "\xff\xff\xff\xff"
poke_file "$TEST_IMG" "$catalog_entries_offset" "\x01\x00\x00\x40"
-{ $QEMU_IO -c "read 64M 64M" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
+{ $QEMU_IO -c "read 64M 64M" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
echo
echo "== Zero sectors per track =="
_use_sample_img parallels-v1.bz2
poke_file "$TEST_IMG" "$tracks_offset" "\x00\x00\x00\x00"
-{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
+{ $QEMU_IO -c "read 0 512" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
echo
echo "== Read from a valid v2 image =="
_use_sample_img parallels-v2.bz2
-{ $QEMU_IO -c "read -P 0x11 0 64k" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
+{ $QEMU_IO -c "read -P 0x11 0 64k" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
+{ $QEMU_IO -c "write -P 0x21 1024k 1k" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
+{ $QEMU_IO -c "write -P 0x22 1025k 1k" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
+{ $QEMU_IO -c "read -P 0x21 1024k 1k" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
+{ $QEMU_IO -c "read -P 0x22 1025k 1k" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
+{ $QEMU_IO -c "read -P 0 1026k 62k" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
# success, all done
echo "*** done"
diff --git a/tests/qemu-iotests/076.out b/tests/qemu-iotests/076.out
index 32ade0856..b0000aeed 100644
--- a/tests/qemu-iotests/076.out
+++ b/tests/qemu-iotests/076.out
@@ -19,4 +19,14 @@ no file open, try 'help open'
== Read from a valid v2 image ==
read 65536/65536 bytes at offset 0
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 1024/1024 bytes at offset 1048576
+1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 1024/1024 bytes at offset 1049600
+1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 1024/1024 bytes at offset 1048576
+1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 1024/1024 bytes at offset 1049600
+1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 63488/63488 bytes at offset 1050624
+62 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
*** done
diff --git a/tests/qemu-iotests/093 b/tests/qemu-iotests/093
index b9096a55d..c0e9e2b0b 100755
--- a/tests/qemu-iotests/093
+++ b/tests/qemu-iotests/093
@@ -3,6 +3,7 @@
# Tests for IO throttling
#
# Copyright (C) 2015 Red Hat, Inc.
+# Copyright (C) 2015 Igalia, S.L.
#
# 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
@@ -22,6 +23,7 @@ import iotests
class ThrottleTestCase(iotests.QMPTestCase):
test_img = "null-aio://"
+ max_drives = 3
def blockstats(self, device):
result = self.vm.qmp("query-blockstats")
@@ -32,26 +34,31 @@ class ThrottleTestCase(iotests.QMPTestCase):
raise Exception("Device not found for blockstats: %s" % device)
def setUp(self):
- self.vm = iotests.VM().add_drive(self.test_img)
+ self.vm = iotests.VM()
+ for i in range(0, self.max_drives):
+ self.vm.add_drive(self.test_img)
self.vm.launch()
def tearDown(self):
self.vm.shutdown()
- def do_test_throttle(self, seconds, params):
+ def do_test_throttle(self, ndrives, seconds, params):
def check_limit(limit, num):
# IO throttling algorithm is discrete, allow 10% error so the test
# is more robust
return limit == 0 or \
- (num < seconds * limit * 1.1
- and num > seconds * limit * 0.9)
+ (num < seconds * limit * 1.1 / ndrives
+ and num > seconds * limit * 0.9 / ndrives)
nsec_per_sec = 1000000000
- params['device'] = 'drive0'
+ params['group'] = 'test'
- result = self.vm.qmp("block_set_io_throttle", conv_keys=False, **params)
- self.assert_qmp(result, 'return', {})
+ # Set the I/O throttling parameters to all drives
+ for i in range(0, ndrives):
+ params['device'] = 'drive%d' % i
+ result = self.vm.qmp("block_set_io_throttle", conv_keys=False, **params)
+ self.assert_qmp(result, 'return', {})
# Set vm clock to a known value
ns = seconds * nsec_per_sec
@@ -66,32 +73,60 @@ class ThrottleTestCase(iotests.QMPTestCase):
params['iops'] / 2,
params['iops_rd'])
rd_nr *= seconds * 2
+ rd_nr /= ndrives
wr_nr = max(params['bps'] / rq_size / 2,
params['bps_wr'] / rq_size,
params['iops'] / 2,
params['iops_wr'])
wr_nr *= seconds * 2
+ wr_nr /= ndrives
+
+ # Send I/O requests to all drives
for i in range(rd_nr):
- self.vm.hmp_qemu_io("drive0", "aio_read %d %d" % (i * rq_size, rq_size))
- for i in range(wr_nr):
- self.vm.hmp_qemu_io("drive0", "aio_write %d %d" % (i * rq_size, rq_size))
+ for drive in range(0, ndrives):
+ self.vm.hmp_qemu_io("drive%d" % drive, "aio_read %d %d" %
+ (i * rq_size, rq_size))
- start_rd_bytes, start_rd_iops, start_wr_bytes, start_wr_iops = self.blockstats('drive0')
+ for i in range(wr_nr):
+ for drive in range(0, ndrives):
+ self.vm.hmp_qemu_io("drive%d" % drive, "aio_write %d %d" %
+ (i * rq_size, rq_size))
+
+ # We'll store the I/O stats for each drive in these arrays
+ start_rd_bytes = [0] * ndrives
+ start_rd_iops = [0] * ndrives
+ start_wr_bytes = [0] * ndrives
+ start_wr_iops = [0] * ndrives
+ end_rd_bytes = [0] * ndrives
+ end_rd_iops = [0] * ndrives
+ end_wr_bytes = [0] * ndrives
+ end_wr_iops = [0] * ndrives
+
+ # Read the stats before advancing the clock
+ for i in range(0, ndrives):
+ start_rd_bytes[i], start_rd_iops[i], start_wr_bytes[i], \
+ start_wr_iops[i] = self.blockstats('drive%d' % i)
self.vm.qtest("clock_step %d" % ns)
- end_rd_bytes, end_rd_iops, end_wr_bytes, end_wr_iops = self.blockstats('drive0')
-
- rd_bytes = end_rd_bytes - start_rd_bytes
- rd_iops = end_rd_iops - start_rd_iops
- wr_bytes = end_wr_bytes - start_wr_bytes
- wr_iops = end_wr_iops - start_wr_iops
- self.assertTrue(check_limit(params['bps'], rd_bytes + wr_bytes))
- self.assertTrue(check_limit(params['bps_rd'], rd_bytes))
- self.assertTrue(check_limit(params['bps_wr'], wr_bytes))
- self.assertTrue(check_limit(params['iops'], rd_iops + wr_iops))
- self.assertTrue(check_limit(params['iops_rd'], rd_iops))
- self.assertTrue(check_limit(params['iops_wr'], wr_iops))
+ # Read the stats after advancing the clock
+ for i in range(0, ndrives):
+ end_rd_bytes[i], end_rd_iops[i], end_wr_bytes[i], \
+ end_wr_iops[i] = self.blockstats('drive%d' % i)
+
+ # Check that the I/O is within the limits and evenly distributed
+ for i in range(0, ndrives):
+ rd_bytes = end_rd_bytes[i] - start_rd_bytes[i]
+ rd_iops = end_rd_iops[i] - start_rd_iops[i]
+ wr_bytes = end_wr_bytes[i] - start_wr_bytes[i]
+ wr_iops = end_wr_iops[i] - start_wr_iops[i]
+
+ self.assertTrue(check_limit(params['bps'], rd_bytes + wr_bytes))
+ self.assertTrue(check_limit(params['bps_rd'], rd_bytes))
+ self.assertTrue(check_limit(params['bps_wr'], wr_bytes))
+ self.assertTrue(check_limit(params['iops'], rd_iops + wr_iops))
+ self.assertTrue(check_limit(params['iops_rd'], rd_iops))
+ self.assertTrue(check_limit(params['iops_wr'], wr_iops))
def test_all(self):
params = {"bps": 4096,
@@ -101,11 +136,13 @@ class ThrottleTestCase(iotests.QMPTestCase):
"iops_rd": 10,
"iops_wr": 10,
}
- # Pick each out of all possible params and test
- for tk in params:
- limits = dict([(k, 0) for k in params])
- limits[tk] = params[tk]
- self.do_test_throttle(5, limits)
+ # Repeat the test with different numbers of drives
+ for ndrives in range(1, self.max_drives + 1):
+ # Pick each out of all possible params and test
+ for tk in params:
+ limits = dict([(k, 0) for k in params])
+ limits[tk] = params[tk] * ndrives
+ self.do_test_throttle(ndrives, 5, limits)
class ThrottleTestCoroutine(ThrottleTestCase):
test_img = "null-co://"
diff --git a/tests/qemu-iotests/119 b/tests/qemu-iotests/119
new file mode 100755
index 000000000..9a11f1b92
--- /dev/null
+++ b/tests/qemu-iotests/119
@@ -0,0 +1,60 @@
+#!/bin/bash
+#
+# NBD test case for overriding BDRV_O_PROTOCOL by explicitly specifying
+# a driver
+#
+# Copyright (C) 2015 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 program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+# creator
+owner=mreitz@redhat.com
+
+seq="$(basename $0)"
+echo "QA output created by $seq"
+
+here="$PWD"
+tmp=/tmp/$$
+status=1 # failure is the default!
+
+_cleanup()
+{
+ _cleanup_test_img
+}
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+# get standard environment, filters and checks
+. ./common.rc
+. ./common.filter
+
+_supported_fmt raw
+_supported_proto nbd
+_supported_os Linux
+
+_make_test_img 64M
+# This should not crash
+echo "{'execute': 'qmp_capabilities'}
+ {'execute': 'human-monitor-command',
+ 'arguments': {'command-line': 'qemu-io drv \"read -P 0 0 64k\"'}}
+ {'execute': 'quit'}" \
+ | $QEMU -drive id=drv,if=none,file="$TEST_IMG",driver=nbd \
+ -qmp stdio -nodefaults \
+ | _filter_qmp | _filter_qemu_io
+
+# success, all done
+echo
+echo '*** done'
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/119.out b/tests/qemu-iotests/119.out
new file mode 100644
index 000000000..58e7114e8
--- /dev/null
+++ b/tests/qemu-iotests/119.out
@@ -0,0 +1,11 @@
+QA output created by 119
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
+QMP_VERSION
+{"return": {}}
+read 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+{"return": ""}
+{"return": {}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"}
+
+*** done
diff --git a/tests/qemu-iotests/120 b/tests/qemu-iotests/120
new file mode 100755
index 000000000..9f1307876
--- /dev/null
+++ b/tests/qemu-iotests/120
@@ -0,0 +1,65 @@
+#!/bin/bash
+#
+# Non-NBD test cases for overriding BDRV_O_PROTOCOL by explicitly
+# specifying a driver
+#
+# Copyright (C) 2015 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 program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+# creator
+owner=mreitz@redhat.com
+
+seq="$(basename $0)"
+echo "QA output created by $seq"
+
+here="$PWD"
+tmp=/tmp/$$
+status=1 # failure is the default!
+
+_cleanup()
+{
+ _cleanup_test_img
+}
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+# get standard environment, filters and checks
+. ./common.rc
+. ./common.filter
+
+_supported_fmt generic
+_supported_proto file
+_supported_os Linux
+
+_make_test_img 64M
+
+echo "{'execute': 'qmp_capabilities'}
+ {'execute': 'human-monitor-command',
+ 'arguments': {'command-line': 'qemu-io drv \"write -P 42 0 64k\"'}}
+ {'execute': 'quit'}" \
+ | $QEMU -qmp stdio -nodefaults \
+ -drive id=drv,if=none,file="$TEST_IMG",driver=raw,file.driver=$IMGFMT \
+ | _filter_qmp | _filter_qemu_io
+$QEMU_IO -c 'read -P 42 0 64k' "$TEST_IMG" | _filter_qemu_io
+
+$QEMU_IO_PROG -c 'read -P 42 0 64k' \
+ "json:{'driver': 'raw', 'file': {'driver': '$IMGFMT', 'file': {'filename': '$TEST_IMG'}}}" \
+ | _filter_qemu_io
+
+# success, all done
+echo
+echo '*** done'
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/120.out b/tests/qemu-iotests/120.out
new file mode 100644
index 000000000..9131b1bce
--- /dev/null
+++ b/tests/qemu-iotests/120.out
@@ -0,0 +1,15 @@
+QA output created by 120
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
+QMP_VERSION
+{"return": {}}
+wrote 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+{"return": ""}
+{"return": {}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"}
+read 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+*** done
diff --git a/tests/qemu-iotests/122 b/tests/qemu-iotests/122
new file mode 100755
index 000000000..350ca9c46
--- /dev/null
+++ b/tests/qemu-iotests/122
@@ -0,0 +1,223 @@
+#!/bin/bash
+#
+# Test some qemu-img convert cases
+#
+# Copyright (C) 2015 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 program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+# creator
+owner=kwolf@redhat.com
+
+seq="$(basename $0)"
+echo "QA output created by $seq"
+
+here="$PWD"
+tmp=/tmp/$$
+status=1 # failure is the default!
+
+_cleanup()
+{
+ rm -f "$TEST_IMG".[123]
+ _cleanup_test_img
+}
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+# get standard environment, filters and checks
+. ./common.rc
+. ./common.filter
+
+_supported_fmt qcow2
+_supported_proto file
+_supported_os Linux
+
+
+TEST_IMG="$TEST_IMG".base _make_test_img 64M
+$QEMU_IO -c "write -P 0x11 0 64M" "$TEST_IMG".base 2>&1 | _filter_qemu_io | _filter_testdir
+
+
+echo
+echo "=== Check allocation status regression with -B ==="
+echo
+
+_make_test_img -b "$TEST_IMG".base
+$QEMU_IO -c "write -P 0x22 0 3M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir
+$QEMU_IMG convert -O $IMGFMT -B "$TEST_IMG".base "$TEST_IMG" "$TEST_IMG".orig
+$QEMU_IMG map "$TEST_IMG".orig | _filter_qemu_img_map
+
+
+echo
+echo "=== Check that zero clusters are kept in overlay ==="
+echo
+
+_make_test_img -b "$TEST_IMG".base
+
+$QEMU_IO -c "write -P 0 0 3M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir
+$QEMU_IMG convert -O $IMGFMT -B "$TEST_IMG".base "$TEST_IMG" "$TEST_IMG".orig
+$QEMU_IO -c "read -P 0 0 3M" "$TEST_IMG".orig 2>&1 | _filter_qemu_io | _filter_testdir
+$QEMU_IMG convert -O $IMGFMT -c -B "$TEST_IMG".base "$TEST_IMG" "$TEST_IMG".orig
+$QEMU_IO -c "read -P 0 0 3M" "$TEST_IMG".orig 2>&1 | _filter_qemu_io | _filter_testdir
+
+$QEMU_IO -c "write -z 0 3M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir
+$QEMU_IMG convert -O $IMGFMT -B "$TEST_IMG".base "$TEST_IMG" "$TEST_IMG".orig
+$QEMU_IO -c "read -P 0 0 3M" "$TEST_IMG".orig 2>&1 | _filter_qemu_io | _filter_testdir
+$QEMU_IMG convert -O $IMGFMT -c -B "$TEST_IMG".base "$TEST_IMG" "$TEST_IMG".orig
+$QEMU_IO -c "read -P 0 0 3M" "$TEST_IMG".orig 2>&1 | _filter_qemu_io | _filter_testdir
+
+
+echo
+echo "=== Concatenate multiple source images ==="
+echo
+
+TEST_IMG="$TEST_IMG".1 _make_test_img 4M
+TEST_IMG="$TEST_IMG".2 _make_test_img 4M
+TEST_IMG="$TEST_IMG".3 _make_test_img 4M
+
+$QEMU_IO -c "write -P 0x11 0 64k" "$TEST_IMG".1 2>&1 | _filter_qemu_io | _filter_testdir
+$QEMU_IO -c "write -P 0x22 0 64k" "$TEST_IMG".2 2>&1 | _filter_qemu_io | _filter_testdir
+$QEMU_IO -c "write -P 0x33 0 64k" "$TEST_IMG".3 2>&1 | _filter_qemu_io | _filter_testdir
+
+$QEMU_IMG convert -O $IMGFMT "$TEST_IMG".[123] "$TEST_IMG"
+$QEMU_IMG map "$TEST_IMG" | _filter_qemu_img_map
+$QEMU_IO -c "read -P 0x11 0 64k" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir
+$QEMU_IO -c "read -P 0x22 4M 64k" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir
+$QEMU_IO -c "read -P 0x33 8M 64k" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir
+
+$QEMU_IMG convert -c -O $IMGFMT "$TEST_IMG".[123] "$TEST_IMG"
+$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
+$QEMU_IO -c "read -P 0x11 0 64k" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir
+$QEMU_IO -c "read -P 0x22 4M 64k" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir
+$QEMU_IO -c "read -P 0x33 8M 64k" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir
+
+# -B can't be combined with concatenation
+$QEMU_IMG convert -O $IMGFMT -B "$TEST_IMG".base "$TEST_IMG".[123] "$TEST_IMG"
+$QEMU_IMG convert -O $IMGFMT -c -B "$TEST_IMG".base "$TEST_IMG".[123] "$TEST_IMG"
+
+
+echo
+echo "=== Compression with misaligned allocations and image sizes ==="
+echo
+
+TEST_IMG="$TEST_IMG".1 _make_test_img 1023k -o cluster_size=1024
+TEST_IMG="$TEST_IMG".2 _make_test_img 1023k -o cluster_size=1024
+
+$QEMU_IO -c "write -P 0x11 16k 16k" "$TEST_IMG".1 2>&1 | _filter_qemu_io | _filter_testdir
+$QEMU_IO -c "write -P 0x22 130k 130k" "$TEST_IMG".1 2>&1 | _filter_qemu_io | _filter_testdir
+$QEMU_IO -c "write -P 0x33 1022k 1k" "$TEST_IMG".1 2>&1 | _filter_qemu_io | _filter_testdir
+$QEMU_IO -c "write -P 0x44 0k 1k" "$TEST_IMG".2 2>&1 | _filter_qemu_io | _filter_testdir
+
+$QEMU_IMG convert -c -O $IMGFMT "$TEST_IMG".[12] "$TEST_IMG"
+$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
+$QEMU_IO -c "read -P 0 0k 16k" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir
+$QEMU_IO -c "read -P 0x11 16k 16k" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir
+$QEMU_IO -c "read -P 0 32k 98k" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir
+$QEMU_IO -c "read -P 0x22 130k 130k" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir
+$QEMU_IO -c "read -P 0 260k 762k" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir
+$QEMU_IO -c "read -P 0x33 1022k 1k" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir
+$QEMU_IO -c "read -P 0x44 1023k 1k" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir
+$QEMU_IO -c "read -P 0 1024k 1022k" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir
+
+
+echo
+echo "=== Full allocation with -S 0 ==="
+echo
+
+# Standalone image
+_make_test_img 64M
+$QEMU_IO -c "write -P 0x22 0 3M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir
+$QEMU_IO -c "write -P 0 3M 3M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir
+
+echo
+echo convert -S 0:
+$QEMU_IMG convert -O $IMGFMT -S 0 "$TEST_IMG" "$TEST_IMG".orig
+$QEMU_IO -c "read -P 0x22 0 3M" "$TEST_IMG".orig 2>&1 | _filter_qemu_io | _filter_testdir
+$QEMU_IO -c "read -P 0 3M 61M" "$TEST_IMG".orig 2>&1 | _filter_qemu_io | _filter_testdir
+$QEMU_IMG map --output=json "$TEST_IMG".orig | _filter_qemu_img_map
+
+echo
+echo convert -c -S 0:
+$QEMU_IMG convert -O $IMGFMT -c -S 0 "$TEST_IMG" "$TEST_IMG".orig
+$QEMU_IO -c "read -P 0x22 0 3M" "$TEST_IMG".orig 2>&1 | _filter_qemu_io | _filter_testdir
+$QEMU_IO -c "read -P 0 3M 61M" "$TEST_IMG".orig 2>&1 | _filter_qemu_io | _filter_testdir
+$QEMU_IMG map --output=json "$TEST_IMG".orig | _filter_qemu_img_map
+
+# With backing file
+TEST_IMG="$TEST_IMG".base _make_test_img 64M
+$QEMU_IO -c "write -P 0x11 0 32M" "$TEST_IMG".base 2>&1 | _filter_qemu_io | _filter_testdir
+
+_make_test_img -b "$TEST_IMG".base 64M
+$QEMU_IO -c "write -P 0x22 0 3M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir
+
+echo
+echo convert -S 0 with source backing file:
+$QEMU_IMG convert -O $IMGFMT -S 0 "$TEST_IMG" "$TEST_IMG".orig
+$QEMU_IO -c "read -P 0x22 0 3M" "$TEST_IMG".orig 2>&1 | _filter_qemu_io | _filter_testdir
+$QEMU_IO -c "read -P 0x11 3M 29M" "$TEST_IMG".orig 2>&1 | _filter_qemu_io | _filter_testdir
+$QEMU_IO -c "read -P 0 32M 32M" "$TEST_IMG".orig 2>&1 | _filter_qemu_io | _filter_testdir
+$QEMU_IMG map --output=json "$TEST_IMG".orig | _filter_qemu_img_map
+
+echo
+echo convert -c -S 0 with source backing file:
+$QEMU_IMG convert -O $IMGFMT -c -S 0 "$TEST_IMG" "$TEST_IMG".orig
+$QEMU_IO -c "read -P 0x22 0 3M" "$TEST_IMG".orig 2>&1 | _filter_qemu_io | _filter_testdir
+$QEMU_IO -c "read -P 0x11 3M 29M" "$TEST_IMG".orig 2>&1 | _filter_qemu_io | _filter_testdir
+$QEMU_IO -c "read -P 0 32M 32M" "$TEST_IMG".orig 2>&1 | _filter_qemu_io | _filter_testdir
+$QEMU_IMG map --output=json "$TEST_IMG".orig | _filter_qemu_img_map
+
+# With keeping the backing file
+echo
+echo convert -S 0 -B ...
+$QEMU_IMG convert -O $IMGFMT -S 0 "$TEST_IMG" "$TEST_IMG".orig
+$QEMU_IO -c "read -P 0x22 0 3M" "$TEST_IMG".orig 2>&1 | _filter_qemu_io | _filter_testdir
+$QEMU_IO -c "read -P 0x11 3M 29M" "$TEST_IMG".orig 2>&1 | _filter_qemu_io | _filter_testdir
+$QEMU_IO -c "read -P 0 32M 32M" "$TEST_IMG".orig 2>&1 | _filter_qemu_io | _filter_testdir
+$QEMU_IMG map --output=json "$TEST_IMG".orig | _filter_qemu_img_map
+
+echo
+echo convert -c -S 0 -B ...
+$QEMU_IMG convert -O $IMGFMT -c -S 0 "$TEST_IMG" "$TEST_IMG".orig
+$QEMU_IO -c "read -P 0x22 0 3M" "$TEST_IMG".orig 2>&1 | _filter_qemu_io | _filter_testdir
+$QEMU_IO -c "read -P 0x11 3M 29M" "$TEST_IMG".orig 2>&1 | _filter_qemu_io | _filter_testdir
+$QEMU_IO -c "read -P 0 32M 32M" "$TEST_IMG".orig 2>&1 | _filter_qemu_io | _filter_testdir
+$QEMU_IMG map --output=json "$TEST_IMG".orig | _filter_qemu_img_map
+
+
+echo
+echo "=== Non-zero -S ==="
+echo
+
+_make_test_img 64M -o cluster_size=1k
+$QEMU_IO -c "write -P 0 0 64k" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir
+$QEMU_IO -c "write 0 1k" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir
+$QEMU_IO -c "write 8k 1k" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir
+$QEMU_IO -c "write 17k 1k" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir
+
+for min_sparse in 4k 8k; do
+ echo
+ echo convert -S $min_sparse
+ $QEMU_IMG convert -O $IMGFMT -o cluster_size=1k -S $min_sparse "$TEST_IMG" "$TEST_IMG".orig
+ $QEMU_IMG map --output=json "$TEST_IMG".orig | _filter_qemu_img_map
+
+ echo
+ echo convert -c -S $min_sparse
+ # For compressed images, -S values other than 0 are ignored
+ $QEMU_IMG convert -O $IMGFMT -o cluster_size=1k -c -S $min_sparse "$TEST_IMG" "$TEST_IMG".orig
+ $QEMU_IMG map --output=json "$TEST_IMG".orig | _filter_qemu_img_map
+done
+
+# success, all done
+echo '*** done'
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/122.out b/tests/qemu-iotests/122.out
new file mode 100644
index 000000000..1f853b9e9
--- /dev/null
+++ b/tests/qemu-iotests/122.out
@@ -0,0 +1,209 @@
+QA output created by 122
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
+wrote 67108864/67108864 bytes at offset 0
+64 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+=== Check allocation status regression with -B ===
+
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base'
+wrote 3145728/3145728 bytes at offset 0
+3 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Offset Length File
+0 0x300000 TEST_DIR/t.IMGFMT.orig
+0x300000 0x3d00000 TEST_DIR/t.IMGFMT.base
+
+=== Check that zero clusters are kept in overlay ===
+
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base'
+wrote 3145728/3145728 bytes at offset 0
+3 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 3145728/3145728 bytes at offset 0
+3 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 3145728/3145728 bytes at offset 0
+3 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 3145728/3145728 bytes at offset 0
+3 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 3145728/3145728 bytes at offset 0
+3 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 3145728/3145728 bytes at offset 0
+3 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+=== Concatenate multiple source images ===
+
+Formatting 'TEST_DIR/t.IMGFMT.1', fmt=IMGFMT size=4194304
+Formatting 'TEST_DIR/t.IMGFMT.2', fmt=IMGFMT size=4194304
+Formatting 'TEST_DIR/t.IMGFMT.3', fmt=IMGFMT size=4194304
+wrote 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Offset Length File
+0 0x10000 TEST_DIR/t.IMGFMT
+0x400000 0x10000 TEST_DIR/t.IMGFMT
+0x800000 0x10000 TEST_DIR/t.IMGFMT
+read 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 65536/65536 bytes at offset 4194304
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 65536/65536 bytes at offset 8388608
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+[{ "start": 0, "length": 65536, "depth": 0, "zero": false, "data": true},
+{ "start": 65536, "length": 4128768, "depth": 0, "zero": true, "data": false},
+{ "start": 4194304, "length": 65536, "depth": 0, "zero": false, "data": true},
+{ "start": 4259840, "length": 4128768, "depth": 0, "zero": true, "data": false},
+{ "start": 8388608, "length": 65536, "depth": 0, "zero": false, "data": true},
+{ "start": 8454144, "length": 4128768, "depth": 0, "zero": true, "data": false}]
+read 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 65536/65536 bytes at offset 4194304
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 65536/65536 bytes at offset 8388608
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+qemu-img: -B makes no sense when concatenating multiple input images
+qemu-img: -B makes no sense when concatenating multiple input images
+
+=== Compression with misaligned allocations and image sizes ===
+
+Formatting 'TEST_DIR/t.IMGFMT.1', fmt=IMGFMT size=1047552
+Formatting 'TEST_DIR/t.IMGFMT.2', fmt=IMGFMT size=1047552
+wrote 16384/16384 bytes at offset 16384
+16 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 133120/133120 bytes at offset 133120
+130 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 1024/1024 bytes at offset 1046528
+1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 1024/1024 bytes at offset 0
+1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+[{ "start": 0, "length": 65536, "depth": 0, "zero": false, "data": true},
+{ "start": 65536, "length": 65536, "depth": 0, "zero": true, "data": false},
+{ "start": 131072, "length": 196608, "depth": 0, "zero": false, "data": true},
+{ "start": 327680, "length": 655360, "depth": 0, "zero": true, "data": false},
+{ "start": 983040, "length": 65536, "depth": 0, "zero": false, "data": true},
+{ "start": 1048576, "length": 1046528, "depth": 0, "zero": true, "data": false}]
+read 16384/16384 bytes at offset 0
+16 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 16384/16384 bytes at offset 16384
+16 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 100352/100352 bytes at offset 32768
+98 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 133120/133120 bytes at offset 133120
+130 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 780288/780288 bytes at offset 266240
+762 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 1024/1024 bytes at offset 1046528
+1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 1024/1024 bytes at offset 1047552
+1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 1046528/1046528 bytes at offset 1048576
+1022 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+=== Full allocation with -S 0 ===
+
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
+wrote 3145728/3145728 bytes at offset 0
+3 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 3145728/3145728 bytes at offset 3145728
+3 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+convert -S 0:
+read 3145728/3145728 bytes at offset 0
+3 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 63963136/63963136 bytes at offset 3145728
+61 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+[{ "start": 0, "length": 6291456, "depth": 0, "zero": false, "data": true, "offset": 327680},
+{ "start": 6291456, "length": 60817408, "depth": 0, "zero": true, "data": false}]
+
+convert -c -S 0:
+read 3145728/3145728 bytes at offset 0
+3 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 63963136/63963136 bytes at offset 3145728
+61 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+[{ "start": 0, "length": 6291456, "depth": 0, "zero": false, "data": true},
+{ "start": 6291456, "length": 60817408, "depth": 0, "zero": true, "data": false}]
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
+wrote 33554432/33554432 bytes at offset 0
+32 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base'
+wrote 3145728/3145728 bytes at offset 0
+3 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+convert -S 0 with source backing file:
+read 3145728/3145728 bytes at offset 0
+3 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 30408704/30408704 bytes at offset 3145728
+29 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 33554432/33554432 bytes at offset 33554432
+32 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+[{ "start": 0, "length": 67108864, "depth": 0, "zero": false, "data": true, "offset": 327680}]
+
+convert -c -S 0 with source backing file:
+read 3145728/3145728 bytes at offset 0
+3 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 30408704/30408704 bytes at offset 3145728
+29 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 33554432/33554432 bytes at offset 33554432
+32 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+[{ "start": 0, "length": 67108864, "depth": 0, "zero": false, "data": true}]
+
+convert -S 0 -B ...
+read 3145728/3145728 bytes at offset 0
+3 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 30408704/30408704 bytes at offset 3145728
+29 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 33554432/33554432 bytes at offset 33554432
+32 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+[{ "start": 0, "length": 67108864, "depth": 0, "zero": false, "data": true, "offset": 327680}]
+
+convert -c -S 0 -B ...
+read 3145728/3145728 bytes at offset 0
+3 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 30408704/30408704 bytes at offset 3145728
+29 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 33554432/33554432 bytes at offset 33554432
+32 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+[{ "start": 0, "length": 67108864, "depth": 0, "zero": false, "data": true}]
+
+=== Non-zero -S ===
+
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
+wrote 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 1024/1024 bytes at offset 0
+1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 1024/1024 bytes at offset 8192
+1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 1024/1024 bytes at offset 17408
+1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+convert -S 4k
+[{ "start": 0, "length": 1024, "depth": 0, "zero": false, "data": true, "offset": 8192},
+{ "start": 1024, "length": 7168, "depth": 0, "zero": true, "data": false},
+{ "start": 8192, "length": 1024, "depth": 0, "zero": false, "data": true, "offset": 9216},
+{ "start": 9216, "length": 8192, "depth": 0, "zero": true, "data": false},
+{ "start": 17408, "length": 1024, "depth": 0, "zero": false, "data": true, "offset": 10240},
+{ "start": 18432, "length": 67090432, "depth": 0, "zero": true, "data": false}]
+
+convert -c -S 4k
+[{ "start": 0, "length": 1024, "depth": 0, "zero": false, "data": true},
+{ "start": 1024, "length": 7168, "depth": 0, "zero": true, "data": false},
+{ "start": 8192, "length": 1024, "depth": 0, "zero": false, "data": true},
+{ "start": 9216, "length": 8192, "depth": 0, "zero": true, "data": false},
+{ "start": 17408, "length": 1024, "depth": 0, "zero": false, "data": true},
+{ "start": 18432, "length": 67090432, "depth": 0, "zero": true, "data": false}]
+
+convert -S 8k
+[{ "start": 0, "length": 9216, "depth": 0, "zero": false, "data": true, "offset": 8192},
+{ "start": 9216, "length": 8192, "depth": 0, "zero": true, "data": false},
+{ "start": 17408, "length": 1024, "depth": 0, "zero": false, "data": true, "offset": 17408},
+{ "start": 18432, "length": 67090432, "depth": 0, "zero": true, "data": false}]
+
+convert -c -S 8k
+[{ "start": 0, "length": 1024, "depth": 0, "zero": false, "data": true},
+{ "start": 1024, "length": 7168, "depth": 0, "zero": true, "data": false},
+{ "start": 8192, "length": 1024, "depth": 0, "zero": false, "data": true},
+{ "start": 9216, "length": 8192, "depth": 0, "zero": true, "data": false},
+{ "start": 17408, "length": 1024, "depth": 0, "zero": false, "data": true},
+{ "start": 18432, "length": 67090432, "depth": 0, "zero": true, "data": false}]
+*** done
diff --git a/tests/qemu-iotests/124 b/tests/qemu-iotests/124
new file mode 100644
index 000000000..9ccd11809
--- /dev/null
+++ b/tests/qemu-iotests/124
@@ -0,0 +1,363 @@
+#!/usr/bin/env python
+#
+# Tests for incremental drive-backup
+#
+# Copyright (C) 2015 John Snow for Red Hat, Inc.
+#
+# Based on 056.
+#
+# 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/>.
+#
+
+import os
+import iotests
+
+
+def io_write_patterns(img, patterns):
+ for pattern in patterns:
+ iotests.qemu_io('-c', 'write -P%s %s %s' % pattern, img)
+
+
+def try_remove(img):
+ try:
+ os.remove(img)
+ except OSError:
+ pass
+
+
+class Bitmap:
+ def __init__(self, name, drive):
+ self.name = name
+ self.drive = drive
+ self.num = 0
+ self.backups = list()
+
+ def base_target(self):
+ return (self.drive['backup'], None)
+
+ def new_target(self, num=None):
+ if num is None:
+ num = self.num
+ self.num = num + 1
+ base = os.path.join(iotests.test_dir,
+ "%s.%s." % (self.drive['id'], self.name))
+ suff = "%i.%s" % (num, self.drive['fmt'])
+ target = base + "inc" + suff
+ reference = base + "ref" + suff
+ self.backups.append((target, reference))
+ return (target, reference)
+
+ def last_target(self):
+ if self.backups:
+ return self.backups[-1]
+ return self.base_target()
+
+ def del_target(self):
+ for image in self.backups.pop():
+ try_remove(image)
+ self.num -= 1
+
+ def cleanup(self):
+ for backup in self.backups:
+ for image in backup:
+ try_remove(image)
+
+
+class TestIncrementalBackup(iotests.QMPTestCase):
+ def setUp(self):
+ self.bitmaps = list()
+ self.files = list()
+ self.drives = list()
+ self.vm = iotests.VM()
+ self.err_img = os.path.join(iotests.test_dir, 'err.%s' % iotests.imgfmt)
+
+ # Create a base image with a distinctive patterning
+ drive0 = self.add_node('drive0')
+ self.img_create(drive0['file'], drive0['fmt'])
+ self.vm.add_drive(drive0['file'])
+ io_write_patterns(drive0['file'], (('0x41', 0, 512),
+ ('0xd5', '1M', '32k'),
+ ('0xdc', '32M', '124k')))
+ self.vm.launch()
+
+
+ def add_node(self, node_id, fmt=iotests.imgfmt, path=None, backup=None):
+ if path is None:
+ path = os.path.join(iotests.test_dir, '%s.%s' % (node_id, fmt))
+ if backup is None:
+ backup = os.path.join(iotests.test_dir,
+ '%s.full.backup.%s' % (node_id, fmt))
+
+ self.drives.append({
+ 'id': node_id,
+ 'file': path,
+ 'backup': backup,
+ 'fmt': fmt })
+ return self.drives[-1]
+
+
+ def img_create(self, img, fmt=iotests.imgfmt, size='64M',
+ parent=None, parentFormat=None):
+ if parent:
+ if parentFormat is None:
+ parentFormat = fmt
+ iotests.qemu_img('create', '-f', fmt, img, size,
+ '-b', parent, '-F', parentFormat)
+ else:
+ iotests.qemu_img('create', '-f', fmt, img, size)
+ self.files.append(img)
+
+
+ def do_qmp_backup(self, error='Input/output error', **kwargs):
+ res = self.vm.qmp('drive-backup', **kwargs)
+ self.assert_qmp(res, 'return', {})
+
+ event = self.vm.event_wait(name="BLOCK_JOB_COMPLETED",
+ match={'data': {'device': kwargs['device']}})
+ self.assertNotEqual(event, None)
+
+ try:
+ failure = self.dictpath(event, 'data/error')
+ except AssertionError:
+ # Backup succeeded.
+ self.assert_qmp(event, 'data/offset', event['data']['len'])
+ return True
+ else:
+ # Backup failed.
+ self.assert_qmp(event, 'data/error', error)
+ return False
+
+
+ def create_anchor_backup(self, drive=None):
+ if drive is None:
+ drive = self.drives[-1]
+ res = self.do_qmp_backup(device=drive['id'], sync='full',
+ format=drive['fmt'], target=drive['backup'])
+ self.assertTrue(res)
+ self.files.append(drive['backup'])
+ return drive['backup']
+
+
+ def make_reference_backup(self, bitmap=None):
+ if bitmap is None:
+ bitmap = self.bitmaps[-1]
+ _, reference = bitmap.last_target()
+ res = self.do_qmp_backup(device=bitmap.drive['id'], sync='full',
+ format=bitmap.drive['fmt'], target=reference)
+ self.assertTrue(res)
+
+
+ def add_bitmap(self, name, drive, **kwargs):
+ bitmap = Bitmap(name, drive)
+ self.bitmaps.append(bitmap)
+ result = self.vm.qmp('block-dirty-bitmap-add', node=drive['id'],
+ name=bitmap.name, **kwargs)
+ self.assert_qmp(result, 'return', {})
+ return bitmap
+
+
+ def prepare_backup(self, bitmap=None, parent=None):
+ if bitmap is None:
+ bitmap = self.bitmaps[-1]
+ if parent is None:
+ parent, _ = bitmap.last_target()
+
+ target, _ = bitmap.new_target()
+ self.img_create(target, bitmap.drive['fmt'], parent=parent)
+ return target
+
+
+ def create_incremental(self, bitmap=None, parent=None,
+ parentFormat=None, validate=True):
+ if bitmap is None:
+ bitmap = self.bitmaps[-1]
+ if parent is None:
+ parent, _ = bitmap.last_target()
+
+ target = self.prepare_backup(bitmap, parent)
+ res = self.do_qmp_backup(device=bitmap.drive['id'],
+ sync='incremental', bitmap=bitmap.name,
+ format=bitmap.drive['fmt'], target=target,
+ mode='existing')
+ if not res:
+ bitmap.del_target();
+ self.assertFalse(validate)
+ else:
+ self.make_reference_backup(bitmap)
+ return res
+
+
+ def check_backups(self):
+ for bitmap in self.bitmaps:
+ for incremental, reference in bitmap.backups:
+ self.assertTrue(iotests.compare_images(incremental, reference))
+ last = bitmap.last_target()[0]
+ self.assertTrue(iotests.compare_images(last, bitmap.drive['file']))
+
+
+ def hmp_io_writes(self, drive, patterns):
+ for pattern in patterns:
+ self.vm.hmp_qemu_io(drive, 'write -P%s %s %s' % pattern)
+ self.vm.hmp_qemu_io(drive, 'flush')
+
+
+ def do_incremental_simple(self, **kwargs):
+ self.create_anchor_backup()
+ self.add_bitmap('bitmap0', self.drives[0], **kwargs)
+
+ # Sanity: Create a "hollow" incremental backup
+ self.create_incremental()
+ # Three writes: One complete overwrite, one new segment,
+ # and one partial overlap.
+ self.hmp_io_writes(self.drives[0]['id'], (('0xab', 0, 512),
+ ('0xfe', '16M', '256k'),
+ ('0x64', '32736k', '64k')))
+ self.create_incremental()
+ # Three more writes, one of each kind, like above
+ self.hmp_io_writes(self.drives[0]['id'], (('0x9a', 0, 512),
+ ('0x55', '8M', '352k'),
+ ('0x78', '15872k', '1M')))
+ self.create_incremental()
+ self.vm.shutdown()
+ self.check_backups()
+
+
+ def test_incremental_simple(self):
+ '''
+ Test: Create and verify three incremental backups.
+
+ Create a bitmap and a full backup before VM execution begins,
+ then create a series of three incremental backups "during execution,"
+ i.e.; after IO requests begin modifying the drive.
+ '''
+ return self.do_incremental_simple()
+
+
+ def test_small_granularity(self):
+ '''
+ Test: Create and verify backups made with a small granularity bitmap.
+
+ Perform the same test as test_incremental_simple, but with a granularity
+ of only 32KiB instead of the present default of 64KiB.
+ '''
+ return self.do_incremental_simple(granularity=32768)
+
+
+ def test_large_granularity(self):
+ '''
+ Test: Create and verify backups made with a large granularity bitmap.
+
+ Perform the same test as test_incremental_simple, but with a granularity
+ of 128KiB instead of the present default of 64KiB.
+ '''
+ return self.do_incremental_simple(granularity=131072)
+
+
+ def test_incremental_failure(self):
+ '''Test: Verify backups made after a failure are correct.
+
+ Simulate a failure during an incremental backup block job,
+ emulate additional writes, then create another incremental backup
+ afterwards and verify that the backup created is correct.
+ '''
+
+ # Create a blkdebug interface to this img as 'drive1',
+ # but don't actually create a new image.
+ drive1 = self.add_node('drive1', self.drives[0]['fmt'],
+ path=self.drives[0]['file'],
+ backup=self.drives[0]['backup'])
+ result = self.vm.qmp('blockdev-add', options={
+ 'id': drive1['id'],
+ 'driver': drive1['fmt'],
+ 'file': {
+ 'driver': 'blkdebug',
+ 'image': {
+ 'driver': 'file',
+ 'filename': drive1['file']
+ },
+ 'set-state': [{
+ 'event': 'flush_to_disk',
+ 'state': 1,
+ 'new_state': 2
+ }],
+ 'inject-error': [{
+ 'event': 'read_aio',
+ 'errno': 5,
+ 'state': 2,
+ 'immediately': False,
+ 'once': True
+ }],
+ }
+ })
+ self.assert_qmp(result, 'return', {})
+
+ self.create_anchor_backup(self.drives[0])
+ self.add_bitmap('bitmap0', drive1)
+ # Note: at this point, during a normal execution,
+ # Assume that the VM resumes and begins issuing IO requests here.
+
+ self.hmp_io_writes(drive1['id'], (('0xab', 0, 512),
+ ('0xfe', '16M', '256k'),
+ ('0x64', '32736k', '64k')))
+
+ result = self.create_incremental(validate=False)
+ self.assertFalse(result)
+ self.hmp_io_writes(drive1['id'], (('0x9a', 0, 512),
+ ('0x55', '8M', '352k'),
+ ('0x78', '15872k', '1M')))
+ self.create_incremental()
+ self.vm.shutdown()
+ self.check_backups()
+
+
+ def test_sync_dirty_bitmap_missing(self):
+ self.assert_no_active_block_jobs()
+ self.files.append(self.err_img)
+ result = self.vm.qmp('drive-backup', device=self.drives[0]['id'],
+ sync='incremental', format=self.drives[0]['fmt'],
+ target=self.err_img)
+ self.assert_qmp(result, 'error/class', 'GenericError')
+
+
+ def test_sync_dirty_bitmap_not_found(self):
+ self.assert_no_active_block_jobs()
+ self.files.append(self.err_img)
+ result = self.vm.qmp('drive-backup', device=self.drives[0]['id'],
+ sync='incremental', bitmap='unknown',
+ format=self.drives[0]['fmt'], target=self.err_img)
+ self.assert_qmp(result, 'error/class', 'GenericError')
+
+
+ def test_sync_dirty_bitmap_bad_granularity(self):
+ '''
+ Test: Test what happens if we provide an improper granularity.
+
+ The granularity must always be a power of 2.
+ '''
+ self.assert_no_active_block_jobs()
+ self.assertRaises(AssertionError, self.add_bitmap,
+ 'bitmap0', self.drives[0],
+ granularity=64000)
+
+
+ def tearDown(self):
+ self.vm.shutdown()
+ for bitmap in self.bitmaps:
+ bitmap.cleanup()
+ for filename in self.files:
+ try_remove(filename)
+
+
+if __name__ == '__main__':
+ iotests.main(supported_fmts=['qcow2'])
diff --git a/tests/qemu-iotests/124.out b/tests/qemu-iotests/124.out
new file mode 100644
index 000000000..2f7d3902f
--- /dev/null
+++ b/tests/qemu-iotests/124.out
@@ -0,0 +1,5 @@
+.......
+----------------------------------------------------------------------
+Ran 7 tests
+
+OK
diff --git a/tests/qemu-iotests/128 b/tests/qemu-iotests/128
index 249a86558..e2a0f2f89 100755
--- a/tests/qemu-iotests/128
+++ b/tests/qemu-iotests/128
@@ -29,6 +29,7 @@ tmp=/tmp/$$
status=1 # failure is the default!
devname="eiodev$$"
+sudo=""
_setup_eiodev()
{
@@ -37,6 +38,7 @@ _setup_eiodev()
echo "0 $((1024 * 1024 * 1024 / 512)) error" | \
$cmd dmsetup create "$devname" 2>/dev/null
if [ "$?" -eq 0 ]; then
+ sudo="$cmd"
return
fi
done
@@ -74,7 +76,7 @@ TEST_IMG="/dev/mapper/$devname"
echo
echo "== reading from error device =="
# Opening image should succeed but the read operation should fail
-$QEMU_IO --format "$IMGFMT" --nocache -c "read 0 65536" "$TEST_IMG" | _filter_qemu_io
+$sudo $QEMU_IO --format "$IMGFMT" --nocache -c "read 0 65536" "$TEST_IMG" | _filter_qemu_io
# success, all done
echo "*** done"
diff --git a/tests/qemu-iotests/129 b/tests/qemu-iotests/129
new file mode 100644
index 000000000..9e87e1c8d
--- /dev/null
+++ b/tests/qemu-iotests/129
@@ -0,0 +1,86 @@
+#!/usr/bin/env python
+#
+# Tests that "bdrv_drain_all" doesn't drain block jobs
+#
+# Copyright (C) 2015 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 program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+import os
+import iotests
+import time
+
+class TestStopWithBlockJob(iotests.QMPTestCase):
+ test_img = os.path.join(iotests.test_dir, 'test.img')
+ target_img = os.path.join(iotests.test_dir, 'target.img')
+ base_img = os.path.join(iotests.test_dir, 'base.img')
+
+ def setUp(self):
+ iotests.qemu_img('create', '-f', iotests.imgfmt, self.base_img, "1G")
+ iotests.qemu_img('create', '-f', iotests.imgfmt, self.test_img, "-b", self.base_img)
+ iotests.qemu_io('-f', iotests.imgfmt, '-c', 'write -P0x5d 1M 128M', self.test_img)
+ self.vm = iotests.VM().add_drive(self.test_img)
+ self.vm.launch()
+
+ def tearDown(self):
+ params = {"device": "drive0",
+ "bps": 0,
+ "bps_rd": 0,
+ "bps_wr": 0,
+ "iops": 0,
+ "iops_rd": 0,
+ "iops_wr": 0,
+ }
+ result = self.vm.qmp("block_set_io_throttle", conv_keys=False,
+ **params)
+ self.vm.shutdown()
+
+ def do_test_stop(self, cmd, **args):
+ """Test 'stop' while block job is running on a throttled drive.
+ The 'stop' command shouldn't drain the job"""
+ params = {"device": "drive0",
+ "bps": 1024,
+ "bps_rd": 0,
+ "bps_wr": 0,
+ "iops": 0,
+ "iops_rd": 0,
+ "iops_wr": 0,
+ }
+ result = self.vm.qmp("block_set_io_throttle", conv_keys=False,
+ **params)
+ self.assert_qmp(result, 'return', {})
+ result = self.vm.qmp(cmd, **args)
+ self.assert_qmp(result, 'return', {})
+ result = self.vm.qmp("stop")
+ self.assert_qmp(result, 'return', {})
+ result = self.vm.qmp("query-block-jobs")
+ self.assert_qmp(result, 'return[0]/busy', True)
+ self.assert_qmp(result, 'return[0]/ready', False)
+
+ def test_drive_mirror(self):
+ self.do_test_stop("drive-mirror", device="drive0",
+ target=self.target_img,
+ sync="full")
+
+ def test_drive_backup(self):
+ self.do_test_stop("drive-backup", device="drive0",
+ target=self.target_img,
+ sync="full")
+
+ def test_block_commit(self):
+ self.do_test_stop("block-commit", device="drive0")
+
+if __name__ == '__main__':
+ iotests.main(supported_fmts=["qcow2"])
diff --git a/tests/qemu-iotests/129.out b/tests/qemu-iotests/129.out
new file mode 100644
index 000000000..8d7e99670
--- /dev/null
+++ b/tests/qemu-iotests/129.out
@@ -0,0 +1,5 @@
+...
+----------------------------------------------------------------------
+Ran 3 tests
+
+OK
diff --git a/tests/qemu-iotests/131 b/tests/qemu-iotests/131
new file mode 100755
index 000000000..4873f40e9
--- /dev/null
+++ b/tests/qemu-iotests/131
@@ -0,0 +1,77 @@
+#!/bin/bash
+#
+# parallels format validation tests (created by QEMU)
+#
+# Copyright (C) 2014 Denis V. Lunev <den@openvz.org>
+#
+# 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/>.
+#
+
+# creator
+owner=den@openvz.org
+
+seq=`basename $0`
+echo "QA output created by $seq"
+
+here=`pwd`
+tmp=/tmp/$$
+status=1 # failure is the default!
+
+_cleanup()
+{
+ _cleanup_test_img
+}
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+# get standard environment, filters and checks
+. ./common.rc
+. ./common.filter
+
+_supported_fmt parallels
+_supported_proto file
+_supported_os Linux
+
+inuse_offset=$((0x2c))
+
+size=64M
+CLUSTER_SIZE=64k
+IMGFMT=parallels
+_make_test_img $size
+
+echo == read empty image ==
+{ $QEMU_IO -c "read -P 0 32k 64k" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
+echo == write more than 1 block in a row ==
+{ $QEMU_IO -c "write -P 0x11 32k 128k" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
+echo == read less than block ==
+{ $QEMU_IO -c "read -P 0x11 32k 32k" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
+echo == read exactly 1 block ==
+{ $QEMU_IO -c "read -P 0x11 64k 64k" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
+echo == read more than 1 block ==
+{ $QEMU_IO -c "read -P 0x11 32k 128k" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
+echo == check that there is no trash after written ==
+{ $QEMU_IO -c "read -P 0 160k 32k" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
+echo == check that there is no trash before written ==
+{ $QEMU_IO -c "read -P 0 0 32k" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
+
+echo "== Corrupt image =="
+poke_file "$TEST_IMG" "$inuse_offset" "\x59\x6e\x6f\x74"
+{ $QEMU_IO -c "read -P 0x11 64k 64k" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
+_check_test_img
+_check_test_img -r all
+{ $QEMU_IO -c "read -P 0x11 64k 64k" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
+
+# success, all done
+echo "*** done"
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/131.out b/tests/qemu-iotests/131.out
new file mode 100644
index 000000000..021a04c81
--- /dev/null
+++ b/tests/qemu-iotests/131.out
@@ -0,0 +1,41 @@
+QA output created by 131
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
+== read empty image ==
+read 65536/65536 bytes at offset 32768
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+== write more than 1 block in a row ==
+wrote 131072/131072 bytes at offset 32768
+128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+== read less than block ==
+read 32768/32768 bytes at offset 32768
+32 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+== read exactly 1 block ==
+read 65536/65536 bytes at offset 65536
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+== read more than 1 block ==
+read 131072/131072 bytes at offset 32768
+128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+== check that there is no trash after written ==
+read 32768/32768 bytes at offset 163840
+32 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+== check that there is no trash before written ==
+read 32768/32768 bytes at offset 0
+32 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+== Corrupt image ==
+qemu-io: can't open device TEST_DIR/t.parallels: parallels: Image was not closed correctly; cannot be opened read/write
+no file open, try 'help open'
+ERROR image was not closed correctly
+
+1 errors were found on the image.
+Data may be corrupted, or further writes to the image may corrupt it.
+Repairing image was not closed correctly
+The following inconsistencies were found and repaired:
+
+ 0 leaked clusters
+ 1 corruptions
+
+Double checking the fixed image now...
+No errors were found on the image.
+read 65536/65536 bytes at offset 65536
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+*** done
diff --git a/tests/qemu-iotests/134 b/tests/qemu-iotests/134
new file mode 100755
index 000000000..1c3820b17
--- /dev/null
+++ b/tests/qemu-iotests/134
@@ -0,0 +1,69 @@
+#!/bin/bash
+#
+# Test encrypted read/write using plain bdrv_read/bdrv_write
+#
+# Copyright (C) 2015 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 program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+# creator
+owner=berrange@redhat.com
+
+seq=`basename $0`
+echo "QA output created by $seq"
+
+here=`pwd`
+tmp=/tmp/$$
+status=1 # failure is the default!
+
+_cleanup()
+{
+ _cleanup_test_img
+}
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+# get standard environment, filters and checks
+. ./common.rc
+. ./common.filter
+
+_supported_fmt qcow2
+_supported_proto generic
+_supported_os Linux
+
+
+size=128M
+IMGOPTS="encryption=on" _make_test_img $size
+
+echo
+echo "== reading whole image =="
+echo "astrochicken" | $QEMU_IO -c "read 0 $size" "$TEST_IMG" | _filter_qemu_io | _filter_testdir
+
+echo
+echo "== rewriting whole image =="
+echo "astrochicken" | $QEMU_IO -c "write -P 0xa 0 $size" "$TEST_IMG" | _filter_qemu_io | _filter_testdir
+
+echo
+echo "== verify pattern =="
+echo "astrochicken" | $QEMU_IO -c "read -P 0xa 0 $size" "$TEST_IMG" | _filter_qemu_io | _filter_testdir
+
+echo
+echo "== verify pattern failure with wrong password =="
+echo "platypus" | $QEMU_IO -c "read -P 0xa 0 $size" "$TEST_IMG" | _filter_qemu_io | _filter_testdir
+
+
+# success, all done
+echo "*** done"
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/134.out b/tests/qemu-iotests/134.out
new file mode 100644
index 000000000..a16acb81c
--- /dev/null
+++ b/tests/qemu-iotests/134.out
@@ -0,0 +1,46 @@
+QA output created by 134
+qemu-img: Encrypted images are deprecated
+Support for them will be removed in a future release.
+You can use 'qemu-img convert' to convert your image to an unencrypted one.
+qemu-img: Encrypted images are deprecated
+Support for them will be removed in a future release.
+You can use 'qemu-img convert' to convert your image to an unencrypted one.
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 encryption=on
+
+== reading whole image ==
+Encrypted images are deprecated
+Support for them will be removed in a future release.
+You can use 'qemu-img convert' to convert your image to an unencrypted one.
+Disk image 'TEST_DIR/t.qcow2' is encrypted.
+password:
+read 134217728/134217728 bytes at offset 0
+128 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== rewriting whole image ==
+Encrypted images are deprecated
+Support for them will be removed in a future release.
+You can use 'qemu-img convert' to convert your image to an unencrypted one.
+Disk image 'TEST_DIR/t.qcow2' is encrypted.
+password:
+wrote 134217728/134217728 bytes at offset 0
+128 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== verify pattern ==
+Encrypted images are deprecated
+Support for them will be removed in a future release.
+You can use 'qemu-img convert' to convert your image to an unencrypted one.
+Disk image 'TEST_DIR/t.qcow2' is encrypted.
+password:
+read 134217728/134217728 bytes at offset 0
+128 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== verify pattern failure with wrong password ==
+Encrypted images are deprecated
+Support for them will be removed in a future release.
+You can use 'qemu-img convert' to convert your image to an unencrypted one.
+Disk image 'TEST_DIR/t.qcow2' is encrypted.
+password:
+Pattern verification failed at offset 0, 134217728 bytes
+read 134217728/134217728 bytes at offset 0
+128 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+*** done
diff --git a/tests/qemu-iotests/check b/tests/qemu-iotests/check
index baeae80f9..1fa63193b 100755
--- a/tests/qemu-iotests/check
+++ b/tests/qemu-iotests/check
@@ -296,9 +296,15 @@ do
run_command="./$seq"
fi
export OUTPUT_DIR=$PWD
- (cd "$source_iotests";
- MALLOC_PERTURB_=${MALLOC_PERTURB_:-$(($RANDOM % 255 + 1))} \
- $run_command >$tmp.out 2>&1)
+ if $debug; then
+ (cd "$source_iotests";
+ MALLOC_PERTURB_=${MALLOC_PERTURB_:-$(($RANDOM % 255 + 1))} \
+ $run_command -d 2>&1 | tee $tmp.out)
+ else
+ (cd "$source_iotests";
+ MALLOC_PERTURB_=${MALLOC_PERTURB_:-$(($RANDOM % 255 + 1))} \
+ $run_command >$tmp.out 2>&1)
+ fi
sts=$?
$timestamp && _timestamp
stop=`_wallclock`
diff --git a/tests/qemu-iotests/common b/tests/qemu-iotests/common
index 1e556bbb7..1030aaf25 100644
--- a/tests/qemu-iotests/common
+++ b/tests/qemu-iotests/common
@@ -32,6 +32,7 @@ check=${check-true}
diff="diff -u"
verbose=false
+debug=false
group=false
xgroup=false
imgopts=false
@@ -132,6 +133,7 @@ s/ .*//p
common options
-v verbose
+ -d debug
check options
-raw test raw (default)
@@ -322,6 +324,10 @@ testlist options
verbose=true
xpand=false
;;
+ -d)
+ debug=true
+ xpand=false
+ ;;
-x) # -x group ... exclude from group file
xgroup=true
xpand=false
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
index 4c6d9efc8..c430b6c23 100644
--- a/tests/qemu-iotests/group
+++ b/tests/qemu-iotests/group
@@ -121,9 +121,16 @@
114 rw auto quick
115 rw auto
116 rw auto quick
+119 rw auto quick
+120 rw auto quick
121 rw auto
+122 rw auto
123 rw auto quick
+124 rw auto backing
128 rw auto quick
+129 rw auto quick
130 rw auto quick
+131 rw auto quick
132 rw auto quick
+134 rw auto quick
135 rw auto
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
index 0ddc5130c..8615b1075 100644
--- a/tests/qemu-iotests/iotests.py
+++ b/tests/qemu-iotests/iotests.py
@@ -361,6 +361,8 @@ def notrun(reason):
def main(supported_fmts=[], supported_oses=['linux']):
'''Run tests'''
+ debug = '-d' in sys.argv
+ verbosity = 1
if supported_fmts and (imgfmt not in supported_fmts):
notrun('not suitable for this image format: %s' % imgfmt)
@@ -370,14 +372,20 @@ def main(supported_fmts=[], supported_oses=['linux']):
# We need to filter out the time taken from the output so that qemu-iotest
# can reliably diff the results against master output.
import StringIO
- output = StringIO.StringIO()
+ if debug:
+ output = sys.stdout
+ verbosity = 2
+ sys.argv.remove('-d')
+ else:
+ output = StringIO.StringIO()
class MyTestRunner(unittest.TextTestRunner):
- def __init__(self, stream=output, descriptions=True, verbosity=1):
+ def __init__(self, stream=output, descriptions=True, verbosity=verbosity):
unittest.TextTestRunner.__init__(self, stream, descriptions, verbosity)
# unittest.main() will use sys.exit() so expect a SystemExit exception
try:
unittest.main(testRunner=MyTestRunner)
finally:
- sys.stderr.write(re.sub(r'Ran (\d+) tests? in [\d.]+s', r'Ran \1 tests', output.getvalue()))
+ if not debug:
+ sys.stderr.write(re.sub(r'Ran (\d+) tests? in [\d.]+s', r'Ran \1 tests', output.getvalue()))
diff --git a/tests/qemu-iotests/sample_images/afl9.vmdk.bz2 b/tests/qemu-iotests/sample_images/afl9.vmdk.bz2
new file mode 100644
index 000000000..03615d36a
--- /dev/null
+++ b/tests/qemu-iotests/sample_images/afl9.vmdk.bz2
Binary files differ
diff --git a/tests/qom-test.c b/tests/qom-test.c
index 4246382d3..fde04e7a1 100644
--- a/tests/qom-test.c
+++ b/tests/qom-test.c
@@ -128,8 +128,8 @@ static void add_machine_test_cases(void)
g_assert(qstr);
mname = qstring_get_str(qstr);
if (!is_blacklisted(arch, mname)) {
- path = g_strdup_printf("/%s/qom/%s", arch, mname);
- g_test_add_data_func(path, mname, test_machine);
+ path = g_strdup_printf("qom/%s", mname);
+ qtest_add_data_func(path, mname, test_machine);
}
}
qtest_end();
diff --git a/tests/rocker/README b/tests/rocker/README
new file mode 100644
index 000000000..531e6730c
--- /dev/null
+++ b/tests/rocker/README
@@ -0,0 +1,5 @@
+Tests require simp (simple network simulator) found here:
+
+https://github.com/scottfeldman/simp
+
+Run 'all' to run all tests.
diff --git a/tests/rocker/all b/tests/rocker/all
new file mode 100755
index 000000000..d5ae9632a
--- /dev/null
+++ b/tests/rocker/all
@@ -0,0 +1,19 @@
+echo -n "Running port test... "
+./port
+if [ $? -eq 0 ]; then echo "pass"; else echo "FAILED"; exit 1; fi
+
+echo -n "Running bridge test... "
+./bridge
+if [ $? -eq 0 ]; then echo "pass"; else echo "FAILED"; exit 1; fi
+
+echo -n "Running bridge STP test... "
+./bridge-stp
+if [ $? -eq 0 ]; then echo "pass"; else echo "FAILED"; exit 1; fi
+
+echo -n "Running bridge VLAN test... "
+./bridge-vlan
+if [ $? -eq 0 ]; then echo "pass"; else echo "FAILED"; exit 1; fi
+
+echo -n "Running bridge VLAN STP test... "
+./bridge-vlan-stp
+if [ $? -eq 0 ]; then echo "pass"; else echo "FAILED"; exit 1; fi
diff --git a/tests/rocker/bridge b/tests/rocker/bridge
new file mode 100755
index 000000000..46abc6f4f
--- /dev/null
+++ b/tests/rocker/bridge
@@ -0,0 +1,43 @@
+simp destroy ".*"
+simp create -o sw1:rocker:sw1 tut tut.dot
+simp start tut
+sleep 10
+while ! simp ssh tut sw1 --cmd "ping -c 1 localhost >/dev/null"; do sleep 1; done
+while ! simp ssh tut h1 --cmd "ping -c 1 localhost >/dev/null"; do sleep 1; done
+while ! simp ssh tut h2 --cmd "ping -c 1 localhost >/dev/null"; do sleep 1; done
+
+# configure a 2-port bridge
+
+simp ssh tut sw1 --cmd "sudo /sbin/ip link add name br0 type bridge"
+simp ssh tut sw1 --cmd "sudo /sbin/ip link set dev sw1p1 master br0"
+simp ssh tut sw1 --cmd "sudo /sbin/ip link set dev sw1p2 master br0"
+
+# turn off vlan default_pvid on br0
+
+simp ssh tut sw1 --cmd "echo 0 | sudo dd of=/sys/class/net/br0/bridge/default_pvid 2> /dev/null"
+
+# turn off learning and flooding in SW
+
+simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p1 learning off"
+simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p2 learning off"
+
+simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p1 flood off"
+simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p2 flood off"
+
+# bring up bridge and ports
+
+simp ssh tut sw1 --cmd "sudo ifconfig br0 up"
+simp ssh tut sw1 --cmd "sudo ifconfig sw1p1 up"
+simp ssh tut sw1 --cmd "sudo ifconfig sw1p2 up"
+simp ssh tut sw1 --cmd "sudo ifconfig br0 11.0.0.3/24"
+
+# config IP on hosts
+
+simp ssh tut h1 --cmd "sudo ifconfig sw1p1 11.0.0.1/24"
+simp ssh tut h2 --cmd "sudo ifconfig sw1p1 11.0.0.2/24"
+
+# test...
+
+simp ssh tut h1 --cmd "ping -c10 11.0.0.2 >/dev/null"
+if [ $? -ne 0 ]; then exit 1; fi
+simp ssh tut h1 --cmd "ping -c10 11.0.0.3 >/dev/null"
diff --git a/tests/rocker/bridge-stp b/tests/rocker/bridge-stp
new file mode 100755
index 000000000..008568ad8
--- /dev/null
+++ b/tests/rocker/bridge-stp
@@ -0,0 +1,52 @@
+simp destroy ".*"
+simp create -o sw1:rocker:sw1 tut tut.dot
+simp start tut
+sleep 10
+while ! simp ssh tut sw1 --cmd "ping -c 1 localhost >/dev/null"; do sleep 1; done
+while ! simp ssh tut h1 --cmd "ping -c 1 localhost >/dev/null"; do sleep 1; done
+while ! simp ssh tut h2 --cmd "ping -c 1 localhost >/dev/null"; do sleep 1; done
+
+# configure a 2-port bridge
+
+simp ssh tut sw1 --cmd "sudo /sbin/ip link add name br0 type bridge"
+simp ssh tut sw1 --cmd "sudo brctl stp br0 on"
+simp ssh tut sw1 --cmd "sudo /sbin/ip link set dev sw1p1 master br0"
+simp ssh tut sw1 --cmd "sudo /sbin/ip link set dev sw1p2 master br0"
+
+# turn off vlan default_pvid on br0
+
+simp ssh tut sw1 --cmd "echo 0 | sudo dd of=/sys/class/net/br0/bridge/default_pvid 2> /dev/null"
+
+# turn off learning and flooding in SW
+
+simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p1 learning off"
+simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p2 learning off"
+
+simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p1 flood off"
+simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p2 flood off"
+
+# config IP on hosts
+
+simp ssh tut h1 --cmd "sudo ifconfig sw1p1 11.0.0.1/24"
+simp ssh tut h2 --cmd "sudo ifconfig sw1p1 11.0.0.2/24"
+
+# bring up bridge and ports
+
+simp ssh tut sw1 --cmd "sudo ifconfig br0 up"
+simp ssh tut sw1 --cmd "sudo ifconfig sw1p1 up"
+simp ssh tut sw1 --cmd "sudo ifconfig sw1p2 up"
+
+# test...
+
+simp ssh tut h1 --cmd "ping -w 1 -c1 11.0.0.2 >/dev/null"
+if [ $? -eq 0 ]; then exit 1; fi
+sleep 10
+simp ssh tut h1 --cmd "ping -c10 11.0.0.2 >/dev/null"
+sleep 10
+simp ssh tut h1 --cmd "ping -c10 11.0.0.2 >/dev/null"
+sleep 10
+simp ssh tut h1 --cmd "ping -c10 11.0.0.2 >/dev/null"
+sleep 10
+simp ssh tut h1 --cmd "ping -c10 11.0.0.2 >/dev/null"
+sleep 10
+simp ssh tut h1 --cmd "ping -c10 11.0.0.2 >/dev/null"
diff --git a/tests/rocker/bridge-vlan b/tests/rocker/bridge-vlan
new file mode 100755
index 000000000..897d82c5c
--- /dev/null
+++ b/tests/rocker/bridge-vlan
@@ -0,0 +1,52 @@
+simp destroy ".*"
+simp create -o sw1:rocker:sw1 tut tut.dot
+simp start tut
+sleep 10
+while ! simp ssh tut sw1 --cmd "ping -c 1 localhost >/dev/null"; do sleep 1; done
+while ! simp ssh tut h1 --cmd "ping -c 1 localhost >/dev/null"; do sleep 1; done
+while ! simp ssh tut h2 --cmd "ping -c 1 localhost >/dev/null"; do sleep 1; done
+
+# configure a 2-port bridge
+
+simp ssh tut sw1 --cmd "sudo /sbin/ip link add name br0 type bridge"
+simp ssh tut sw1 --cmd "sudo /sbin/ip link set dev sw1p1 master br0"
+simp ssh tut sw1 --cmd "sudo /sbin/ip link set dev sw1p2 master br0"
+
+# turn off vlan default_pvid on br0
+# turn on vlan filtering on br0
+
+simp ssh tut sw1 --cmd "echo 0 | sudo dd of=/sys/class/net/br0/bridge/default_pvid 2> /dev/null"
+simp ssh tut sw1 --cmd "echo 1 | sudo dd of=/sys/class/net/br0/bridge/vlan_filtering 2> /dev/null"
+
+# add both ports to VLAN 57
+
+simp ssh tut sw1 --cmd "sudo /sbin/bridge vlan add vid 57 dev sw1p1"
+simp ssh tut sw1 --cmd "sudo /sbin/bridge vlan add vid 57 dev sw1p2"
+
+# turn off learning and flooding in SW
+
+simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p1 learning off"
+simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p2 learning off"
+
+simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p1 flood off"
+simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p2 flood off"
+
+# bring up bridge and ports
+
+simp ssh tut sw1 --cmd "sudo ifconfig br0 up"
+simp ssh tut sw1 --cmd "sudo ifconfig sw1p1 up"
+simp ssh tut sw1 --cmd "sudo ifconfig sw1p2 up"
+
+# config IP on host VLANs
+
+simp ssh tut h1 --cmd "sudo vconfig add sw1p1 57 >/dev/null 2>&1"
+simp ssh tut h1 --cmd "sudo ifconfig sw1p1 up"
+simp ssh tut h1 --cmd "sudo ifconfig sw1p1.57 11.0.0.1/24"
+
+simp ssh tut h2 --cmd "sudo vconfig add sw1p1 57 >/dev/null 2>&1"
+simp ssh tut h2 --cmd "sudo ifconfig sw1p1 up"
+simp ssh tut h2 --cmd "sudo ifconfig sw1p1.57 11.0.0.2/24"
+
+# test...
+
+simp ssh tut h1 --cmd "ping -c10 11.0.0.2 >/dev/null"
diff --git a/tests/rocker/bridge-vlan-stp b/tests/rocker/bridge-vlan-stp
new file mode 100755
index 000000000..85d264682
--- /dev/null
+++ b/tests/rocker/bridge-vlan-stp
@@ -0,0 +1,64 @@
+simp destroy ".*"
+simp create -o sw1:rocker:sw1 tut tut.dot
+simp start tut
+sleep 10
+while ! simp ssh tut sw1 --cmd "ping -c 1 localhost >/dev/null"; do sleep 1; done
+while ! simp ssh tut h1 --cmd "ping -c 1 localhost >/dev/null"; do sleep 1; done
+while ! simp ssh tut h2 --cmd "ping -c 1 localhost >/dev/null"; do sleep 1; done
+
+# configure a 2-port bridge
+
+simp ssh tut sw1 --cmd "sudo /sbin/ip link add name br0 type bridge"
+simp ssh tut sw1 --cmd "sudo brctl stp br0 on"
+simp ssh tut sw1 --cmd "sudo /sbin/ip link set dev sw1p1 master br0"
+simp ssh tut sw1 --cmd "sudo /sbin/ip link set dev sw1p2 master br0"
+
+# turn off vlan default_pvid on br0
+# turn on vlan filtering on br0
+
+simp ssh tut sw1 --cmd "echo 0 | sudo dd of=/sys/class/net/br0/bridge/default_pvid 2> /dev/null"
+simp ssh tut sw1 --cmd "echo 1 | sudo dd of=/sys/class/net/br0/bridge/vlan_filtering 2> /dev/null"
+
+# add both ports to VLAN 57
+
+simp ssh tut sw1 --cmd "sudo /sbin/bridge vlan add vid 57 dev sw1p1"
+simp ssh tut sw1 --cmd "sudo /sbin/bridge vlan add vid 57 dev sw1p2"
+
+# turn off learning and flooding in SW
+
+simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p1 learning off"
+simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p2 learning off"
+
+simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p1 flood off"
+simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p2 flood off"
+
+# config IP on host VLANs
+
+simp ssh tut h1 --cmd "sudo vconfig add sw1p1 57 >/dev/null 2>&1"
+simp ssh tut h1 --cmd "sudo ifconfig sw1p1 up"
+simp ssh tut h1 --cmd "sudo ifconfig sw1p1.57 11.0.0.1/24"
+
+simp ssh tut h2 --cmd "sudo vconfig add sw1p1 57 >/dev/null 2>&1"
+simp ssh tut h2 --cmd "sudo ifconfig sw1p1 up"
+simp ssh tut h2 --cmd "sudo ifconfig sw1p1.57 11.0.0.2/24"
+
+# bring up bridge and ports
+
+simp ssh tut sw1 --cmd "sudo ifconfig br0 up"
+simp ssh tut sw1 --cmd "sudo ifconfig sw1p1 up"
+simp ssh tut sw1 --cmd "sudo ifconfig sw1p2 up"
+
+# test...
+
+simp ssh tut h1 --cmd "ping -w 1 -c1 11.0.0.2 >/dev/null"
+if [ $? -eq 0 ]; then exit 1; fi
+sleep 10
+simp ssh tut h1 --cmd "ping -c10 11.0.0.2 >/dev/null"
+sleep 10
+simp ssh tut h1 --cmd "ping -c10 11.0.0.2 >/dev/null"
+sleep 10
+simp ssh tut h1 --cmd "ping -c10 11.0.0.2 >/dev/null"
+sleep 10
+simp ssh tut h1 --cmd "ping -c10 11.0.0.2 >/dev/null"
+sleep 10
+simp ssh tut h1 --cmd "ping -c10 11.0.0.2 >/dev/null"
diff --git a/tests/rocker/port b/tests/rocker/port
new file mode 100755
index 000000000..5f2c24804
--- /dev/null
+++ b/tests/rocker/port
@@ -0,0 +1,22 @@
+simp destroy ".*"
+simp create -o sw1:rocker:sw1 tut tut.dot
+simp start tut
+while ! simp ssh tut sw1 --cmd "ping -c 1 localhost >/dev/null"; do sleep 1; done
+while ! simp ssh tut h1 --cmd "ping -c 1 localhost >/dev/null"; do sleep 1; done
+while ! simp ssh tut h2 --cmd "ping -c 1 localhost >/dev/null"; do sleep 1; done
+
+# bring up DUT ports
+
+simp ssh tut sw1 --cmd "sudo ifconfig sw1p1 11.0.0.1/24"
+simp ssh tut sw1 --cmd "sudo ifconfig sw1p2 12.0.0.1/24"
+
+# config IP on hosts
+
+simp ssh tut h1 --cmd "sudo ifconfig sw1p1 11.0.0.2/24"
+simp ssh tut h2 --cmd "sudo ifconfig sw1p1 12.0.0.2/24"
+
+# test...
+
+simp ssh tut h1 --cmd "ping -c10 11.0.0.1 >/dev/null"
+if [ $? -eq 1 ]; then exit 1; fi
+simp ssh tut h2 --cmd "ping -c10 12.0.0.1 >/dev/null"
diff --git a/tests/rocker/tut.dot b/tests/rocker/tut.dot
new file mode 100644
index 000000000..87f7266f0
--- /dev/null
+++ b/tests/rocker/tut.dot
@@ -0,0 +1,8 @@
+graph G {
+ graph [hostidtype="hostname", version="1:0", date="04/12/2013"];
+ edge [dir=none, notify="log"];
+ sw1:swp1 -- h1:swp1;
+ sw1:swp2 -- h2:swp1;
+ sw1:swp3 -- h3:swp1;
+ sw1:swp4 -- h4:swp1;
+}
diff --git a/tests/rtl8139-test.c b/tests/rtl8139-test.c
index 4e0bf02c3..e749be38e 100644
--- a/tests/rtl8139-test.c
+++ b/tests/rtl8139-test.c
@@ -12,6 +12,7 @@
#include "libqtest.h"
#include "libqos/pci-pc.h"
#include "qemu/osdep.h"
+#include "qemu/timer.h"
#include "qemu-common.h"
/* Tests only initialization so far. TODO: Replace with functional tests */
@@ -20,7 +21,6 @@ static void nop(void)
}
#define CLK 33000000
-#define NS_PER_SEC 1000000000ULL
static QPCIBus *pcibus;
static QPCIDevice *dev;
@@ -86,7 +86,7 @@ static void test_timer(void)
fatal("time too big %u\n", curr);
}
for (cnt = 0; ; ) {
- clock_step(1 * NS_PER_SEC);
+ clock_step(1 * NANOSECONDS_PER_SECOND);
prev = curr;
curr = in_Timer();
@@ -125,7 +125,7 @@ static void test_timer(void)
out_IntrStatus(0x4000);
curr = in_Timer();
out_TimerInt(curr + 0.5 * CLK);
- clock_step(1 * NS_PER_SEC);
+ clock_step(1 * NANOSECONDS_PER_SECOND);
out_Timer(0);
if ((in_IntrStatus() & 0x4000) == 0) {
fatal("we should have an interrupt here!\n");
@@ -137,7 +137,7 @@ static void test_timer(void)
out_IntrStatus(0x4000);
curr = in_Timer();
out_TimerInt(curr + 0.5 * CLK);
- clock_step(1 * NS_PER_SEC);
+ clock_step(1 * NANOSECONDS_PER_SECOND);
out_TimerInt(0);
if ((in_IntrStatus() & 0x4000) == 0) {
fatal("we should have an interrupt here!\n");
@@ -148,7 +148,7 @@ static void test_timer(void)
next = curr + 5.0 * CLK;
out_TimerInt(next);
for (cnt = 0; ; ) {
- clock_step(1 * NS_PER_SEC);
+ clock_step(1 * NANOSECONDS_PER_SECOND);
prev = curr;
curr = in_Timer();
diff = (curr-prev) & 0xffffffffu;
diff --git a/tests/tco-test.c b/tests/tco-test.c
new file mode 100644
index 000000000..419f7cf46
--- /dev/null
+++ b/tests/tco-test.c
@@ -0,0 +1,465 @@
+/*
+ * QEMU ICH9 TCO emulation tests
+ *
+ * Copyright (c) 2015 Paulo Alcantara <pcacjr@zytor.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 <glib.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "libqtest.h"
+#include "libqos/pci.h"
+#include "libqos/pci-pc.h"
+#include "hw/pci/pci_regs.h"
+#include "hw/i386/ich9.h"
+#include "hw/acpi/ich9.h"
+#include "hw/acpi/tco.h"
+
+#define RCBA_BASE_ADDR 0xfed1c000
+#define PM_IO_BASE_ADDR 0xb000
+
+enum {
+ TCO_RLD_DEFAULT = 0x0000,
+ TCO_DAT_IN_DEFAULT = 0x00,
+ TCO_DAT_OUT_DEFAULT = 0x00,
+ TCO1_STS_DEFAULT = 0x0000,
+ TCO2_STS_DEFAULT = 0x0000,
+ TCO1_CNT_DEFAULT = 0x0000,
+ TCO2_CNT_DEFAULT = 0x0008,
+ TCO_MESSAGE1_DEFAULT = 0x00,
+ TCO_MESSAGE2_DEFAULT = 0x00,
+ TCO_WDCNT_DEFAULT = 0x00,
+ TCO_TMR_DEFAULT = 0x0004,
+ SW_IRQ_GEN_DEFAULT = 0x03,
+};
+
+#define TCO_SECS_TO_TICKS(secs) (((secs) * 10) / 6)
+#define TCO_TICKS_TO_SECS(ticks) (((ticks) * 6) / 10)
+
+typedef struct {
+ const char *args;
+ bool noreboot;
+ QPCIDevice *dev;
+ void *tco_io_base;
+} TestData;
+
+static void test_init(TestData *d)
+{
+ QPCIBus *bus;
+ QTestState *qs;
+ char *s;
+
+ s = g_strdup_printf("-machine q35 %s %s",
+ d->noreboot ? "" : "-global ICH9-LPC.noreboot=false",
+ !d->args ? "" : d->args);
+ qs = qtest_start(s);
+ qtest_irq_intercept_in(qs, "ioapic");
+ g_free(s);
+
+ bus = qpci_init_pc();
+ d->dev = qpci_device_find(bus, QPCI_DEVFN(0x1f, 0x00));
+ g_assert(d->dev != NULL);
+
+ qpci_device_enable(d->dev);
+
+ /* set ACPI PM I/O space base address */
+ qpci_config_writel(d->dev, ICH9_LPC_PMBASE, PM_IO_BASE_ADDR | 0x1);
+ /* enable ACPI I/O */
+ qpci_config_writeb(d->dev, ICH9_LPC_ACPI_CTRL, 0x80);
+ /* set Root Complex BAR */
+ qpci_config_writel(d->dev, ICH9_LPC_RCBA, RCBA_BASE_ADDR | 0x1);
+
+ d->tco_io_base = (void *)((uintptr_t)PM_IO_BASE_ADDR + 0x60);
+}
+
+static void stop_tco(const TestData *d)
+{
+ uint32_t val;
+
+ val = qpci_io_readw(d->dev, d->tco_io_base + TCO1_CNT);
+ val |= TCO_TMR_HLT;
+ qpci_io_writew(d->dev, d->tco_io_base + TCO1_CNT, val);
+}
+
+static void start_tco(const TestData *d)
+{
+ uint32_t val;
+
+ val = qpci_io_readw(d->dev, d->tco_io_base + TCO1_CNT);
+ val &= ~TCO_TMR_HLT;
+ qpci_io_writew(d->dev, d->tco_io_base + TCO1_CNT, val);
+}
+
+static void load_tco(const TestData *d)
+{
+ qpci_io_writew(d->dev, d->tco_io_base + TCO_RLD, 4);
+}
+
+static void set_tco_timeout(const TestData *d, uint16_t ticks)
+{
+ qpci_io_writew(d->dev, d->tco_io_base + TCO_TMR, ticks);
+}
+
+static void clear_tco_status(const TestData *d)
+{
+ qpci_io_writew(d->dev, d->tco_io_base + TCO1_STS, 0x0008);
+ qpci_io_writew(d->dev, d->tco_io_base + TCO2_STS, 0x0002);
+ qpci_io_writew(d->dev, d->tco_io_base + TCO2_STS, 0x0004);
+}
+
+static void reset_on_second_timeout(bool enable)
+{
+ uint32_t val;
+
+ val = readl(RCBA_BASE_ADDR + ICH9_CC_GCS);
+ if (enable) {
+ val &= ~ICH9_CC_GCS_NO_REBOOT;
+ } else {
+ val |= ICH9_CC_GCS_NO_REBOOT;
+ }
+ writel(RCBA_BASE_ADDR + ICH9_CC_GCS, val);
+}
+
+static void test_tco_defaults(void)
+{
+ TestData d;
+
+ d.args = NULL;
+ d.noreboot = true;
+ test_init(&d);
+ g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_RLD), ==,
+ TCO_RLD_DEFAULT);
+ /* TCO_DAT_IN & TCO_DAT_OUT */
+ g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_DAT_IN), ==,
+ (TCO_DAT_OUT_DEFAULT << 8) | TCO_DAT_IN_DEFAULT);
+ /* TCO1_STS & TCO2_STS */
+ g_assert_cmpint(qpci_io_readl(d.dev, d.tco_io_base + TCO1_STS), ==,
+ (TCO2_STS_DEFAULT << 16) | TCO1_STS_DEFAULT);
+ /* TCO1_CNT & TCO2_CNT */
+ g_assert_cmpint(qpci_io_readl(d.dev, d.tco_io_base + TCO1_CNT), ==,
+ (TCO2_CNT_DEFAULT << 16) | TCO1_CNT_DEFAULT);
+ /* TCO_MESSAGE1 & TCO_MESSAGE2 */
+ g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_MESSAGE1), ==,
+ (TCO_MESSAGE2_DEFAULT << 8) | TCO_MESSAGE1_DEFAULT);
+ g_assert_cmpint(qpci_io_readb(d.dev, d.tco_io_base + TCO_WDCNT), ==,
+ TCO_WDCNT_DEFAULT);
+ g_assert_cmpint(qpci_io_readb(d.dev, d.tco_io_base + SW_IRQ_GEN), ==,
+ SW_IRQ_GEN_DEFAULT);
+ g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_TMR), ==,
+ TCO_TMR_DEFAULT);
+ qtest_end();
+}
+
+static void test_tco_timeout(void)
+{
+ TestData d;
+ const uint16_t ticks = TCO_SECS_TO_TICKS(4);
+ uint32_t val;
+ int ret;
+
+ d.args = NULL;
+ d.noreboot = true;
+ test_init(&d);
+
+ stop_tco(&d);
+ clear_tco_status(&d);
+ reset_on_second_timeout(false);
+ set_tco_timeout(&d, ticks);
+ load_tco(&d);
+ start_tco(&d);
+ clock_step(ticks * TCO_TICK_NSEC);
+
+ /* test first timeout */
+ val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
+ ret = val & TCO_TIMEOUT ? 1 : 0;
+ g_assert(ret == 1);
+
+ /* test clearing timeout bit */
+ val |= TCO_TIMEOUT;
+ qpci_io_writew(d.dev, d.tco_io_base + TCO1_STS, val);
+ val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
+ ret = val & TCO_TIMEOUT ? 1 : 0;
+ g_assert(ret == 0);
+
+ /* test second timeout */
+ clock_step(ticks * TCO_TICK_NSEC);
+ val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
+ ret = val & TCO_TIMEOUT ? 1 : 0;
+ g_assert(ret == 1);
+ val = qpci_io_readw(d.dev, d.tco_io_base + TCO2_STS);
+ ret = val & TCO_SECOND_TO_STS ? 1 : 0;
+ g_assert(ret == 1);
+
+ stop_tco(&d);
+ qtest_end();
+}
+
+static void test_tco_max_timeout(void)
+{
+ TestData d;
+ const uint16_t ticks = 0xffff;
+ uint32_t val;
+ int ret;
+
+ d.args = NULL;
+ d.noreboot = true;
+ test_init(&d);
+
+ stop_tco(&d);
+ clear_tco_status(&d);
+ reset_on_second_timeout(false);
+ set_tco_timeout(&d, ticks);
+ load_tco(&d);
+ start_tco(&d);
+ clock_step(((ticks & TCO_TMR_MASK) - 1) * TCO_TICK_NSEC);
+
+ val = qpci_io_readw(d.dev, d.tco_io_base + TCO_RLD);
+ g_assert_cmpint(val & TCO_RLD_MASK, ==, 1);
+ val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
+ ret = val & TCO_TIMEOUT ? 1 : 0;
+ g_assert(ret == 0);
+ clock_step(TCO_TICK_NSEC);
+ val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
+ ret = val & TCO_TIMEOUT ? 1 : 0;
+ g_assert(ret == 1);
+
+ stop_tco(&d);
+ qtest_end();
+}
+
+static QDict *get_watchdog_action(void)
+{
+ QDict *ev = qmp("");
+ QDict *data;
+ g_assert(!strcmp(qdict_get_str(ev, "event"), "WATCHDOG"));
+
+ data = qdict_get_qdict(ev, "data");
+ QINCREF(data);
+ QDECREF(ev);
+ return data;
+}
+
+static void test_tco_second_timeout_pause(void)
+{
+ TestData td;
+ const uint16_t ticks = TCO_SECS_TO_TICKS(32);
+ QDict *ad;
+
+ td.args = "-watchdog-action pause";
+ td.noreboot = false;
+ test_init(&td);
+
+ stop_tco(&td);
+ clear_tco_status(&td);
+ reset_on_second_timeout(true);
+ set_tco_timeout(&td, TCO_SECS_TO_TICKS(16));
+ load_tco(&td);
+ start_tco(&td);
+ clock_step(ticks * TCO_TICK_NSEC * 2);
+ ad = get_watchdog_action();
+ g_assert(!strcmp(qdict_get_str(ad, "action"), "pause"));
+ QDECREF(ad);
+
+ stop_tco(&td);
+ qtest_end();
+}
+
+static void test_tco_second_timeout_reset(void)
+{
+ TestData td;
+ const uint16_t ticks = TCO_SECS_TO_TICKS(16);
+ QDict *ad;
+
+ td.args = "-watchdog-action reset";
+ td.noreboot = false;
+ test_init(&td);
+
+ stop_tco(&td);
+ clear_tco_status(&td);
+ reset_on_second_timeout(true);
+ set_tco_timeout(&td, TCO_SECS_TO_TICKS(16));
+ load_tco(&td);
+ start_tco(&td);
+ clock_step(ticks * TCO_TICK_NSEC * 2);
+ ad = get_watchdog_action();
+ g_assert(!strcmp(qdict_get_str(ad, "action"), "reset"));
+ QDECREF(ad);
+
+ stop_tco(&td);
+ qtest_end();
+}
+
+static void test_tco_second_timeout_shutdown(void)
+{
+ TestData td;
+ const uint16_t ticks = TCO_SECS_TO_TICKS(128);
+ QDict *ad;
+
+ td.args = "-watchdog-action shutdown";
+ td.noreboot = false;
+ test_init(&td);
+
+ stop_tco(&td);
+ clear_tco_status(&td);
+ reset_on_second_timeout(true);
+ set_tco_timeout(&td, ticks);
+ load_tco(&td);
+ start_tco(&td);
+ clock_step(ticks * TCO_TICK_NSEC * 2);
+ ad = get_watchdog_action();
+ g_assert(!strcmp(qdict_get_str(ad, "action"), "shutdown"));
+ QDECREF(ad);
+
+ stop_tco(&td);
+ qtest_end();
+}
+
+static void test_tco_second_timeout_none(void)
+{
+ TestData td;
+ const uint16_t ticks = TCO_SECS_TO_TICKS(256);
+ QDict *ad;
+
+ td.args = "-watchdog-action none";
+ td.noreboot = false;
+ test_init(&td);
+
+ stop_tco(&td);
+ clear_tco_status(&td);
+ reset_on_second_timeout(true);
+ set_tco_timeout(&td, ticks);
+ load_tco(&td);
+ start_tco(&td);
+ clock_step(ticks * TCO_TICK_NSEC * 2);
+ ad = get_watchdog_action();
+ g_assert(!strcmp(qdict_get_str(ad, "action"), "none"));
+ QDECREF(ad);
+
+ stop_tco(&td);
+ qtest_end();
+}
+
+static void test_tco_ticks_counter(void)
+{
+ TestData d;
+ uint16_t ticks = TCO_SECS_TO_TICKS(8);
+ uint16_t rld;
+
+ d.args = NULL;
+ d.noreboot = true;
+ test_init(&d);
+
+ stop_tco(&d);
+ clear_tco_status(&d);
+ reset_on_second_timeout(false);
+ set_tco_timeout(&d, ticks);
+ load_tco(&d);
+ start_tco(&d);
+
+ do {
+ rld = qpci_io_readw(d.dev, d.tco_io_base + TCO_RLD) & TCO_RLD_MASK;
+ g_assert_cmpint(rld, ==, ticks);
+ clock_step(TCO_TICK_NSEC);
+ ticks--;
+ } while (!(qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS) & TCO_TIMEOUT));
+
+ stop_tco(&d);
+ qtest_end();
+}
+
+static void test_tco1_control_bits(void)
+{
+ TestData d;
+ uint16_t val;
+
+ d.args = NULL;
+ d.noreboot = true;
+ test_init(&d);
+
+ val = TCO_LOCK;
+ qpci_io_writew(d.dev, d.tco_io_base + TCO1_CNT, val);
+ val &= ~TCO_LOCK;
+ qpci_io_writew(d.dev, d.tco_io_base + TCO1_CNT, val);
+ g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO1_CNT), ==,
+ TCO_LOCK);
+ qtest_end();
+}
+
+static void test_tco1_status_bits(void)
+{
+ TestData d;
+ uint16_t ticks = 8;
+ uint16_t val;
+ int ret;
+
+ d.args = NULL;
+ d.noreboot = true;
+ test_init(&d);
+
+ stop_tco(&d);
+ clear_tco_status(&d);
+ reset_on_second_timeout(false);
+ set_tco_timeout(&d, ticks);
+ load_tco(&d);
+ start_tco(&d);
+ clock_step(ticks * TCO_TICK_NSEC);
+
+ qpci_io_writeb(d.dev, d.tco_io_base + TCO_DAT_IN, 0);
+ qpci_io_writeb(d.dev, d.tco_io_base + TCO_DAT_OUT, 0);
+ val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
+ ret = val & (TCO_TIMEOUT | SW_TCO_SMI | TCO_INT_STS) ? 1 : 0;
+ g_assert(ret == 1);
+ qpci_io_writew(d.dev, d.tco_io_base + TCO1_STS, val);
+ g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS), ==, 0);
+ qtest_end();
+}
+
+static void test_tco2_status_bits(void)
+{
+ TestData d;
+ uint16_t ticks = 8;
+ uint16_t val;
+ int ret;
+
+ d.args = NULL;
+ d.noreboot = true;
+ test_init(&d);
+
+ stop_tco(&d);
+ clear_tco_status(&d);
+ reset_on_second_timeout(true);
+ set_tco_timeout(&d, ticks);
+ load_tco(&d);
+ start_tco(&d);
+ clock_step(ticks * TCO_TICK_NSEC * 2);
+
+ val = qpci_io_readw(d.dev, d.tco_io_base + TCO2_STS);
+ ret = val & (TCO_SECOND_TO_STS | TCO_BOOT_STS) ? 1 : 0;
+ g_assert(ret == 1);
+ qpci_io_writew(d.dev, d.tco_io_base + TCO2_STS, val);
+ g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO2_STS), ==, 0);
+ qtest_end();
+}
+
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+
+ qtest_add_func("tco/defaults", test_tco_defaults);
+ qtest_add_func("tco/timeout/no_action", test_tco_timeout);
+ qtest_add_func("tco/timeout/no_action/max", test_tco_max_timeout);
+ qtest_add_func("tco/second_timeout/pause", test_tco_second_timeout_pause);
+ qtest_add_func("tco/second_timeout/reset", test_tco_second_timeout_reset);
+ qtest_add_func("tco/second_timeout/shutdown",
+ test_tco_second_timeout_shutdown);
+ qtest_add_func("tco/second_timeout/none", test_tco_second_timeout_none);
+ qtest_add_func("tco/counter", test_tco_ticks_counter);
+ qtest_add_func("tco/tco1_control/bits", test_tco1_control_bits);
+ qtest_add_func("tco/tco1_status/bits", test_tco1_status_bits);
+ qtest_add_func("tco/tco2_status/bits", test_tco2_status_bits);
+ return g_test_run();
+}
diff --git a/tests/test-aio.c b/tests/test-aio.c
index a7cb5c991..217e33772 100644
--- a/tests/test-aio.c
+++ b/tests/test-aio.c
@@ -97,14 +97,6 @@ static void event_ready_cb(EventNotifier *e)
/* Tests using aio_*. */
-static void test_notify(void)
-{
- g_assert(!aio_poll(ctx, false));
- aio_notify(ctx);
- g_assert(!aio_poll(ctx, true));
- g_assert(!aio_poll(ctx, false));
-}
-
typedef struct {
QemuMutex start_lock;
bool thread_acquired;
@@ -331,7 +323,7 @@ static void test_wait_event_notifier(void)
EventNotifierTestData data = { .n = 0, .active = 1 };
event_notifier_init(&data.e, false);
aio_set_event_notifier(ctx, &data.e, event_ready_cb);
- g_assert(!aio_poll(ctx, false));
+ while (aio_poll(ctx, false));
g_assert_cmpint(data.n, ==, 0);
g_assert_cmpint(data.active, ==, 1);
@@ -356,7 +348,7 @@ static void test_flush_event_notifier(void)
EventNotifierTestData data = { .n = 0, .active = 10, .auto_set = true };
event_notifier_init(&data.e, false);
aio_set_event_notifier(ctx, &data.e, event_ready_cb);
- g_assert(!aio_poll(ctx, false));
+ while (aio_poll(ctx, false));
g_assert_cmpint(data.n, ==, 0);
g_assert_cmpint(data.active, ==, 10);
@@ -494,14 +486,6 @@ static void test_timer_schedule(void)
* works well, and that's what I am using.
*/
-static void test_source_notify(void)
-{
- while (g_main_context_iteration(NULL, false));
- aio_notify(ctx);
- g_assert(g_main_context_iteration(NULL, true));
- g_assert(!g_main_context_iteration(NULL, false));
-}
-
static void test_source_flush(void)
{
g_assert(!g_main_context_iteration(NULL, false));
@@ -669,7 +653,7 @@ static void test_source_wait_event_notifier(void)
EventNotifierTestData data = { .n = 0, .active = 1 };
event_notifier_init(&data.e, false);
aio_set_event_notifier(ctx, &data.e, event_ready_cb);
- g_assert(g_main_context_iteration(NULL, false));
+ while (g_main_context_iteration(NULL, false));
g_assert_cmpint(data.n, ==, 0);
g_assert_cmpint(data.active, ==, 1);
@@ -694,7 +678,7 @@ static void test_source_flush_event_notifier(void)
EventNotifierTestData data = { .n = 0, .active = 10, .auto_set = true };
event_notifier_init(&data.e, false);
aio_set_event_notifier(ctx, &data.e, event_ready_cb);
- g_assert(g_main_context_iteration(NULL, false));
+ while (g_main_context_iteration(NULL, false));
g_assert_cmpint(data.n, ==, 0);
g_assert_cmpint(data.active, ==, 10);
@@ -830,7 +814,6 @@ int main(int argc, char **argv)
while (g_main_context_iteration(NULL, false));
g_test_init(&argc, &argv, NULL);
- g_test_add_func("/aio/notify", test_notify);
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);
@@ -845,7 +828,6 @@ int main(int argc, char **argv)
g_test_add_func("/aio/event/flush", test_flush_event_notifier);
g_test_add_func("/aio/timer/schedule", test_timer_schedule);
- g_test_add_func("/aio-gsource/notify", test_source_notify);
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);
diff --git a/tests/test-crypto-cipher.c b/tests/test-crypto-cipher.c
new file mode 100644
index 000000000..9d38d2640
--- /dev/null
+++ b/tests/test-crypto-cipher.c
@@ -0,0 +1,302 @@
+/*
+ * 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 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 <glib.h>
+
+#include "crypto/init.h"
+#include "crypto/cipher.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",
+ },
+};
+
+
+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, *ciphertext, *plaintext, *outtext;
+ size_t nkey, niv, nciphertext, nplaintext;
+ char *outtexthex;
+
+ nkey = unhex_string(data->key, &key);
+ niv = unhex_string(data->iv, &iv);
+ nciphertext = unhex_string(data->ciphertext, &ciphertext);
+ 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,
+ &error_abort);
+ g_assert(cipher != NULL);
+
+
+ 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);
+
+ g_free(outtext);
+ g_free(outtexthex);
+ g_free(key);
+ g_free(iv);
+ g_free(ciphertext);
+ g_free(plaintext);
+ 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++) {
+ g_test_add_data_func(test_data[i].path, &test_data[i], test_cipher);
+ }
+ return g_test_run();
+}
diff --git a/tests/test-crypto-hash.c b/tests/test-crypto-hash.c
new file mode 100644
index 000000000..911437e60
--- /dev/null
+++ b/tests/test-crypto-hash.c
@@ -0,0 +1,209 @@
+/*
+ * 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 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 <glib.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_SHA256 "bc757abb0436586f392b437e5dd24096" \
+ "f7f224de6b74d4d86e2abc6121b160d0"
+
+#define OUTPUT_MD5_B64 "Yo0gY3FWMDWrjvYvSSveyQ=="
+#define OUTPUT_SHA1_B64 "sudPJnWKOkIeUJzuBFJEt4dTzAI="
+#define OUTPUT_SHA256_B64 "vHV6uwQ2WG85K0N+XdJAlvfyJN5rdNTYbiq8YSGxYNA="
+
+static const char *expected_outputs[] = {
+ [QCRYPTO_HASH_ALG_MD5] = OUTPUT_MD5,
+ [QCRYPTO_HASH_ALG_SHA1] = OUTPUT_SHA1,
+ [QCRYPTO_HASH_ALG_SHA256] = OUTPUT_SHA256,
+};
+static const char *expected_outputs_b64[] = {
+ [QCRYPTO_HASH_ALG_MD5] = OUTPUT_MD5_B64,
+ [QCRYPTO_HASH_ALG_SHA1] = OUTPUT_SHA1_B64,
+ [QCRYPTO_HASH_ALG_SHA256] = OUTPUT_SHA256_B64,
+};
+static const int expected_lens[] = {
+ [QCRYPTO_HASH_ALG_MD5] = 16,
+ [QCRYPTO_HASH_ALG_SHA1] = 20,
+ [QCRYPTO_HASH_ALG_SHA256] = 32,
+};
+
+static const char hex[] = "0123456789abcdef";
+
+/* Test with dynamic allocation */
+static void test_hash_alloc(void)
+{
+ size_t i;
+
+ g_assert(qcrypto_init(NULL) == 0);
+
+ for (i = 0; i < G_N_ELEMENTS(expected_outputs) ; i++) {
+ uint8_t *result = NULL;
+ size_t resultlen = 0;
+ int ret;
+ size_t j;
+
+ 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;
+
+ g_assert(qcrypto_init(NULL) == 0);
+
+ for (i = 0; i < G_N_ELEMENTS(expected_outputs) ; i++) {
+ uint8_t *result;
+ size_t resultlen;
+ int ret;
+ size_t j;
+
+ 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;
+
+ g_assert(qcrypto_init(NULL) == 0);
+
+ 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;
+
+ 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;
+
+ g_assert(qcrypto_init(NULL) == 0);
+
+ for (i = 0; i < G_N_ELEMENTS(expected_outputs) ; i++) {
+ int ret;
+ char *digest;
+
+ ret = qcrypto_hash_digest(i,
+ INPUT_TEXT,
+ strlen(INPUT_TEXT),
+ &digest,
+ NULL);
+ g_assert(ret == 0);
+ g_assert(g_str_equal(digest, expected_outputs[i]));
+ g_free(digest);
+ }
+}
+
+/* Test with base64 encoding */
+static void test_hash_base64(void)
+{
+ size_t i;
+
+ g_assert(qcrypto_init(NULL) == 0);
+
+ for (i = 0; i < G_N_ELEMENTS(expected_outputs) ; i++) {
+ int ret;
+ char *digest;
+
+ ret = qcrypto_hash_base64(i,
+ INPUT_TEXT,
+ strlen(INPUT_TEXT),
+ &digest,
+ NULL);
+ g_assert(ret == 0);
+ g_assert(g_str_equal(digest, expected_outputs_b64[i]));
+ g_free(digest);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ 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-hbitmap.c b/tests/test-hbitmap.c
index 8c902f205..161eeb496 100644
--- a/tests/test-hbitmap.c
+++ b/tests/test-hbitmap.c
@@ -11,6 +11,8 @@
#include <glib.h>
#include <stdarg.h>
+#include <string.h>
+#include <sys/types.h>
#include "qemu/hbitmap.h"
#define LOG_BITS_PER_LONG (BITS_PER_LONG == 32 ? 5 : 6)
@@ -23,6 +25,7 @@ typedef struct TestHBitmapData {
HBitmap *hb;
unsigned long *bits;
size_t size;
+ size_t old_size;
int granularity;
} TestHBitmapData;
@@ -91,6 +94,44 @@ static void hbitmap_test_init(TestHBitmapData *data,
}
}
+static inline size_t hbitmap_test_array_size(size_t bits)
+{
+ size_t n = (bits + BITS_PER_LONG - 1) / 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)
{
@@ -143,6 +184,23 @@ static void hbitmap_test_reset(TestHBitmapData *data,
}
}
+static void hbitmap_test_reset_all(TestHBitmapData *data)
+{
+ size_t n;
+
+ hbitmap_reset_all(data->hb);
+
+ n = (data->size + BITS_PER_LONG - 1) / 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;
@@ -323,6 +381,26 @@ static void test_hbitmap_reset(TestHBitmapData *data,
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)
{
@@ -369,6 +447,198 @@ static void test_hbitmap_iter_granularity(TestHBitmapData *data,
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 hbitmap_test_add(const char *testpath,
void (*test_func)(TestHBitmapData *data, const void *user_data))
{
@@ -394,7 +664,30 @@ int main(int argc, char **argv)
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);
g_test_run();
return 0;
diff --git a/tests/test-opts-visitor.c b/tests/test-opts-visitor.c
index ebeee5d58..1c753d982 100644
--- a/tests/test-opts-visitor.c
+++ b/tests/test-opts-visitor.c
@@ -39,7 +39,8 @@ setup_fixture(OptsVisitorFixture *f, gconstpointer test_data)
QemuOpts *opts;
OptsVisitor *ov;
- opts = qemu_opts_parse(qemu_find_opts("userdef"), opts_string, 0);
+ opts = qemu_opts_parse(qemu_find_opts("userdef"), opts_string, false,
+ NULL);
g_assert(opts != NULL);
ov = opts_visitor_new(opts);
diff --git a/tests/test-qemu-opts.c b/tests/test-qemu-opts.c
index da564923d..0c1136d1b 100644
--- a/tests/test-qemu-opts.c
+++ b/tests/test-qemu-opts.c
@@ -323,7 +323,7 @@ static void test_qemu_opt_unset(void)
int ret;
/* dynamically initialized (parsed) opts */
- opts = qemu_opts_parse(&opts_list_03, "key=value", 0);
+ opts = qemu_opts_parse(&opts_list_03, "key=value", false, NULL);
g_assert(opts != NULL);
/* check default/parsed value */
diff --git a/tests/test-qmp-commands.c b/tests/test-qmp-commands.c
index 554e222b3..9918f2306 100644
--- a/tests/test-qmp-commands.c
+++ b/tests/test-qmp-commands.c
@@ -31,14 +31,17 @@ UserDefTwo *qmp_user_def_cmd2(UserDefOne *ud1a,
ud1d->base = g_new0(UserDefZero, 1);
ud1d->base->integer = has_udb1 ? ud1b->base->integer : 0;
- ret = g_malloc0(sizeof(UserDefTwo));
- ret->string = strdup("blah1");
- ret->dict.string = strdup("blah2");
- ret->dict.dict.userdef = ud1c;
- ret->dict.dict.string = strdup("blah3");
- ret->dict.has_dict2 = true;
- ret->dict.dict2.userdef = ud1d;
- ret->dict.dict2.string = strdup("blah4");
+ 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;
}
@@ -48,6 +51,21 @@ int64_t qmp_user_def_cmd3(int64_t a, bool has_b, int64_t b, Error **errp)
return a + (has_b ? b : 0);
}
+__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->kind = ORG_QEMU_X_UNION1_KIND___ORG_QEMU_X_BRANCH;
+ ret->__org_qemu_x_branch = strdup("blah1");
+
+ return ret;
+}
+
+
/* test commands with no input and no return value */
static void test_dispatch_cmd(void)
{
@@ -120,15 +138,15 @@ static void test_dispatch_cmd_io(void)
ret = qobject_to_qdict(test_qmp_dispatch(req));
- assert(!strcmp(qdict_get_str(ret, "string"), "blah1"));
- ret_dict = qdict_get_qdict(ret, "dict");
- assert(!strcmp(qdict_get_str(ret_dict, "string"), "blah2"));
- ret_dict_dict = qdict_get_qdict(ret_dict, "dict");
+ 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, "dict2");
+ 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"));
@@ -192,7 +210,7 @@ static void test_dealloc_partial(void)
QmpInputVisitor *qiv;
ud2_dict = qdict_new();
- qdict_put_obj(ud2_dict, "string", QOBJECT(qstring_from_str(text)));
+ qdict_put_obj(ud2_dict, "string0", QOBJECT(qstring_from_str(text)));
qiv = qmp_input_visitor_new(QOBJECT(ud2_dict));
visit_type_UserDefTwo(qmp_input_get_visitor(qiv), &ud2, NULL, &err);
@@ -202,9 +220,9 @@ static void test_dealloc_partial(void)
/* verify partial success */
assert(ud2 != NULL);
- assert(ud2->string != NULL);
- assert(strcmp(ud2->string, text) == 0);
- assert(ud2->dict.dict.userdef == NULL);
+ assert(ud2->string0 != NULL);
+ assert(strcmp(ud2->string0, text) == 0);
+ assert(ud2->dict1 == NULL);
/* confirm & release construction error */
assert(err != NULL);
diff --git a/tests/test-qmp-event.c b/tests/test-qmp-event.c
index cb354e6e8..1ee40e148 100644
--- a/tests/test-qmp-event.c
+++ b/tests/test-qmp-event.c
@@ -60,8 +60,8 @@ void qdict_cmp_do_simple(const char *key, QObject *obj1, void *opaque)
switch (qobject_type(obj1)) {
case QTYPE_QBOOL:
- d->result = (qbool_get_int(qobject_to_qbool(obj1)) ==
- qbool_get_int(qobject_to_qbool(obj2)));
+ d->result = (qbool_get_bool(qobject_to_qbool(obj1)) ==
+ qbool_get_bool(qobject_to_qbool(obj2)));
return;
case QTYPE_QINT:
d->result = (qint_get_int(qobject_to_qint(obj1)) ==
diff --git a/tests/test-qmp-input-strict.c b/tests/test-qmp-input-strict.c
index d5360c6a8..68f855bdf 100644
--- a/tests/test-qmp-input-strict.c
+++ b/tests/test-qmp-input-strict.c
@@ -1,7 +1,7 @@
/*
* QMP Input Visitor unit-tests (strict mode).
*
- * Copyright (C) 2011-2012 Red Hat Inc.
+ * Copyright (C) 2011-2012, 2015 Red Hat Inc.
*
* Authors:
* Luiz Capitulino <lcapitulino@redhat.com>
@@ -116,15 +116,18 @@ static void test_validate_struct(TestInputVisitorData *data,
static void test_validate_struct_nested(TestInputVisitorData *data,
const void *unused)
{
- UserDefNested *udp = NULL;
+ UserDefTwo *udp = NULL;
Error *err = NULL;
Visitor *v;
- v = validate_test_init(data, "{ 'string0': 'string0', 'dict1': { 'string1': 'string1', 'dict2': { 'userdef1': { 'integer': 42, 'string': 'string' }, 'string2': 'string2'}}}");
+ v = validate_test_init(data, "{ 'string0': 'string0', "
+ "'dict1': { 'string1': 'string1', "
+ "'dict2': { 'userdef': { 'integer': 42, "
+ "'string': 'string' }, 'string': 'string2'}}}");
- visit_type_UserDefNested(v, &udp, NULL, &err);
+ visit_type_UserDefTwo(v, &udp, NULL, &err);
g_assert(!err);
- qapi_free_UserDefNested(udp);
+ qapi_free_UserDefTwo(udp);
}
static void test_validate_list(TestInputVisitorData *data,
@@ -141,18 +144,18 @@ static void test_validate_list(TestInputVisitorData *data,
qapi_free_UserDefOneList(head);
}
-static void test_validate_union(TestInputVisitorData *data,
- const void *unused)
+static void test_validate_union_native_list(TestInputVisitorData *data,
+ const void *unused)
{
- UserDefUnion *tmp = NULL;
+ UserDefNativeListUnion *tmp = NULL;
Visitor *v;
Error *err = NULL;
- v = validate_test_init(data, "{ 'type': 'b', 'integer': 41, 'data' : { 'integer': 42 } }");
+ v = validate_test_init(data, "{ 'type': 'integer', 'data' : [ 1, 2 ] }");
- visit_type_UserDefUnion(v, &tmp, NULL, &err);
+ visit_type_UserDefNativeListUnion(v, &tmp, NULL, &err);
g_assert(!err);
- qapi_free_UserDefUnion(tmp);
+ qapi_free_UserDefNativeListUnion(tmp);
}
static void test_validate_union_flat(TestInputVisitorData *data,
@@ -173,18 +176,18 @@ static void test_validate_union_flat(TestInputVisitorData *data,
qapi_free_UserDefFlatUnion(tmp);
}
-static void test_validate_union_anon(TestInputVisitorData *data,
- const void *unused)
+static void test_validate_alternate(TestInputVisitorData *data,
+ const void *unused)
{
- UserDefAnonUnion *tmp = NULL;
+ UserDefAlternate *tmp = NULL;
Visitor *v;
Error *err = NULL;
v = validate_test_init(data, "42");
- visit_type_UserDefAnonUnion(v, &tmp, NULL, &err);
+ visit_type_UserDefAlternate(v, &tmp, NULL, &err);
g_assert(!err);
- qapi_free_UserDefAnonUnion(tmp);
+ qapi_free_UserDefAlternate(tmp);
}
static void test_validate_fail_struct(TestInputVisitorData *data,
@@ -207,15 +210,15 @@ static void test_validate_fail_struct(TestInputVisitorData *data,
static void test_validate_fail_struct_nested(TestInputVisitorData *data,
const void *unused)
{
- UserDefNested *udp = NULL;
+ UserDefTwo *udp = NULL;
Error *err = NULL;
Visitor *v;
v = validate_test_init(data, "{ 'string0': 'string0', 'dict1': { 'string1': 'string1', 'dict2': { 'userdef1': { 'integer': 42, 'string': 'string', 'extra': [42, 23, {'foo':'bar'}] }, 'string2': 'string2'}}}");
- visit_type_UserDefNested(v, &udp, NULL, &err);
+ visit_type_UserDefTwo(v, &udp, NULL, &err);
g_assert(err);
- qapi_free_UserDefNested(udp);
+ qapi_free_UserDefTwo(udp);
}
static void test_validate_fail_list(TestInputVisitorData *data,
@@ -232,18 +235,19 @@ static void test_validate_fail_list(TestInputVisitorData *data,
qapi_free_UserDefOneList(head);
}
-static void test_validate_fail_union(TestInputVisitorData *data,
- const void *unused)
+static void test_validate_fail_union_native_list(TestInputVisitorData *data,
+ const void *unused)
{
- UserDefUnion *tmp = NULL;
+ UserDefNativeListUnion *tmp = NULL;
Error *err = NULL;
Visitor *v;
- v = validate_test_init(data, "{ 'type': 'b', 'data' : { 'integer': 42 } }");
+ v = validate_test_init(data,
+ "{ 'type': 'integer', 'data' : [ 'string' ] }");
- visit_type_UserDefUnion(v, &tmp, NULL, &err);
+ visit_type_UserDefNativeListUnion(v, &tmp, NULL, &err);
g_assert(err);
- qapi_free_UserDefUnion(tmp);
+ qapi_free_UserDefNativeListUnion(tmp);
}
static void test_validate_fail_union_flat(TestInputVisitorData *data,
@@ -275,18 +279,18 @@ static void test_validate_fail_union_flat_no_discrim(TestInputVisitorData *data,
qapi_free_UserDefFlatUnion2(tmp);
}
-static void test_validate_fail_union_anon(TestInputVisitorData *data,
- const void *unused)
+static void test_validate_fail_alternate(TestInputVisitorData *data,
+ const void *unused)
{
- UserDefAnonUnion *tmp = NULL;
+ UserDefAlternate *tmp = NULL;
Visitor *v;
Error *err = NULL;
v = validate_test_init(data, "3.14");
- visit_type_UserDefAnonUnion(v, &tmp, NULL, &err);
+ visit_type_UserDefAlternate(v, &tmp, NULL, &err);
g_assert(err);
- qapi_free_UserDefAnonUnion(tmp);
+ qapi_free_UserDefAlternate(tmp);
}
static void validate_test_add(const char *testpath,
@@ -304,31 +308,31 @@ int main(int argc, char **argv)
g_test_init(&argc, &argv, NULL);
validate_test_add("/visitor/input-strict/pass/struct",
- &testdata, test_validate_struct);
+ &testdata, test_validate_struct);
validate_test_add("/visitor/input-strict/pass/struct-nested",
- &testdata, test_validate_struct_nested);
+ &testdata, test_validate_struct_nested);
validate_test_add("/visitor/input-strict/pass/list",
- &testdata, test_validate_list);
- validate_test_add("/visitor/input-strict/pass/union",
- &testdata, test_validate_union);
+ &testdata, test_validate_list);
validate_test_add("/visitor/input-strict/pass/union-flat",
- &testdata, test_validate_union_flat);
- validate_test_add("/visitor/input-strict/pass/union-anon",
- &testdata, test_validate_union_anon);
+ &testdata, test_validate_union_flat);
+ validate_test_add("/visitor/input-strict/pass/alternate",
+ &testdata, test_validate_alternate);
+ validate_test_add("/visitor/input-strict/pass/union-native-list",
+ &testdata, test_validate_union_native_list);
validate_test_add("/visitor/input-strict/fail/struct",
- &testdata, test_validate_fail_struct);
+ &testdata, test_validate_fail_struct);
validate_test_add("/visitor/input-strict/fail/struct-nested",
- &testdata, test_validate_fail_struct_nested);
+ &testdata, test_validate_fail_struct_nested);
validate_test_add("/visitor/input-strict/fail/list",
- &testdata, test_validate_fail_list);
- validate_test_add("/visitor/input-strict/fail/union",
- &testdata, test_validate_fail_union);
+ &testdata, test_validate_fail_list);
validate_test_add("/visitor/input-strict/fail/union-flat",
- &testdata, test_validate_fail_union_flat);
+ &testdata, test_validate_fail_union_flat);
validate_test_add("/visitor/input-strict/fail/union-flat-no-discriminator",
- &testdata, test_validate_fail_union_flat_no_discrim);
- validate_test_add("/visitor/input-strict/fail/union-anon",
- &testdata, test_validate_fail_union_anon);
+ &testdata, test_validate_fail_union_flat_no_discrim);
+ validate_test_add("/visitor/input-strict/fail/alternate",
+ &testdata, test_validate_fail_alternate);
+ validate_test_add("/visitor/input-strict/fail/union-native-list",
+ &testdata, test_validate_fail_union_native_list);
g_test_run();
diff --git a/tests/test-qmp-input-visitor.c b/tests/test-qmp-input-visitor.c
index 1c8e87295..b96195309 100644
--- a/tests/test-qmp-input-visitor.c
+++ b/tests/test-qmp-input-visitor.c
@@ -1,7 +1,7 @@
/*
* QMP Input Visitor unit-tests.
*
- * Copyright (C) 2011 Red Hat Inc.
+ * Copyright (C) 2011, 2015 Red Hat Inc.
*
* Authors:
* Luiz Capitulino <lcapitulino@redhat.com>
@@ -248,23 +248,28 @@ static void check_and_free_str(char *str, const char *cmp)
static void test_visitor_in_struct_nested(TestInputVisitorData *data,
const void *unused)
{
- UserDefNested *udp = NULL;
+ 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' }, 'string2': 'string2'}}}");
+ v = visitor_input_test_init(data, "{ 'string0': 'string0', "
+ "'dict1': { 'string1': 'string1', "
+ "'dict2': { 'userdef': { 'integer': 42, "
+ "'string': 'string' }, 'string': 'string2'}}}");
- visit_type_UserDefNested(v, &udp, NULL, &err);
+ visit_type_UserDefTwo(v, &udp, NULL, &err);
g_assert(!err);
check_and_free_str(udp->string0, "string0");
- check_and_free_str(udp->dict1.string1, "string1");
- g_assert_cmpint(udp->dict1.dict2.userdef1->base->integer, ==, 42);
- check_and_free_str(udp->dict1.dict2.userdef1->string, "string");
- check_and_free_str(udp->dict1.dict2.string2, "string2");
- g_assert(udp->dict1.has_dict3 == false);
-
- g_free(udp->dict1.dict2.userdef1);
+ check_and_free_str(udp->dict1->string1, "string1");
+ g_assert_cmpint(udp->dict1->dict2->userdef->base->integer, ==, 42);
+ check_and_free_str(udp->dict1->dict2->userdef->string, "string");
+ check_and_free_str(udp->dict1->dict2->string, "string2");
+ g_assert(udp->dict1->has_dict3 == false);
+
+ g_free(udp->dict1->dict2->userdef);
+ g_free(udp->dict1->dict2);
+ g_free(udp->dict1);
g_free(udp);
}
@@ -293,23 +298,6 @@ static void test_visitor_in_list(TestInputVisitorData *data,
qapi_free_UserDefOneList(head);
}
-static void test_visitor_in_union(TestInputVisitorData *data,
- const void *unused)
-{
- Visitor *v;
- Error *err = NULL;
- UserDefUnion *tmp;
-
- v = visitor_input_test_init(data, "{ 'type': 'b', 'integer': 41, 'data' : { 'integer': 42 } }");
-
- visit_type_UserDefUnion(v, &tmp, NULL, &err);
- g_assert(err == NULL);
- g_assert_cmpint(tmp->kind, ==, USER_DEF_UNION_KIND_B);
- g_assert_cmpint(tmp->integer, ==, 41);
- g_assert_cmpint(tmp->b->integer, ==, 42);
- qapi_free_UserDefUnion(tmp);
-}
-
static void test_visitor_in_union_flat(TestInputVisitorData *data,
const void *unused)
{
@@ -332,20 +320,20 @@ static void test_visitor_in_union_flat(TestInputVisitorData *data,
qapi_free_UserDefFlatUnion(tmp);
}
-static void test_visitor_in_union_anon(TestInputVisitorData *data,
- const void *unused)
+static void test_visitor_in_alternate(TestInputVisitorData *data,
+ const void *unused)
{
Visitor *v;
Error *err = NULL;
- UserDefAnonUnion *tmp;
+ UserDefAlternate *tmp;
v = visitor_input_test_init(data, "42");
- visit_type_UserDefAnonUnion(v, &tmp, NULL, &err);
+ visit_type_UserDefAlternate(v, &tmp, NULL, &err);
g_assert(err == NULL);
- g_assert_cmpint(tmp->kind, ==, USER_DEF_ANON_UNION_KIND_I);
+ g_assert_cmpint(tmp->kind, ==, USER_DEF_ALTERNATE_KIND_I);
g_assert_cmpint(tmp->i, ==, 42);
- qapi_free_UserDefAnonUnion(tmp);
+ qapi_free_UserDefAlternate(tmp);
}
static void test_native_list_integer_helper(TestInputVisitorData *data,
@@ -670,55 +658,56 @@ int main(int argc, char **argv)
input_visitor_test_add("/visitor/input/number",
&in_visitor_data, test_visitor_in_number);
input_visitor_test_add("/visitor/input/string",
- &in_visitor_data, test_visitor_in_string);
+ &in_visitor_data, test_visitor_in_string);
input_visitor_test_add("/visitor/input/enum",
- &in_visitor_data, test_visitor_in_enum);
+ &in_visitor_data, test_visitor_in_enum);
input_visitor_test_add("/visitor/input/struct",
- &in_visitor_data, test_visitor_in_struct);
+ &in_visitor_data, test_visitor_in_struct);
input_visitor_test_add("/visitor/input/struct-nested",
- &in_visitor_data, test_visitor_in_struct_nested);
+ &in_visitor_data, test_visitor_in_struct_nested);
input_visitor_test_add("/visitor/input/list",
- &in_visitor_data, test_visitor_in_list);
- input_visitor_test_add("/visitor/input/union",
- &in_visitor_data, test_visitor_in_union);
+ &in_visitor_data, test_visitor_in_list);
input_visitor_test_add("/visitor/input/union-flat",
- &in_visitor_data, test_visitor_in_union_flat);
- input_visitor_test_add("/visitor/input/union-anon",
- &in_visitor_data, test_visitor_in_union_anon);
+ &in_visitor_data, test_visitor_in_union_flat);
+ input_visitor_test_add("/visitor/input/alternate",
+ &in_visitor_data, test_visitor_in_alternate);
input_visitor_test_add("/visitor/input/errors",
- &in_visitor_data, test_visitor_in_errors);
+ &in_visitor_data, test_visitor_in_errors);
input_visitor_test_add("/visitor/input/native_list/int",
- &in_visitor_data,
- test_visitor_in_native_list_int);
+ &in_visitor_data,
+ test_visitor_in_native_list_int);
input_visitor_test_add("/visitor/input/native_list/int8",
- &in_visitor_data,
- test_visitor_in_native_list_int8);
+ &in_visitor_data,
+ test_visitor_in_native_list_int8);
input_visitor_test_add("/visitor/input/native_list/int16",
- &in_visitor_data,
- test_visitor_in_native_list_int16);
+ &in_visitor_data,
+ test_visitor_in_native_list_int16);
input_visitor_test_add("/visitor/input/native_list/int32",
- &in_visitor_data,
- test_visitor_in_native_list_int32);
+ &in_visitor_data,
+ test_visitor_in_native_list_int32);
input_visitor_test_add("/visitor/input/native_list/int64",
- &in_visitor_data,
- test_visitor_in_native_list_int64);
+ &in_visitor_data,
+ test_visitor_in_native_list_int64);
input_visitor_test_add("/visitor/input/native_list/uint8",
- &in_visitor_data,
- test_visitor_in_native_list_uint8);
+ &in_visitor_data,
+ test_visitor_in_native_list_uint8);
input_visitor_test_add("/visitor/input/native_list/uint16",
- &in_visitor_data,
- test_visitor_in_native_list_uint16);
+ &in_visitor_data,
+ test_visitor_in_native_list_uint16);
input_visitor_test_add("/visitor/input/native_list/uint32",
- &in_visitor_data,
- test_visitor_in_native_list_uint32);
+ &in_visitor_data,
+ test_visitor_in_native_list_uint32);
input_visitor_test_add("/visitor/input/native_list/uint64",
- &in_visitor_data, test_visitor_in_native_list_uint64);
+ &in_visitor_data,
+ test_visitor_in_native_list_uint64);
input_visitor_test_add("/visitor/input/native_list/bool",
- &in_visitor_data, test_visitor_in_native_list_bool);
+ &in_visitor_data, test_visitor_in_native_list_bool);
input_visitor_test_add("/visitor/input/native_list/str",
- &in_visitor_data, test_visitor_in_native_list_string);
+ &in_visitor_data,
+ test_visitor_in_native_list_string);
input_visitor_test_add("/visitor/input/native_list/number",
- &in_visitor_data, test_visitor_in_native_list_number);
+ &in_visitor_data,
+ test_visitor_in_native_list_number);
g_test_run();
diff --git a/tests/test-qmp-output-visitor.c b/tests/test-qmp-output-visitor.c
index 74020de5e..87ba350b4 100644
--- a/tests/test-qmp-output-visitor.c
+++ b/tests/test-qmp-output-visitor.c
@@ -1,7 +1,7 @@
/*
* QMP Output Visitor unit-tests.
*
- * Copyright (C) 2011 Red Hat Inc.
+ * Copyright (C) 2011, 2015 Red Hat Inc.
*
* Authors:
* Luiz Capitulino <lcapitulino@redhat.com>
@@ -72,7 +72,7 @@ static void test_visitor_out_bool(TestOutputVisitorData *data,
obj = qmp_output_get_qobject(data->qov);
g_assert(obj != NULL);
g_assert(qobject_type(obj) == QTYPE_QBOOL);
- g_assert(qbool_get_int(qobject_to_qbool(obj)) == value);
+ g_assert(qbool_get_bool(qobject_to_qbool(obj)) == value);
qobject_decref(obj);
}
@@ -223,7 +223,7 @@ static void test_visitor_out_struct(TestOutputVisitorData *data,
qdict = qobject_to_qdict(obj);
g_assert_cmpint(qdict_size(qdict), ==, 3);
g_assert_cmpint(qdict_get_int(qdict, "integer"), ==, 42);
- g_assert_cmpint(qdict_get_bool(qdict, "boolean"), ==, 0);
+ g_assert_cmpint(qdict_get_bool(qdict, "boolean"), ==, false);
g_assert_cmpstr(qdict_get_str(qdict, "string"), ==, "foo");
QDECREF(qdict);
@@ -234,7 +234,7 @@ static void test_visitor_out_struct_nested(TestOutputVisitorData *data,
{
int64_t value = 42;
Error *err = NULL;
- UserDefNested *ud2;
+ UserDefTwo *ud2;
QObject *obj;
QDict *qdict, *dict1, *dict2, *dict3, *userdef;
const char *string = "user def string";
@@ -244,21 +244,25 @@ static void test_visitor_out_struct_nested(TestOutputVisitorData *data,
ud2 = g_malloc0(sizeof(*ud2));
ud2->string0 = g_strdup(strings[0]);
- ud2->dict1.string1 = g_strdup(strings[1]);
- ud2->dict1.dict2.userdef1 = g_malloc0(sizeof(UserDefOne));
- ud2->dict1.dict2.userdef1->string = g_strdup(string);
- ud2->dict1.dict2.userdef1->base = g_new0(UserDefZero, 1);
- ud2->dict1.dict2.userdef1->base->integer = value;
- ud2->dict1.dict2.string2 = g_strdup(strings[2]);
-
- ud2->dict1.has_dict3 = true;
- ud2->dict1.dict3.userdef2 = g_malloc0(sizeof(UserDefOne));
- ud2->dict1.dict3.userdef2->string = g_strdup(string);
- ud2->dict1.dict3.userdef2->base = g_new0(UserDefZero, 1);
- ud2->dict1.dict3.userdef2->base->integer = value;
- ud2->dict1.dict3.string3 = g_strdup(strings[3]);
-
- visit_type_UserDefNested(data->ov, &ud2, "unused", &err);
+ 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->base = g_new0(UserDefZero, 1);
+ ud2->dict1->dict2->userdef->base->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->base = g_new0(UserDefZero, 1);
+ ud2->dict1->dict3->userdef->base->integer = value;
+ ud2->dict1->dict3->string = g_strdup(strings[3]);
+
+ visit_type_UserDefTwo(data->ov, &ud2, "unused", &err);
g_assert(!err);
obj = qmp_output_get_qobject(data->qov);
@@ -275,22 +279,22 @@ static void test_visitor_out_struct_nested(TestOutputVisitorData *data,
dict2 = qdict_get_qdict(dict1, "dict2");
g_assert_cmpint(qdict_size(dict2), ==, 2);
- g_assert_cmpstr(qdict_get_str(dict2, "string2"), ==, strings[2]);
- userdef = qdict_get_qdict(dict2, "userdef1");
+ 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, "string3"), ==, strings[3]);
- userdef = qdict_get_qdict(dict3, "userdef2");
+ 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);
QDECREF(qdict);
- qapi_free_UserDefNested(ud2);
+ qapi_free_UserDefTwo(ud2);
}
static void test_visitor_out_struct_errors(TestOutputVisitorData *data,
@@ -398,7 +402,7 @@ static void test_visitor_out_list(TestOutputVisitorData *data,
static void test_visitor_out_list_qapi_free(TestOutputVisitorData *data,
const void *unused)
{
- UserDefNestedList *p, *head = NULL;
+ UserDefTwoList *p, *head = NULL;
const char string[] = "foo bar";
int i, max_count = 1024;
@@ -407,53 +411,21 @@ static void test_visitor_out_list_qapi_free(TestOutputVisitorData *data,
p->value = g_malloc0(sizeof(*p->value));
p->value->string0 = g_strdup(string);
- p->value->dict1.string1 = g_strdup(string);
- p->value->dict1.dict2.userdef1 = g_malloc0(sizeof(UserDefOne));
- p->value->dict1.dict2.userdef1->string = g_strdup(string);
- p->value->dict1.dict2.userdef1->base = g_new0(UserDefZero, 1);
- p->value->dict1.dict2.userdef1->base->integer = 42;
- p->value->dict1.dict2.string2 = g_strdup(string);
- p->value->dict1.has_dict3 = false;
+ p->value->dict1 = g_new0(UserDefTwoDict, 1);
+ p->value->dict1->string1 = g_strdup(string);
+ p->value->dict1->dict2 = g_new0(UserDefTwoDictDict, 1);
+ p->value->dict1->dict2->userdef = g_new0(UserDefOne, 1);
+ p->value->dict1->dict2->userdef->string = g_strdup(string);
+ p->value->dict1->dict2->userdef->base = g_new0(UserDefZero, 1);
+ p->value->dict1->dict2->userdef->base->integer = 42;
+ p->value->dict1->dict2->string = g_strdup(string);
+ p->value->dict1->has_dict3 = false;
p->next = head;
head = p;
}
- qapi_free_UserDefNestedList(head);
-}
-
-static void test_visitor_out_union(TestOutputVisitorData *data,
- const void *unused)
-{
- QObject *arg, *qvalue;
- QDict *qdict, *value;
-
- Error *err = NULL;
-
- UserDefUnion *tmp = g_malloc0(sizeof(UserDefUnion));
- tmp->kind = USER_DEF_UNION_KIND_A;
- tmp->integer = 41;
- tmp->a = g_malloc0(sizeof(UserDefA));
- tmp->a->boolean = true;
-
- visit_type_UserDefUnion(data->ov, &tmp, NULL, &err);
- g_assert(err == NULL);
- arg = qmp_output_get_qobject(data->qov);
-
- g_assert(qobject_type(arg) == QTYPE_QDICT);
- qdict = qobject_to_qdict(arg);
-
- g_assert_cmpstr(qdict_get_str(qdict, "type"), ==, "a");
- g_assert_cmpint(qdict_get_int(qdict, "integer"), ==, 41);
-
- qvalue = qdict_get(qdict, "data");
- g_assert(data != NULL);
- g_assert(qobject_type(qvalue) == QTYPE_QDICT);
- value = qobject_to_qdict(qvalue);
- g_assert_cmpint(qdict_get_bool(value, "boolean"), ==, true);
-
- qapi_free_UserDefUnion(tmp);
- QDECREF(qdict);
+ qapi_free_UserDefTwoList(head);
}
static void test_visitor_out_union_flat(TestOutputVisitorData *data,
@@ -487,24 +459,24 @@ static void test_visitor_out_union_flat(TestOutputVisitorData *data,
QDECREF(qdict);
}
-static void test_visitor_out_union_anon(TestOutputVisitorData *data,
- const void *unused)
+static void test_visitor_out_alternate(TestOutputVisitorData *data,
+ const void *unused)
{
QObject *arg;
Error *err = NULL;
- UserDefAnonUnion *tmp = g_malloc0(sizeof(UserDefAnonUnion));
- tmp->kind = USER_DEF_ANON_UNION_KIND_I;
+ UserDefAlternate *tmp = g_malloc0(sizeof(UserDefAlternate));
+ tmp->kind = USER_DEF_ALTERNATE_KIND_I;
tmp->i = 42;
- visit_type_UserDefAnonUnion(data->ov, &tmp, NULL, &err);
+ visit_type_UserDefAlternate(data->ov, &tmp, NULL, &err);
g_assert(err == NULL);
arg = qmp_output_get_qobject(data->qov);
g_assert(qobject_type(arg) == QTYPE_QINT);
g_assert_cmpint(qint_get_int(qobject_to_qint(arg)), ==, 42);
- qapi_free_UserDefAnonUnion(tmp);
+ qapi_free_UserDefAlternate(tmp);
}
static void test_visitor_out_empty(TestOutputVisitorData *data,
@@ -690,7 +662,7 @@ static void check_native_list(QObject *qobj,
tmp = qlist_peek(qlist);
g_assert(tmp);
qvalue = qobject_to_qbool(tmp);
- g_assert_cmpint(qbool_get_int(qvalue), ==, (i % 3 == 0) ? 1 : 0);
+ g_assert_cmpint(qbool_get_bool(qvalue), ==, i % 3 == 0);
qobject_decref(qlist_pop(qlist));
}
break;
@@ -862,38 +834,48 @@ int main(int argc, char **argv)
&out_visitor_data, test_visitor_out_list);
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",
- &out_visitor_data, test_visitor_out_union);
output_visitor_test_add("/visitor/output/union-flat",
&out_visitor_data, test_visitor_out_union_flat);
- output_visitor_test_add("/visitor/output/union-anon",
- &out_visitor_data, test_visitor_out_union_anon);
+ output_visitor_test_add("/visitor/output/alternate",
+ &out_visitor_data, test_visitor_out_alternate);
output_visitor_test_add("/visitor/output/empty",
&out_visitor_data, test_visitor_out_empty);
output_visitor_test_add("/visitor/output/native_list/int",
- &out_visitor_data, test_visitor_out_native_list_int);
+ &out_visitor_data,
+ test_visitor_out_native_list_int);
output_visitor_test_add("/visitor/output/native_list/int8",
- &out_visitor_data, test_visitor_out_native_list_int8);
+ &out_visitor_data,
+ test_visitor_out_native_list_int8);
output_visitor_test_add("/visitor/output/native_list/int16",
- &out_visitor_data, test_visitor_out_native_list_int16);
+ &out_visitor_data,
+ test_visitor_out_native_list_int16);
output_visitor_test_add("/visitor/output/native_list/int32",
- &out_visitor_data, test_visitor_out_native_list_int32);
+ &out_visitor_data,
+ test_visitor_out_native_list_int32);
output_visitor_test_add("/visitor/output/native_list/int64",
- &out_visitor_data, test_visitor_out_native_list_int64);
+ &out_visitor_data,
+ test_visitor_out_native_list_int64);
output_visitor_test_add("/visitor/output/native_list/uint8",
- &out_visitor_data, test_visitor_out_native_list_uint8);
+ &out_visitor_data,
+ test_visitor_out_native_list_uint8);
output_visitor_test_add("/visitor/output/native_list/uint16",
- &out_visitor_data, test_visitor_out_native_list_uint16);
+ &out_visitor_data,
+ test_visitor_out_native_list_uint16);
output_visitor_test_add("/visitor/output/native_list/uint32",
- &out_visitor_data, test_visitor_out_native_list_uint32);
+ &out_visitor_data,
+ test_visitor_out_native_list_uint32);
output_visitor_test_add("/visitor/output/native_list/uint64",
- &out_visitor_data, test_visitor_out_native_list_uint64);
+ &out_visitor_data,
+ test_visitor_out_native_list_uint64);
output_visitor_test_add("/visitor/output/native_list/bool",
- &out_visitor_data, test_visitor_out_native_list_bool);
+ &out_visitor_data,
+ test_visitor_out_native_list_bool);
output_visitor_test_add("/visitor/output/native_list/string",
- &out_visitor_data, test_visitor_out_native_list_str);
+ &out_visitor_data,
+ test_visitor_out_native_list_str);
output_visitor_test_add("/visitor/output/native_list/number",
- &out_visitor_data, test_visitor_out_native_list_number);
+ &out_visitor_data,
+ test_visitor_out_native_list_number);
g_test_run();
diff --git a/tests/test-rcu-list.c b/tests/test-rcu-list.c
index 4c5f62e99..daa8bf41d 100644
--- a/tests/test-rcu-list.c
+++ b/tests/test-rcu-list.c
@@ -108,6 +108,8 @@ 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;
atomic_inc(&nthreadsrunning);
while (goflag == GOFLAG_INIT) {
@@ -129,6 +131,8 @@ static void *rcu_q_reader(void *arg)
qemu_mutex_lock(&counts_mutex);
n_reads += n_reads_local;
qemu_mutex_unlock(&counts_mutex);
+
+ rcu_unregister_thread();
return NULL;
}
diff --git a/tests/test-throttle.c b/tests/test-throttle.c
index d8ba415e4..016844546 100644
--- a/tests/test-throttle.c
+++ b/tests/test-throttle.c
@@ -1,10 +1,12 @@
/*
* Throttle infrastructure tests
*
- * Copyright Nodalink, SARL. 2013
+ * Copyright Nodalink, EURL. 2013-2014
+ * Copyright Igalia, S.L. 2015
*
* Authors:
- * Benoît Canet <benoit.canet@irqsave.net>
+ * 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.
@@ -15,11 +17,13 @@
#include "block/aio.h"
#include "qemu/throttle.h"
#include "qemu/error-report.h"
+#include "block/throttle-groups.h"
static AioContext *ctx;
static LeakyBucket bkt;
static ThrottleConfig cfg;
static ThrottleState ts;
+static ThrottleTimers tt;
/* useful function */
static bool double_cmp(double x, double y)
@@ -103,17 +107,19 @@ static void test_init(void)
{
int i;
- /* fill the structure with crap */
+ /* fill the structures with crap */
memset(&ts, 1, sizeof(ts));
+ memset(&tt, 1, sizeof(tt));
- /* init the structure */
- throttle_init(&ts, ctx, QEMU_CLOCK_VIRTUAL,
- read_timer_cb, write_timer_cb, &ts);
+ /* 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(ts.clock_type == QEMU_CLOCK_VIRTUAL);
- g_assert(ts.timers[0]);
- g_assert(ts.timers[1]);
+ 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);
@@ -124,17 +130,18 @@ static void test_init(void)
g_assert(!ts.cfg.buckets[i].level);
}
- throttle_destroy(&ts);
+ throttle_timers_destroy(&tt);
}
static void test_destroy(void)
{
int i;
- throttle_init(&ts, ctx, QEMU_CLOCK_VIRTUAL,
- read_timer_cb, write_timer_cb, &ts);
- throttle_destroy(&ts);
+ 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(!ts.timers[i]);
+ g_assert(!tt.timers[i]);
}
}
@@ -170,11 +177,12 @@ static void test_config_functions(void)
orig_cfg.op_size = 1;
- throttle_init(&ts, ctx, QEMU_CLOCK_VIRTUAL,
- read_timer_cb, write_timer_cb, &ts);
+ 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, &orig_cfg);
+ throttle_config(&ts, &tt, &orig_cfg);
/* has previous leak been initialized by throttle_config ? */
g_assert(ts.previous_leak);
@@ -182,7 +190,7 @@ static void test_config_functions(void)
/* get back the fixed configuration */
throttle_get_config(&ts, &final_cfg);
- throttle_destroy(&ts);
+ throttle_timers_destroy(&tt);
g_assert(final_cfg.buckets[THROTTLE_BPS_TOTAL].avg == 153);
g_assert(final_cfg.buckets[THROTTLE_BPS_READ].avg == 56);
@@ -323,43 +331,47 @@ static void test_is_valid(void)
static void test_have_timer(void)
{
- /* zero the structure */
+ /* zero structures */
memset(&ts, 0, sizeof(ts));
+ memset(&tt, 0, sizeof(tt));
/* no timer set should return false */
- g_assert(!throttle_have_timer(&ts));
+ g_assert(!throttle_timers_are_initialized(&tt));
- /* init the structure */
- throttle_init(&ts, ctx, QEMU_CLOCK_VIRTUAL,
- read_timer_cb, write_timer_cb, &ts);
+ /* 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_have_timer(&ts));
+ g_assert(throttle_timers_are_initialized(&tt));
- throttle_destroy(&ts);
+ throttle_timers_destroy(&tt);
}
static void test_detach_attach(void)
{
- /* zero the structure */
+ /* zero structures */
memset(&ts, 0, sizeof(ts));
+ memset(&tt, 0, sizeof(tt));
/* init the structure */
- throttle_init(&ts, ctx, QEMU_CLOCK_VIRTUAL,
- read_timer_cb, write_timer_cb, &ts);
+ 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_have_timer(&ts));
+ g_assert(throttle_timers_are_initialized(&tt));
/* timer should no longer exist after detaching */
- throttle_detach_aio_context(&ts);
- g_assert(!throttle_have_timer(&ts));
+ throttle_timers_detach_aio_context(&tt);
+ g_assert(!throttle_timers_are_initialized(&tt));
/* timer should exist again after attaching */
- throttle_attach_aio_context(&ts, ctx);
- g_assert(throttle_have_timer(&ts));
+ throttle_timers_attach_aio_context(&tt, ctx);
+ g_assert(throttle_timers_are_initialized(&tt));
- throttle_destroy(&ts);
+ throttle_timers_destroy(&tt);
}
static bool do_test_accounting(bool is_ops, /* are we testing bps or ops */
@@ -387,9 +399,10 @@ static bool do_test_accounting(bool is_ops, /* are we testing bps or ops */
cfg.op_size = op_size;
- throttle_init(&ts, ctx, QEMU_CLOCK_VIRTUAL,
- read_timer_cb, write_timer_cb, &ts);
- throttle_config(&ts, &cfg);
+ throttle_init(&ts);
+ throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL,
+ read_timer_cb, write_timer_cb, &ts);
+ throttle_config(&ts, &tt, &cfg);
/* account a read */
throttle_account(&ts, false, size);
@@ -414,7 +427,7 @@ static bool do_test_accounting(bool is_ops, /* are we testing bps or ops */
return false;
}
- throttle_destroy(&ts);
+ throttle_timers_destroy(&tt);
return true;
}
@@ -490,23 +503,80 @@ static void test_accounting(void)
(64.0 / 13)));
}
+static void test_groups(void)
+{
+ ThrottleConfig cfg1, cfg2;
+ BlockDriverState *bdrv1, *bdrv2, *bdrv3;
+
+ bdrv1 = bdrv_new();
+ bdrv2 = bdrv_new();
+ bdrv3 = bdrv_new();
+
+ g_assert(bdrv1->throttle_state == NULL);
+ g_assert(bdrv2->throttle_state == NULL);
+ g_assert(bdrv3->throttle_state == NULL);
+
+ throttle_group_register_bs(bdrv1, "bar");
+ throttle_group_register_bs(bdrv2, "foo");
+ throttle_group_register_bs(bdrv3, "bar");
+
+ g_assert(bdrv1->throttle_state != NULL);
+ g_assert(bdrv2->throttle_state != NULL);
+ g_assert(bdrv3->throttle_state != NULL);
+
+ g_assert(!strcmp(throttle_group_get_name(bdrv1), "bar"));
+ g_assert(!strcmp(throttle_group_get_name(bdrv2), "foo"));
+ g_assert(bdrv1->throttle_state == bdrv3->throttle_state);
+
+ /* Setting the config of a group member affects the whole group */
+ memset(&cfg1, 0, sizeof(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(bdrv1, &cfg1);
+
+ throttle_group_get_config(bdrv1, &cfg1);
+ throttle_group_get_config(bdrv3, &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(bdrv3, &cfg1);
+
+ throttle_group_get_config(bdrv1, &cfg1);
+ throttle_group_get_config(bdrv3, &cfg2);
+ g_assert(!memcmp(&cfg1, &cfg2, sizeof(cfg1)));
+
+ throttle_group_unregister_bs(bdrv1);
+ throttle_group_unregister_bs(bdrv2);
+ throttle_group_unregister_bs(bdrv3);
+
+ g_assert(bdrv1->throttle_state == NULL);
+ g_assert(bdrv2->throttle_state == NULL);
+ g_assert(bdrv3->throttle_state == NULL);
+}
+
int main(int argc, char **argv)
{
- GSource *src;
Error *local_error = NULL;
- init_clocks();
+ qemu_init_main_loop(&local_error);
+ ctx = qemu_get_aio_context();
- ctx = aio_context_new(&local_error);
if (!ctx) {
error_report("Failed to create AIO Context: '%s'",
- error_get_pretty(local_error));
- error_free(local_error);
+ local_error ? error_get_pretty(local_error) :
+ "Failed to initialize the QEMU main loop");
+ if (local_error) {
+ error_free(local_error);
+ }
exit(1);
}
- src = aio_get_g_source(ctx);
- g_source_attach(src, NULL);
- g_source_unref(src);
+
+ bdrv_init();
do {} while (g_main_context_iteration(NULL, false));
@@ -523,6 +593,7 @@ int main(int argc, char **argv)
g_test_add_func("/throttle/config/is_valid", test_is_valid);
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-visitor-serialization.c b/tests/test-visitor-serialization.c
index 7ad188639..fa86cae88 100644
--- a/tests/test-visitor-serialization.c
+++ b/tests/test-visitor-serialization.c
@@ -1,6 +1,7 @@
/*
* Unit-tests for visitor-based serialization
*
+ * Copyright (C) 2014-2015 Red Hat, Inc.
* Copyright IBM, Corp. 2012
*
* Authors:
@@ -249,57 +250,62 @@ static void visit_struct(Visitor *v, void **native, Error **errp)
visit_type_TestStruct(v, (TestStruct **)native, NULL, errp);
}
-static UserDefNested *nested_struct_create(void)
+static UserDefTwo *nested_struct_create(void)
{
- UserDefNested *udnp = g_malloc0(sizeof(*udnp));
+ UserDefTwo *udnp = g_malloc0(sizeof(*udnp));
udnp->string0 = strdup("test_string0");
- udnp->dict1.string1 = strdup("test_string1");
- udnp->dict1.dict2.userdef1 = g_malloc0(sizeof(UserDefOne));
- udnp->dict1.dict2.userdef1->base = g_new0(UserDefZero, 1);
- udnp->dict1.dict2.userdef1->base->integer = 42;
- udnp->dict1.dict2.userdef1->string = strdup("test_string");
- udnp->dict1.dict2.string2 = strdup("test_string2");
- udnp->dict1.has_dict3 = true;
- udnp->dict1.dict3.userdef2 = g_malloc0(sizeof(UserDefOne));
- udnp->dict1.dict3.userdef2->base = g_new0(UserDefZero, 1);
- udnp->dict1.dict3.userdef2->base->integer = 43;
- udnp->dict1.dict3.userdef2->string = strdup("test_string");
- udnp->dict1.dict3.string3 = strdup("test_string3");
+ 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->base = g_new0(UserDefZero, 1);
+ udnp->dict1->dict2->userdef->base->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->base = g_new0(UserDefZero, 1);
+ udnp->dict1->dict3->userdef->base->integer = 43;
+ udnp->dict1->dict3->userdef->string = strdup("test_string");
+ udnp->dict1->dict3->string = strdup("test_string3");
return udnp;
}
-static void nested_struct_compare(UserDefNested *udnp1, UserDefNested *udnp2)
+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.userdef1->base->integer, ==,
- udnp2->dict1.dict2.userdef1->base->integer);
- g_assert_cmpstr(udnp1->dict1.dict2.userdef1->string, ==,
- udnp2->dict1.dict2.userdef1->string);
- g_assert_cmpstr(udnp1->dict1.dict2.string2, ==, udnp2->dict1.dict2.string2);
- g_assert(udnp1->dict1.has_dict3 == udnp2->dict1.has_dict3);
- g_assert_cmpint(udnp1->dict1.dict3.userdef2->base->integer, ==,
- udnp2->dict1.dict3.userdef2->base->integer);
- g_assert_cmpstr(udnp1->dict1.dict3.userdef2->string, ==,
- udnp2->dict1.dict3.userdef2->string);
- g_assert_cmpstr(udnp1->dict1.dict3.string3, ==, udnp2->dict1.dict3.string3);
+ g_assert_cmpstr(udnp1->dict1->string1, ==, udnp2->dict1->string1);
+ g_assert_cmpint(udnp1->dict1->dict2->userdef->base->integer, ==,
+ udnp2->dict1->dict2->userdef->base->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->base->integer, ==,
+ udnp2->dict1->dict3->userdef->base->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(UserDefNested *udnp)
+static void nested_struct_cleanup(UserDefTwo *udnp)
{
- qapi_free_UserDefNested(udnp);
+ qapi_free_UserDefTwo(udnp);
}
static void visit_nested_struct(Visitor *v, void **native, Error **errp)
{
- visit_type_UserDefNested(v, (UserDefNested **)native, NULL, errp);
+ visit_type_UserDefTwo(v, (UserDefTwo **)native, NULL, errp);
}
static void visit_nested_struct_list(Visitor *v, void **native, Error **errp)
{
- visit_type_UserDefNestedList(v, (UserDefNestedList **)native, NULL, errp);
+ visit_type_UserDefTwoList(v, (UserDefTwoList **)native, NULL, errp);
}
/* test cases */
@@ -715,13 +721,14 @@ static void test_nested_struct(gconstpointer opaque)
{
TestArgs *args = (TestArgs *) opaque;
const SerializeOps *ops = args->ops;
- UserDefNested *udnp = nested_struct_create();
- UserDefNested *udnp_copy = NULL;
+ UserDefTwo *udnp = nested_struct_create();
+ UserDefTwo *udnp_copy = NULL;
Error *err = NULL;
void *serialize_data;
-
+
ops->serialize(udnp, &serialize_data, visit_nested_struct, &err);
- ops->deserialize((void **)&udnp_copy, serialize_data, visit_nested_struct, &err);
+ ops->deserialize((void **)&udnp_copy, serialize_data, visit_nested_struct,
+ &err);
g_assert(err == NULL);
nested_struct_compare(udnp, udnp_copy);
@@ -737,18 +744,18 @@ static void test_nested_struct_list(gconstpointer opaque)
{
TestArgs *args = (TestArgs *) opaque;
const SerializeOps *ops = args->ops;
- UserDefNestedList *listp = NULL, *tmp, *tmp_copy, *listp_copy = NULL;
+ UserDefTwoList *listp = NULL, *tmp, *tmp_copy, *listp_copy = NULL;
Error *err = NULL;
void *serialize_data;
int i = 0;
for (i = 0; i < 8; i++) {
- tmp = g_malloc0(sizeof(UserDefNestedList));
+ tmp = g_new0(UserDefTwoList, 1);
tmp->value = nested_struct_create();
tmp->next = listp;
listp = tmp;
}
-
+
ops->serialize(listp, &serialize_data, visit_nested_struct_list, &err);
ops->deserialize((void **)&listp_copy, serialize_data,
visit_nested_struct_list, &err);
@@ -764,8 +771,8 @@ static void test_nested_struct_list(gconstpointer opaque)
listp_copy = listp_copy->next;
}
- qapi_free_UserDefNestedList(tmp);
- qapi_free_UserDefNestedList(tmp_copy);
+ qapi_free_UserDefTwoList(tmp);
+ qapi_free_UserDefTwoList(tmp_copy);
ops->cleanup(serialize_data);
g_free(args);
diff --git a/tests/virtio-scsi-test.c b/tests/virtio-scsi-test.c
index 989f8251c..11ccdd632 100644
--- a/tests/virtio-scsi-test.c
+++ b/tests/virtio-scsi-test.c
@@ -2,6 +2,7 @@
* QTest testcase for VirtIO SCSI
*
* Copyright (c) 2014 SUSE LINUX Products GmbH
+ * Copyright (c) 2015 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.
@@ -11,16 +12,187 @@
#include <string.h>
#include "libqtest.h"
#include "qemu/osdep.h"
+#include <stdio.h>
+#include "libqos/virtio.h"
+#include "libqos/virtio-pci.h"
+#include "libqos/pci-pc.h"
+#include "libqos/malloc.h"
+#include "libqos/malloc-pc.h"
+#include "libqos/malloc-generic.h"
+
+#define PCI_SLOT 0x02
+#define PCI_FN 0x00
+#define QVIRTIO_SCSI_TIMEOUT_US (1 * 1000 * 1000)
+#define CDB_SIZE 32
+
+#define MAX_NUM_QUEUES 64
+
+typedef struct {
+ QVirtioDevice *dev;
+ QGuestAllocator *alloc;
+ QPCIBus *bus;
+ int num_queues;
+ QVirtQueue *vq[MAX_NUM_QUEUES + 2];
+} QVirtIOSCSI;
+
+typedef struct {
+ uint8_t lun[8];
+ int64_t tag;
+ uint8_t task_attr;
+ uint8_t prio;
+ uint8_t crn;
+ uint8_t cdb[CDB_SIZE];
+} QEMU_PACKED QVirtIOSCSICmdReq;
+
+typedef struct {
+ uint32_t sense_len;
+ uint32_t resid;
+ uint16_t status_qualifier;
+ uint8_t status;
+ uint8_t response;
+ uint8_t sense[96];
+} QEMU_PACKED QVirtIOSCSICmdResp;
+
+static void qvirtio_scsi_start(const char *extra_opts)
+{
+ char *cmdline;
+
+ cmdline = g_strdup_printf(
+ "-drive id=drv0,if=none,file=/dev/null,format=raw "
+ "-device virtio-scsi-pci,id=vs0 "
+ "-device scsi-hd,bus=vs0.0,drive=drv0 %s",
+ extra_opts ? : "");
+ qtest_start(cmdline);
+ g_free(cmdline);
+}
+
+static void qvirtio_scsi_stop(void)
+{
+ qtest_end();
+}
+
+static QVirtIOSCSI *qvirtio_scsi_pci_init(int slot)
+{
+ QVirtIOSCSI *vs;
+ QVirtioPCIDevice *dev;
+ void *addr;
+ int i;
+
+ vs = g_new0(QVirtIOSCSI, 1);
+ vs->alloc = pc_alloc_init();
+ vs->bus = qpci_init_pc();
+
+ dev = qvirtio_pci_device_find(vs->bus, QVIRTIO_SCSI_DEVICE_ID);
+ vs->dev = (QVirtioDevice *)dev;
+ g_assert(dev != NULL);
+ g_assert_cmphex(vs->dev->device_type, ==, QVIRTIO_SCSI_DEVICE_ID);
+
+ qvirtio_pci_device_enable(dev);
+ qvirtio_reset(&qvirtio_pci, vs->dev);
+ qvirtio_set_acknowledge(&qvirtio_pci, vs->dev);
+ qvirtio_set_driver(&qvirtio_pci, vs->dev);
+
+ addr = dev->addr + QVIRTIO_PCI_DEVICE_SPECIFIC_NO_MSIX;
+ vs->num_queues = qvirtio_config_readl(&qvirtio_pci, vs->dev,
+ (uint64_t)(uintptr_t)addr);
+
+ g_assert_cmpint(vs->num_queues, <, MAX_NUM_QUEUES);
+
+ for (i = 0; i < vs->num_queues + 2; i++) {
+ vs->vq[i] = qvirtqueue_setup(&qvirtio_pci, vs->dev, vs->alloc, i);
+ }
+
+ return vs;
+}
+
+static void qvirtio_scsi_pci_free(QVirtIOSCSI *vs)
+{
+ int i;
+
+ for (i = 0; i < vs->num_queues + 2; i++) {
+ guest_free(vs->alloc, vs->vq[i]->desc);
+ }
+ pc_alloc_uninit(vs->alloc);
+ qvirtio_pci_device_disable(container_of(vs->dev, QVirtioPCIDevice, vdev));
+ g_free(vs->dev);
+ qpci_free_pc(vs->bus);
+}
+
+static uint64_t qvirtio_scsi_alloc(QVirtIOSCSI *vs, size_t alloc_size,
+ const void *data)
+{
+ uint64_t addr;
+
+ addr = guest_alloc(vs->alloc, alloc_size);
+ if (data) {
+ memwrite(addr, data, alloc_size);
+ }
+
+ return addr;
+}
+
+static uint8_t virtio_scsi_do_command(QVirtIOSCSI *vs, const uint8_t *cdb,
+ const uint8_t *data_in,
+ size_t data_in_len,
+ uint8_t *data_out, size_t data_out_len)
+{
+ QVirtQueue *vq;
+ QVirtIOSCSICmdReq req = { { 0 } };
+ QVirtIOSCSICmdResp resp = { .response = 0xff, .status = 0xff };
+ uint64_t req_addr, resp_addr, data_in_addr = 0, data_out_addr = 0;
+ uint8_t response;
+ uint32_t free_head;
+
+ vq = vs->vq[2];
+
+ req.lun[0] = 1; /* Select LUN */
+ req.lun[1] = 1; /* Select target 1 */
+ memcpy(req.cdb, cdb, CDB_SIZE);
+
+ /* XXX: Fix endian if any multi-byte field in req/resp is used */
+
+ /* Add request header */
+ req_addr = qvirtio_scsi_alloc(vs, sizeof(req), &req);
+ free_head = qvirtqueue_add(vq, req_addr, sizeof(req), false, true);
+
+ if (data_out_len) {
+ data_out_addr = qvirtio_scsi_alloc(vs, data_out_len, data_out);
+ qvirtqueue_add(vq, data_out_addr, data_out_len, false, true);
+ }
+
+ /* Add response header */
+ resp_addr = qvirtio_scsi_alloc(vs, sizeof(resp), &resp);
+ qvirtqueue_add(vq, resp_addr, sizeof(resp), true, !!data_in_len);
+
+ if (data_in_len) {
+ data_in_addr = qvirtio_scsi_alloc(vs, data_in_len, data_in);
+ qvirtqueue_add(vq, data_in_addr, data_in_len, true, false);
+ }
+
+ qvirtqueue_kick(&qvirtio_pci, vs->dev, vq, free_head);
+ qvirtio_wait_queue_isr(&qvirtio_pci, vs->dev, vq, QVIRTIO_SCSI_TIMEOUT_US);
+
+ response = readb(resp_addr + offsetof(QVirtIOSCSICmdResp, response));
+
+ guest_free(vs->alloc, req_addr);
+ guest_free(vs->alloc, resp_addr);
+ guest_free(vs->alloc, data_in_addr);
+ guest_free(vs->alloc, data_out_addr);
+ return response;
+}
/* Tests only initialization so far. TODO: Replace with functional tests */
static void pci_nop(void)
{
+ qvirtio_scsi_start(NULL);
+ qvirtio_scsi_stop();
}
static void hotplug(void)
{
QDict *response;
+ qvirtio_scsi_start("-drive id=drv1,if=none,file=/dev/null,format=raw");
response = qmp("{\"execute\": \"device_add\","
" \"arguments\": {"
" \"driver\": \"scsi-hd\","
@@ -42,6 +214,27 @@ static void hotplug(void)
g_assert(qdict_haskey(response, "event"));
g_assert(!strcmp(qdict_get_str(response, "event"), "DEVICE_DELETED"));
QDECREF(response);
+ qvirtio_scsi_stop();
+}
+
+/* Test WRITE SAME with the lba not aligned */
+static void test_unaligned_write_same(void)
+{
+ QVirtIOSCSI *vs;
+ uint8_t buf[512] = { 0 };
+ const uint8_t write_same_cdb[CDB_SIZE] = { 0x41, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x02, 0x00 };
+
+ qvirtio_scsi_start("-drive file=blkdebug::null-co://,if=none,id=dr1"
+ ",format=raw,file.align=4k "
+ "-device scsi-disk,drive=dr1,lun=0,scsi-id=1");
+ vs = qvirtio_scsi_pci_init(PCI_SLOT);
+
+ g_assert_cmphex(0, ==,
+ virtio_scsi_do_command(vs, write_same_cdb, NULL, 0, buf, 512));
+
+ qvirtio_scsi_pci_free(vs);
+ qvirtio_scsi_stop();
}
int main(int argc, char **argv)
@@ -51,14 +244,10 @@ int main(int argc, char **argv)
g_test_init(&argc, &argv, NULL);
qtest_add_func("/virtio/scsi/pci/nop", pci_nop);
qtest_add_func("/virtio/scsi/pci/hotplug", hotplug);
+ qtest_add_func("/virtio/scsi/pci/scsi-disk/unaligned-write-same",
+ test_unaligned_write_same);
- qtest_start("-drive id=drv0,if=none,file=/dev/null,format=raw "
- "-drive id=drv1,if=none,file=/dev/null,format=raw "
- "-device virtio-scsi-pci,id=vscsi0 "
- "-device scsi-hd,bus=vscsi0.0,drive=drv0");
ret = g_test_run();
- qtest_end();
-
return ret;
}
diff --git a/tests/wdt_ib700-test.c b/tests/wdt_ib700-test.c
index 513a53385..82ca59725 100644
--- a/tests/wdt_ib700-test.c
+++ b/tests/wdt_ib700-test.c
@@ -11,8 +11,7 @@
#include <string.h>
#include "libqtest.h"
#include "qemu/osdep.h"
-
-#define NS_PER_SEC 1000000000ULL
+#include "qemu/timer.h"
static void qmp_check_no_event(void)
{
@@ -41,29 +40,29 @@ static QDict *qmp_get_event(const char *name)
static QDict *ib700_program_and_wait(QTestState *s)
{
- clock_step(NS_PER_SEC * 40);
+ clock_step(NANOSECONDS_PER_SECOND * 40);
qmp_check_no_event();
/* 2 second limit */
outb(0x443, 14);
/* Ping */
- clock_step(NS_PER_SEC);
+ clock_step(NANOSECONDS_PER_SECOND);
qmp_check_no_event();
outb(0x443, 14);
/* Disable */
- clock_step(NS_PER_SEC);
+ clock_step(NANOSECONDS_PER_SECOND);
qmp_check_no_event();
outb(0x441, 1);
- clock_step(3 * NS_PER_SEC);
+ clock_step(3 * NANOSECONDS_PER_SECOND);
qmp_check_no_event();
/* Enable and let it fire */
outb(0x443, 13);
- clock_step(3 * NS_PER_SEC);
+ clock_step(3 * NANOSECONDS_PER_SECOND);
qmp_check_no_event();
- clock_step(2 * NS_PER_SEC);
+ clock_step(2 * NANOSECONDS_PER_SECOND);
return qmp_get_event("WATCHDOG");
}