summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/.gitignore3
-rw-r--r--tests/Makefile7
-rw-r--r--tests/ahci-test.c115
-rw-r--r--tests/i440fx-test.c2
-rw-r--r--tests/ivshmem-test.c6
-rw-r--r--tests/libqos/libqos.c28
-rw-r--r--tests/libqos/libqos.h1
-rw-r--r--tests/qapi-schema/qapi-schema-test.json6
-rw-r--r--tests/qapi-schema/qapi-schema-test.out5
-rw-r--r--tests/qemu-iotests/039.out30
-rwxr-xr-xtests/qemu-iotests/04030
-rw-r--r--tests/qemu-iotests/040.out4
-rwxr-xr-xtests/qemu-iotests/05812
-rw-r--r--tests/qemu-iotests/061.out12
-rwxr-xr-xtests/qemu-iotests/085120
-rw-r--r--tests/qemu-iotests/085.out38
-rwxr-xr-xtests/qemu-iotests/118720
-rw-r--r--tests/qemu-iotests/118.out5
-rw-r--r--tests/qemu-iotests/124182
-rw-r--r--tests/qemu-iotests/124.out4
-rw-r--r--tests/qemu-iotests/136349
-rw-r--r--tests/qemu-iotests/136.out5
-rw-r--r--tests/qemu-iotests/137.out6
-rw-r--r--tests/qemu-iotests/139416
-rw-r--r--tests/qemu-iotests/139.out5
-rw-r--r--tests/qemu-iotests/common9
-rw-r--r--tests/qemu-iotests/common.config32
-rw-r--r--tests/qemu-iotests/common.qemu18
-rw-r--r--tests/qemu-iotests/common.rc18
-rw-r--r--tests/qemu-iotests/group3
-rw-r--r--tests/test-blockjob-txn.c250
-rw-r--r--tests/test-qmp-commands.c3
-rw-r--r--tests/test-qmp-input-strict.c130
-rw-r--r--tests/test-qmp-input-visitor.c271
-rw-r--r--tests/test-qmp-output-visitor.c151
-rw-r--r--tests/test-timed-average.c90
-rw-r--r--tests/test-visitor-serialization.c76
-rw-r--r--tests/vhost-user-bridge.c6
-rw-r--r--tests/vhost-user-test.c12
39 files changed, 2650 insertions, 530 deletions
diff --git a/tests/.gitignore b/tests/.gitignore
index 65496aa..1e55722 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -9,6 +9,7 @@ check-qom-proplist
rcutorture
test-aio
test-bitops
+test-blockjob-txn
test-coroutine
test-crypto-cipher
test-crypto-hash
@@ -30,6 +31,7 @@ test-qapi-types.[ch]
test-qapi-visit.[ch]
test-qdev-global-props
test-qemu-opts
+test-qga
test-qmp-commands
test-qmp-commands.h
test-qmp-event
@@ -44,6 +46,7 @@ test-string-input-visitor
test-string-output-visitor
test-thread-pool
test-throttle
+test-timed-average
test-visitor-serialization
test-vmstate
test-write-threshold
diff --git a/tests/Makefile b/tests/Makefile
index 92969e8..90c4141 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -47,6 +47,8 @@ check-unit-y += tests/test-thread-pool$(EXESUF)
gcov-files-test-thread-pool-y = thread-pool.c
gcov-files-test-hbitmap-y = util/hbitmap.c
check-unit-y += tests/test-hbitmap$(EXESUF)
+gcov-files-test-hbitmap-y = blockjob.c
+check-unit-y += tests/test-blockjob-txn$(EXESUF)
check-unit-y += tests/test-x86-cpuid$(EXESUF)
# all code tested by test-x86-cpuid is inside topology.h
gcov-files-test-x86-cpuid-y =
@@ -81,6 +83,7 @@ check-unit-y += tests/test-crypto-cipher$(EXESUF)
check-unit-$(CONFIG_GNUTLS) += tests/test-crypto-tlscredsx509$(EXESUF)
check-unit-$(CONFIG_GNUTLS) += tests/test-crypto-tlssession$(EXESUF)
check-unit-$(CONFIG_LINUX) += tests/test-qga$(EXESUF)
+check-unit-y += tests/test-timed-average$(EXESUF)
check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh
@@ -390,6 +393,7 @@ tests/test-coroutine$(EXESUF): tests/test-coroutine.o $(test-block-obj-y)
tests/test-aio$(EXESUF): tests/test-aio.o $(test-block-obj-y)
tests/test-rfifolock$(EXESUF): tests/test-rfifolock.o $(test-util-obj-y)
tests/test-throttle$(EXESUF): tests/test-throttle.o $(test-block-obj-y)
+tests/test-blockjob-txn$(EXESUF): tests/test-blockjob-txn.o $(test-block-obj-y) $(test-util-obj-y)
tests/test-thread-pool$(EXESUF): tests/test-thread-pool.o $(test-block-obj-y)
tests/test-iov$(EXESUF): tests/test-iov.o $(test-util-obj-y)
tests/test-hbitmap$(EXESUF): tests/test-hbitmap.o $(test-util-obj-y)
@@ -409,6 +413,9 @@ tests/test-vmstate$(EXESUF): tests/test-vmstate.o \
migration/vmstate.o migration/qemu-file.o migration/qemu-file-buf.o \
migration/qemu-file-unix.o qjson.o \
$(test-qom-obj-y)
+tests/test-timed-average$(EXESUF): tests/test-timed-average.o qemu-timer.o \
+ libqemuutil.a stubs/clock-warp.o stubs/cpu-get-icount.o \
+ stubs/notify-event.o stubs/replay.o
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 $(qapi-py)
diff --git a/tests/ahci-test.c b/tests/ahci-test.c
index 59d387c..0888506 100644
--- a/tests/ahci-test.c
+++ b/tests/ahci-test.c
@@ -39,15 +39,17 @@
#include "hw/pci/pci_ids.h"
#include "hw/pci/pci_regs.h"
-/* 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)
+/* Test images sizes in MB */
+#define TEST_IMAGE_SIZE_MB_LARGE (200 * 1024)
+#define TEST_IMAGE_SIZE_MB_SMALL 64
/*** Globals ***/
static char tmp_path[] = "/tmp/qtest.XXXXXX";
static char debug_path[] = "/tmp/qtest-blkdebug.XXXXXX";
+static char mig_socket[] = "/tmp/qtest-migration.XXXXXX";
static bool ahci_pedantic;
+static const char *imgfmt;
+static unsigned test_image_size_mb;
/*** Function Declarations ***/
static void ahci_test_port_spec(AHCIQState *ahci, uint8_t port);
@@ -60,6 +62,11 @@ static void ahci_test_pmcap(AHCIQState *ahci, uint8_t offset);
/*** Utilities ***/
+static uint64_t mb_to_sectors(uint64_t image_size_mb)
+{
+ return (image_size_mb * 1024 * 1024) / AHCI_SECTOR_SIZE;
+}
+
static void string_bswap16(uint16_t *s, size_t bytes)
{
g_assert_cmphex((bytes & 1), ==, 0);
@@ -114,8 +121,11 @@ static void ahci_migrate(AHCIQState *from, AHCIQState *to, const char *uri)
{
QOSState *tmp = to->parent;
QPCIDevice *dev = to->dev;
+ char *uri_local = NULL;
+
if (uri == NULL) {
- uri = "tcp:127.0.0.1:1234";
+ uri_local = g_strdup_printf("%s%s", "unix:", mig_socket);
+ uri = uri_local;
}
/* context will be 'to' after completion. */
@@ -135,6 +145,7 @@ static void ahci_migrate(AHCIQState *from, AHCIQState *to, const char *uri)
from->dev = dev;
verify_state(to);
+ g_free(uri_local);
}
/*** Test Setup & Teardown ***/
@@ -170,11 +181,11 @@ static AHCIQState *ahci_boot(const char *cli, ...)
va_end(ap);
} else {
cli = "-drive if=none,id=drive0,file=%s,cache=writeback,serial=%s"
- ",format=qcow2"
+ ",format=%s"
" -M q35 "
"-device ide-hd,drive=drive0 "
"-global ide-hd.ver=%s";
- s = ahci_boot(cli, tmp_path, "testdisk", "version");
+ s = ahci_boot(cli, tmp_path, "testdisk", imgfmt, "version");
}
return s;
@@ -900,7 +911,7 @@ static void ahci_test_max(AHCIQState *ahci)
uint64_t nsect;
uint8_t port;
uint8_t cmd;
- uint64_t config_sect = TEST_IMAGE_SECTORS - 1;
+ uint64_t config_sect = mb_to_sectors(test_image_size_mb) - 1;
if (config_sect > 0xFFFFFF) {
cmd = CMD_READ_MAX_EXT;
@@ -1073,12 +1084,12 @@ static void test_flush_retry(void)
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,"
+ "format=%s,cache=writeback,"
"rerror=stop,werror=stop "
"-M q35 "
"-device ide-hd,drive=drive0 ",
debug_path,
- tmp_path);
+ tmp_path, imgfmt);
/* Issue Flush Command and wait for error */
port = ahci_port_select(ahci);
@@ -1105,18 +1116,19 @@ static void test_flush_retry(void)
static void test_migrate_sanity(void)
{
AHCIQState *src, *dst;
- const char *uri = "tcp:127.0.0.1:1234";
+ char *uri = g_strdup_printf("unix:%s", mig_socket);
src = ahci_boot("-m 1024 -M q35 "
- "-hda %s ", tmp_path);
+ "-drive if=ide,file=%s,format=%s ", tmp_path, imgfmt);
dst = ahci_boot("-m 1024 -M q35 "
- "-hda %s "
- "-incoming %s", tmp_path, uri);
+ "-drive if=ide,file=%s,format=%s "
+ "-incoming %s", tmp_path, imgfmt, uri);
ahci_migrate(src, dst, uri);
ahci_shutdown(src);
ahci_shutdown(dst);
+ g_free(uri);
}
/**
@@ -1129,13 +1141,14 @@ static void ahci_migrate_simple(uint8_t cmd_read, uint8_t cmd_write)
size_t bufsize = 4096;
unsigned char *tx = g_malloc(bufsize);
unsigned char *rx = g_malloc0(bufsize);
- const char *uri = "tcp:127.0.0.1:1234";
+ char *uri = g_strdup_printf("unix:%s", mig_socket);
src = ahci_boot_and_enable("-m 1024 -M q35 "
- "-hda %s ", tmp_path);
+ "-drive if=ide,format=%s,file=%s ",
+ imgfmt, tmp_path);
dst = ahci_boot("-m 1024 -M q35 "
- "-hda %s "
- "-incoming %s", tmp_path, uri);
+ "-drive if=ide,format=%s,file=%s "
+ "-incoming %s", imgfmt, tmp_path, uri);
set_context(src->parent);
@@ -1158,6 +1171,7 @@ static void ahci_migrate_simple(uint8_t cmd_read, uint8_t cmd_write)
ahci_shutdown(dst);
g_free(rx);
g_free(tx);
+ g_free(uri);
}
static void test_migrate_dma(void)
@@ -1190,12 +1204,12 @@ static void ahci_halted_io_test(uint8_t cmd_read, uint8_t cmd_write)
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,"
+ "format=%s,cache=writeback,"
"rerror=stop,werror=stop "
"-M q35 "
"-device ide-hd,drive=drive0 ",
debug_path,
- tmp_path);
+ tmp_path, imgfmt);
/* Initialize and prepare */
port = ahci_port_select(ahci);
@@ -1251,25 +1265,25 @@ static void ahci_migrate_halted_io(uint8_t cmd_read, uint8_t cmd_write)
unsigned char *rx = g_malloc0(bufsize);
uint64_t ptr;
AHCICommand *cmd;
- const char *uri = "tcp:127.0.0.1:1234";
+ char *uri = g_strdup_printf("unix:%s", mig_socket);
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,"
+ "format=%s,cache=writeback,"
"rerror=stop,werror=stop "
"-M q35 "
"-device ide-hd,drive=drive0 ",
debug_path,
- tmp_path);
+ tmp_path, imgfmt);
dst = ahci_boot("-drive file=%s,if=none,id=drive0,"
- "format=qcow2,cache=writeback,"
+ "format=%s,cache=writeback,"
"rerror=stop,werror=stop "
"-M q35 "
"-device ide-hd,drive=drive0 "
"-incoming %s",
- tmp_path, uri);
+ tmp_path, imgfmt, uri);
set_context(src->parent);
@@ -1301,6 +1315,7 @@ static void ahci_migrate_halted_io(uint8_t cmd_read, uint8_t cmd_write)
ahci_shutdown(dst);
g_free(rx);
g_free(tx);
+ g_free(uri);
}
static void test_migrate_halted_dma(void)
@@ -1322,20 +1337,22 @@ static void test_flush_migrate(void)
AHCICommand *cmd;
uint8_t px;
const char *s;
- const char *uri = "tcp:127.0.0.1:1234";
+ char *uri = g_strdup_printf("unix:%s", mig_socket);
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 "
+ "cache=writeback,rerror=stop,werror=stop,"
+ "format=%s "
"-M q35 "
"-device ide-hd,drive=drive0 ",
- debug_path, tmp_path);
+ debug_path, tmp_path, imgfmt);
dst = ahci_boot("-drive file=%s,if=none,id=drive0,"
- "cache=writeback,rerror=stop,werror=stop "
+ "cache=writeback,rerror=stop,werror=stop,"
+ "format=%s "
"-M q35 "
"-device ide-hd,drive=drive0 "
- "-incoming %s", tmp_path, uri);
+ "-incoming %s", tmp_path, imgfmt, uri);
set_context(src->parent);
@@ -1360,6 +1377,7 @@ static void test_flush_migrate(void)
ahci_command_free(cmd);
ahci_shutdown(src);
ahci_shutdown(dst);
+ g_free(uri);
}
static void test_max(void)
@@ -1476,7 +1494,7 @@ static uint64_t offset_sector(enum OffsetType ofst,
return 1;
case OFFSET_HIGH:
ceil = (addr_type == ADDR_MODE_LBA28) ? 0xfffffff : 0xffffffffffff;
- ceil = MIN(ceil, TEST_IMAGE_SECTORS - 1);
+ ceil = MIN(ceil, mb_to_sectors(test_image_size_mb) - 1);
nsectors = buffsize / AHCI_SECTOR_SIZE;
return ceil - nsectors + 1;
default:
@@ -1558,8 +1576,9 @@ static void create_ahci_io_test(enum IOMode type, enum AddrMode addr,
enum BuffLen len, enum OffsetType offset)
{
char *name;
- AHCIIOTestOptions *opts = g_malloc(sizeof(AHCIIOTestOptions));
+ AHCIIOTestOptions *opts;
+ opts = g_malloc(sizeof(AHCIIOTestOptions));
opts->length = len;
opts->address_type = addr;
opts->io_type = type;
@@ -1571,6 +1590,13 @@ static void create_ahci_io_test(enum IOMode type, enum AddrMode addr,
buff_len_str[len],
offset_str[offset]);
+ if ((addr == ADDR_MODE_LBA48) && (offset == OFFSET_HIGH) &&
+ (mb_to_sectors(test_image_size_mb) <= 0xFFFFFFF)) {
+ g_test_message("%s: skipped; test image too small", name);
+ g_free(name);
+ return;
+ }
+
qtest_add_data_func(name, opts, test_io_interface);
g_free(name);
}
@@ -1617,15 +1643,33 @@ int main(int argc, char **argv)
return 0;
}
- /* Create a temporary qcow2 image */
- close(mkstemp(tmp_path));
- mkqcow2(tmp_path, TEST_IMAGE_SIZE_MB);
+ /* Create a temporary image */
+ fd = mkstemp(tmp_path);
+ g_assert(fd >= 0);
+ if (have_qemu_img()) {
+ imgfmt = "qcow2";
+ test_image_size_mb = TEST_IMAGE_SIZE_MB_LARGE;
+ mkqcow2(tmp_path, TEST_IMAGE_SIZE_MB_LARGE);
+ } else {
+ g_test_message("QTEST_QEMU_IMG not set or qemu-img missing; "
+ "skipping LBA48 high-sector tests");
+ imgfmt = "raw";
+ test_image_size_mb = TEST_IMAGE_SIZE_MB_SMALL;
+ ret = ftruncate(fd, test_image_size_mb * 1024 * 1024);
+ g_assert(ret == 0);
+ }
+ close(fd);
/* Create temporary blkdebug instructions */
fd = mkstemp(debug_path);
g_assert(fd >= 0);
close(fd);
+ /* Reserve a hollow file to use as a socket for migration tests */
+ fd = mkstemp(mig_socket);
+ g_assert(fd >= 0);
+ close(fd);
+
/* Run the tests */
qtest_add_func("/ahci/sanity", test_sanity);
qtest_add_func("/ahci/pci_spec", test_pci_spec);
@@ -1668,6 +1712,7 @@ int main(int argc, char **argv)
/* Cleanup */
unlink(tmp_path);
unlink(debug_path);
+ unlink(mig_socket);
return ret;
}
diff --git a/tests/i440fx-test.c b/tests/i440fx-test.c
index d0bc8de..7fa1709 100644
--- a/tests/i440fx-test.c
+++ b/tests/i440fx-test.c
@@ -191,7 +191,7 @@ static void write_area(uint32_t start, uint32_t end, uint8_t value)
uint32_t size = end - start + 1;
uint8_t *data;
- data = g_malloc0(size);
+ data = g_malloc(size);
memset(data, value, size);
memwrite(start, data, size);
diff --git a/tests/ivshmem-test.c b/tests/ivshmem-test.c
index c8f0cf0..f1793ba 100644
--- a/tests/ivshmem-test.c
+++ b/tests/ivshmem-test.c
@@ -478,10 +478,12 @@ int main(int argc, char **argv)
tmpserver = g_strconcat(tmpdir, "/server", NULL);
qtest_add_func("/ivshmem/single", test_ivshmem_single);
- qtest_add_func("/ivshmem/pair", test_ivshmem_pair);
- qtest_add_func("/ivshmem/server", test_ivshmem_server);
qtest_add_func("/ivshmem/hotplug", test_ivshmem_hotplug);
qtest_add_func("/ivshmem/memdev", test_ivshmem_memdev);
+ if (g_test_slow()) {
+ qtest_add_func("/ivshmem/pair", test_ivshmem_pair);
+ qtest_add_func("/ivshmem/server", test_ivshmem_server);
+ }
ret = g_test_run();
diff --git a/tests/libqos/libqos.c b/tests/libqos/libqos.c
index 8d7c5a9..2d1a802 100644
--- a/tests/libqos/libqos.c
+++ b/tests/libqos/libqos.c
@@ -147,6 +147,23 @@ void migrate(QOSState *from, QOSState *to, const char *uri)
set_context(to);
}
+bool have_qemu_img(void)
+{
+ char *rpath;
+ const char *path = getenv("QTEST_QEMU_IMG");
+ if (!path) {
+ return false;
+ }
+
+ rpath = realpath(path, NULL);
+ if (!rpath) {
+ return false;
+ } else {
+ free(rpath);
+ return true;
+ }
+}
+
void mkimg(const char *file, const char *fmt, unsigned size_mb)
{
gchar *cli;
@@ -155,13 +172,14 @@ void mkimg(const char *file, const char *fmt, unsigned size_mb)
GError *err = NULL;
char *qemu_img_path;
gchar *out, *out2;
- char *abs_path;
+ char *qemu_img_abs_path;
qemu_img_path = getenv("QTEST_QEMU_IMG");
- abs_path = realpath(qemu_img_path, NULL);
- assert(qemu_img_path);
+ g_assert(qemu_img_path);
+ qemu_img_abs_path = realpath(qemu_img_path, NULL);
+ g_assert(qemu_img_abs_path);
- cli = g_strdup_printf("%s create -f %s %s %uM", abs_path,
+ cli = g_strdup_printf("%s create -f %s %s %uM", qemu_img_abs_path,
fmt, file, size_mb);
ret = g_spawn_command_line_sync(cli, &out, &out2, &rc, &err);
if (err) {
@@ -183,7 +201,7 @@ void mkimg(const char *file, const char *fmt, unsigned size_mb)
g_free(out);
g_free(out2);
g_free(cli);
- free(abs_path);
+ free(qemu_img_abs_path);
}
void mkqcow2(const char *file, unsigned size_mb)
diff --git a/tests/libqos/libqos.h b/tests/libqos/libqos.h
index 492a651..ca14d2e 100644
--- a/tests/libqos/libqos.h
+++ b/tests/libqos/libqos.h
@@ -19,6 +19,7 @@ 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);
+bool have_qemu_img(void);
void mkimg(const char *file, const char *fmt, unsigned size_mb);
void mkqcow2(const char *file, unsigned size_mb);
void set_context(QOSState *s);
diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
index 48e104b..44638da 100644
--- a/tests/qapi-schema/qapi-schema-test.json
+++ b/tests/qapi-schema/qapi-schema-test.json
@@ -3,6 +3,9 @@
# This file is a stress test of supported qapi constructs that must
# parse and compile correctly.
+{ 'struct': 'TestStruct',
+ 'data': { 'integer': 'int', 'boolean': 'bool', 'string': 'str' } }
+
# for testing enums
{ 'struct': 'NestedEnumsOne',
'data': { 'enum1': 'EnumOne', # Intentional forward reference
@@ -46,7 +49,8 @@
# dummy struct to force generation of array types not otherwise mentioned
{ 'struct': 'ForceArrays',
- 'data': { 'unused1':['UserDefOne'], 'unused2':['UserDefTwo'] } }
+ 'data': { 'unused1':['UserDefOne'], 'unused2':['UserDefTwo'],
+ 'unused3':['TestStruct'] } }
# for testing unions
# Among other things, test that a name collision between branches does
diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out
index a7e9aab..e20a823 100644
--- a/tests/qapi-schema/qapi-schema-test.out
+++ b/tests/qapi-schema/qapi-schema-test.out
@@ -92,6 +92,7 @@ object EventStructOne
object ForceArrays
member unused1: UserDefOneList optional=False
member unused2: UserDefTwoList optional=False
+ member unused3: TestStructList optional=False
enum MyEnum []
object NestedEnumsOne
member enum1: EnumOne optional=False
@@ -100,6 +101,10 @@ object NestedEnumsOne
member enum4: EnumOne optional=True
enum QEnumTwo ['value1', 'value2']
prefix QENUM_TWO
+object TestStruct
+ member integer: int optional=False
+ member boolean: bool optional=False
+ member string: str optional=False
object UserDefA
member boolean: bool optional=False
member a_b: int optional=True
diff --git a/tests/qemu-iotests/039.out b/tests/qemu-iotests/039.out
index 03a31c5..32c8846 100644
--- a/tests/qemu-iotests/039.out
+++ b/tests/qemu-iotests/039.out
@@ -11,7 +11,11 @@ No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
wrote 512/512 bytes at offset 0
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-./common.config: Killed ( exec "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@" )
+./common.config: Killed ( if [ "${VALGRIND_QEMU}" == "y" ]; then
+ exec valgrind --log-file="${VALGRIND_LOGFILE}" --error-exitcode=99 "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@";
+else
+ exec "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@";
+fi )
incompatible_features 0x1
ERROR cluster 5 refcount=0 reference=1
ERROR OFLAG_COPIED data cluster: l2_entry=8000000000050000 refcount=0
@@ -46,7 +50,11 @@ read 512/512 bytes at offset 0
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
wrote 512/512 bytes at offset 0
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-./common.config: Killed ( exec "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@" )
+./common.config: Killed ( if [ "${VALGRIND_QEMU}" == "y" ]; then
+ exec valgrind --log-file="${VALGRIND_LOGFILE}" --error-exitcode=99 "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@";
+else
+ exec "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@";
+fi )
incompatible_features 0x1
ERROR cluster 5 refcount=0 reference=1
Rebuilding refcount structure
@@ -60,7 +68,11 @@ incompatible_features 0x0
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
wrote 512/512 bytes at offset 0
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-./common.config: Killed ( exec "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@" )
+./common.config: Killed ( if [ "${VALGRIND_QEMU}" == "y" ]; then
+ exec valgrind --log-file="${VALGRIND_LOGFILE}" --error-exitcode=99 "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@";
+else
+ exec "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@";
+fi )
incompatible_features 0x0
No errors were found on the image.
@@ -79,7 +91,11 @@ No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
wrote 512/512 bytes at offset 0
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-./common.config: Killed ( exec "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@" )
+./common.config: Killed ( if [ "${VALGRIND_QEMU}" == "y" ]; then
+ exec valgrind --log-file="${VALGRIND_LOGFILE}" --error-exitcode=99 "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@";
+else
+ exec "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@";
+fi )
incompatible_features 0x1
ERROR cluster 5 refcount=0 reference=1
ERROR OFLAG_COPIED data cluster: l2_entry=8000000000050000 refcount=0
@@ -89,7 +105,11 @@ Data may be corrupted, or further writes to the image may corrupt it.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
wrote 512/512 bytes at offset 0
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-./common.config: Killed ( exec "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@" )
+./common.config: Killed ( if [ "${VALGRIND_QEMU}" == "y" ]; then
+ exec valgrind --log-file="${VALGRIND_LOGFILE}" --error-exitcode=99 "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@";
+else
+ exec "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@";
+fi )
incompatible_features 0x0
No errors were found on the image.
*** done
diff --git a/tests/qemu-iotests/040 b/tests/qemu-iotests/040
index ea2f98e..5bdaf3d 100755
--- a/tests/qemu-iotests/040
+++ b/tests/qemu-iotests/040
@@ -41,6 +41,7 @@ class ImageCommitTestCase(iotests.QMPTestCase):
while not completed:
for event in self.vm.get_qmp_events(wait=True):
if event['event'] == 'BLOCK_JOB_COMPLETED':
+ self.assert_qmp_absent(event, 'data/error')
self.assert_qmp(event, 'data/type', 'commit')
self.assert_qmp(event, 'data/device', 'drive0')
self.assert_qmp(event, 'data/offset', event['data']['len'])
@@ -251,5 +252,34 @@ class TestSetSpeed(ImageCommitTestCase):
class TestActiveZeroLengthImage(TestSingleDrive):
image_len = 0
+class TestReopenOverlay(ImageCommitTestCase):
+ image_len = 1024 * 1024
+ img0 = os.path.join(iotests.test_dir, '0.img')
+ img1 = os.path.join(iotests.test_dir, '1.img')
+ img2 = os.path.join(iotests.test_dir, '2.img')
+ img3 = os.path.join(iotests.test_dir, '3.img')
+
+ def setUp(self):
+ iotests.create_image(self.img0, self.image_len)
+ qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % self.img0, self.img1)
+ qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % self.img1, self.img2)
+ qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % self.img2, self.img3)
+ qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0xab 0 128K', self.img1)
+ self.vm = iotests.VM().add_drive(self.img3)
+ self.vm.launch()
+
+ def tearDown(self):
+ self.vm.shutdown()
+ os.remove(self.img0)
+ os.remove(self.img1)
+ os.remove(self.img2)
+ os.remove(self.img3)
+
+ # This tests what happens when the overlay image of the 'top' node
+ # needs to be reopened in read-write mode in order to update the
+ # backing image string.
+ def test_reopen_overlay(self):
+ self.run_commit_test(self.img1, self.img0)
+
if __name__ == '__main__':
iotests.main(supported_fmts=['qcow2', 'qed'])
diff --git a/tests/qemu-iotests/040.out b/tests/qemu-iotests/040.out
index 42314e9..4fd1c2d 100644
--- a/tests/qemu-iotests/040.out
+++ b/tests/qemu-iotests/040.out
@@ -1,5 +1,5 @@
-........................
+.........................
----------------------------------------------------------------------
-Ran 24 tests
+Ran 25 tests
OK
diff --git a/tests/qemu-iotests/058 b/tests/qemu-iotests/058
index f2bdd0b..63a6598 100755
--- a/tests/qemu-iotests/058
+++ b/tests/qemu-iotests/058
@@ -32,11 +32,17 @@ status=1 # failure is the default!
nbd_unix_socket=$TEST_DIR/test_qemu_nbd_socket
nbd_snapshot_img="nbd:unix:$nbd_unix_socket"
+rm -f "${TEST_DIR}/qemu-nbd.pid"
_cleanup_nbd()
{
- if [ -n "$NBD_SNAPSHOT_PID" ]; then
- kill "$NBD_SNAPSHOT_PID"
+ local NBD_SNAPSHOT_PID
+ if [ -f "${TEST_DIR}/qemu-nbd.pid" ]; then
+ read NBD_SNAPSHOT_PID < "${TEST_DIR}/qemu-nbd.pid"
+ rm -f "${TEST_DIR}/qemu-nbd.pid"
+ if [ -n "$NBD_SNAPSHOT_PID" ]; then
+ kill "$NBD_SNAPSHOT_PID"
+ fi
fi
rm -f "$nbd_unix_socket"
}
@@ -60,7 +66,6 @@ _export_nbd_snapshot()
{
_cleanup_nbd
$QEMU_NBD -v -t -k "$nbd_unix_socket" "$TEST_IMG" -l $1 &
- NBD_SNAPSHOT_PID=$!
_wait_for_nbd
}
@@ -68,7 +73,6 @@ _export_nbd_snapshot1()
{
_cleanup_nbd
$QEMU_NBD -v -t -k "$nbd_unix_socket" "$TEST_IMG" -l snapshot.name=$1 &
- NBD_SNAPSHOT_PID=$!
_wait_for_nbd
}
diff --git a/tests/qemu-iotests/061.out b/tests/qemu-iotests/061.out
index b16bea9..f2598a8 100644
--- a/tests/qemu-iotests/061.out
+++ b/tests/qemu-iotests/061.out
@@ -57,7 +57,11 @@ No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
wrote 131072/131072 bytes at offset 0
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-./common.config: Killed ( exec "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@" )
+./common.config: Killed ( if [ "${VALGRIND_QEMU}" == "y" ]; then
+ exec valgrind --log-file="${VALGRIND_LOGFILE}" --error-exitcode=99 "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@";
+else
+ exec "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@";
+fi )
magic 0x514649fb
version 3
backing_file_offset 0x0
@@ -215,7 +219,11 @@ No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
wrote 131072/131072 bytes at offset 0
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-./common.config: Killed ( exec "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@" )
+./common.config: Killed ( if [ "${VALGRIND_QEMU}" == "y" ]; then
+ exec valgrind --log-file="${VALGRIND_LOGFILE}" --error-exitcode=99 "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@";
+else
+ exec "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@";
+fi )
magic 0x514649fb
version 3
backing_file_offset 0x0
diff --git a/tests/qemu-iotests/085 b/tests/qemu-iotests/085
index 56cd6f8..aa77eca 100755
--- a/tests/qemu-iotests/085
+++ b/tests/qemu-iotests/085
@@ -7,6 +7,7 @@
# snapshots are performed.
#
# Copyright (C) 2014 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
@@ -34,17 +35,17 @@ status=1 # failure is the default!
snapshot_virt0="snapshot-v0.qcow2"
snapshot_virt1="snapshot-v1.qcow2"
-MAX_SNAPSHOTS=10
+SNAPSHOTS=10
_cleanup()
{
_cleanup_qemu
- for i in $(seq 1 ${MAX_SNAPSHOTS})
+ for i in $(seq 1 ${SNAPSHOTS})
do
rm -f "${TEST_DIR}/${i}-${snapshot_virt0}"
rm -f "${TEST_DIR}/${i}-${snapshot_virt1}"
done
- _cleanup_test_img
+ rm -f "${TEST_IMG}.1" "${TEST_IMG}.2"
}
trap "_cleanup; exit \$status" 0 1 2 3 15
@@ -64,7 +65,7 @@ function create_single_snapshot()
{
cmd="{ 'execute': 'blockdev-snapshot-sync',
'arguments': { 'device': 'virtio0',
- 'snapshot-file':'"${TEST_DIR}/${1}-${snapshot_virt0}"',
+ 'snapshot-file':'${TEST_DIR}/${1}-${snapshot_virt0}',
'format': 'qcow2' } }"
_send_qemu_cmd $h "${cmd}" "return"
}
@@ -76,27 +77,60 @@ function create_group_snapshot()
{'actions': [
{ 'type': 'blockdev-snapshot-sync', 'data' :
{ 'device': 'virtio0',
- 'snapshot-file': '"${TEST_DIR}/${1}-${snapshot_virt0}"' } },
+ 'snapshot-file': '${TEST_DIR}/${1}-${snapshot_virt0}' } },
{ 'type': 'blockdev-snapshot-sync', 'data' :
{ 'device': 'virtio1',
- 'snapshot-file': '"${TEST_DIR}/${1}-${snapshot_virt1}"' } } ]
+ 'snapshot-file': '${TEST_DIR}/${1}-${snapshot_virt1}' } } ]
} }"
_send_qemu_cmd $h "${cmd}" "return"
}
+# ${1}: unique identifier for the snapshot filename
+# ${2}: true: open backing images; false: don't open them (default)
+function add_snapshot_image()
+{
+ if [ "${2}" = "true" ]; then
+ extra_params=""
+ else
+ extra_params="'backing': '', "
+ fi
+ base_image="${TEST_DIR}/$((${1}-1))-${snapshot_virt0}"
+ snapshot_file="${TEST_DIR}/${1}-${snapshot_virt0}"
+ _make_test_img -b "${base_image}" "$size"
+ mv "${TEST_IMG}" "${snapshot_file}"
+ cmd="{ 'execute': 'blockdev-add', 'arguments':
+ { 'options':
+ { 'driver': 'qcow2', 'node-name': 'snap_${1}', ${extra_params}
+ 'file':
+ { 'driver': 'file', 'filename': '${snapshot_file}',
+ 'node-name': 'file_${1}' } } } }"
+ _send_qemu_cmd $h "${cmd}" "return"
+}
+
+# ${1}: unique identifier for the snapshot filename
+# ${2}: expected response, defaults to 'return'
+function blockdev_snapshot()
+{
+ cmd="{ 'execute': 'blockdev-snapshot',
+ 'arguments': { 'node': 'virtio0',
+ 'overlay':'snap_${1}' } }"
+ _send_qemu_cmd $h "${cmd}" "${2:-return}"
+}
+
size=128M
_make_test_img $size
-mv "${TEST_IMG}" "${TEST_IMG}.orig"
+mv "${TEST_IMG}" "${TEST_IMG}.1"
_make_test_img $size
+mv "${TEST_IMG}" "${TEST_IMG}.2"
echo
echo === Running QEMU ===
echo
qemu_comm_method="qmp"
-_launch_qemu -drive file="${TEST_IMG}.orig",if=virtio -drive file="${TEST_IMG}",if=virtio
+_launch_qemu -drive file="${TEST_IMG}.1",if=virtio -drive file="${TEST_IMG}.2",if=virtio
h=$QEMU_HANDLE
echo
@@ -105,6 +139,8 @@ echo
_send_qemu_cmd $h "{ 'execute': 'qmp_capabilities' }" "return"
+# Tests for the blockdev-snapshot-sync command
+
echo
echo === Create a single snapshot on virtio0 ===
echo
@@ -117,7 +153,7 @@ echo === Invalid command - missing device and nodename ===
echo
_send_qemu_cmd $h "{ 'execute': 'blockdev-snapshot-sync',
- 'arguments': { 'snapshot-file':'"${TEST_DIR}/1-${snapshot_virt0}"',
+ 'arguments': { 'snapshot-file':'${TEST_DIR}/1-${snapshot_virt0}',
'format': 'qcow2' } }" "error"
echo
@@ -132,11 +168,75 @@ echo
echo === Create several transactional group snapshots ===
echo
-for i in $(seq 2 ${MAX_SNAPSHOTS})
+for i in $(seq 2 ${SNAPSHOTS})
do
create_group_snapshot ${i}
done
+# Tests for the blockdev-snapshot command
+
+echo
+echo === Create a couple of snapshots using blockdev-snapshot ===
+echo
+
+SNAPSHOTS=$((${SNAPSHOTS}+1))
+add_snapshot_image ${SNAPSHOTS}
+blockdev_snapshot ${SNAPSHOTS}
+
+SNAPSHOTS=$((${SNAPSHOTS}+1))
+add_snapshot_image ${SNAPSHOTS}
+blockdev_snapshot ${SNAPSHOTS}
+
+echo
+echo === Invalid command - cannot create a snapshot using a file BDS ===
+echo
+
+_send_qemu_cmd $h "{ 'execute': 'blockdev-snapshot',
+ 'arguments': { 'node':'virtio0',
+ 'overlay':'file_${SNAPSHOTS}' }
+ }" "error"
+
+echo
+echo === Invalid command - snapshot node used as active layer ===
+echo
+
+blockdev_snapshot ${SNAPSHOTS} error
+
+_send_qemu_cmd $h "{ 'execute': 'blockdev-snapshot',
+ 'arguments': { 'node':'virtio0',
+ 'overlay':'virtio0' }
+ }" "error"
+
+_send_qemu_cmd $h "{ 'execute': 'blockdev-snapshot',
+ 'arguments': { 'node':'virtio0',
+ 'overlay':'virtio1' }
+ }" "error"
+
+echo
+echo === Invalid command - snapshot node used as backing hd ===
+echo
+
+blockdev_snapshot $((${SNAPSHOTS}-1)) error
+
+echo
+echo === Invalid command - snapshot node has a backing image ===
+echo
+
+SNAPSHOTS=$((${SNAPSHOTS}+1))
+add_snapshot_image ${SNAPSHOTS} true
+blockdev_snapshot ${SNAPSHOTS} error
+
+echo
+echo === Invalid command - The node does not exist ===
+echo
+
+blockdev_snapshot $((${SNAPSHOTS}+1)) error
+
+_send_qemu_cmd $h "{ 'execute': 'blockdev-snapshot',
+ 'arguments': { 'node':'nodevice',
+ 'overlay':'snap_${SNAPSHOTS}' }
+ }" "error"
+
# success, all done
echo "*** done"
rm -f $seq.full
diff --git a/tests/qemu-iotests/085.out b/tests/qemu-iotests/085.out
index a6cf19e..01c78d6 100644
--- a/tests/qemu-iotests/085.out
+++ b/tests/qemu-iotests/085.out
@@ -11,7 +11,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
=== Create a single snapshot on virtio0 ===
-Formatting 'TEST_DIR/1-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/t.qcow2.orig backing_fmt=qcow2 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
+Formatting 'TEST_DIR/1-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/t.qcow2.1 backing_fmt=qcow2 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
{"return": {}}
=== Invalid command - missing device and nodename ===
@@ -26,7 +26,7 @@ Formatting 'TEST_DIR/1-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file
=== Create several transactional group snapshots ===
Formatting 'TEST_DIR/2-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/1-snapshot-v0.qcow2 backing_fmt=qcow2 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
-Formatting 'TEST_DIR/2-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/t.qcow2 backing_fmt=qcow2 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
+Formatting 'TEST_DIR/2-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/t.qcow2.2 backing_fmt=qcow2 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
{"return": {}}
Formatting 'TEST_DIR/3-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/2-snapshot-v0.qcow2 backing_fmt=qcow2 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
Formatting 'TEST_DIR/3-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/2-snapshot-v1.qcow2 backing_fmt=qcow2 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
@@ -52,4 +52,38 @@ Formatting 'TEST_DIR/9-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file
Formatting 'TEST_DIR/10-snapshot-v0.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/9-snapshot-v0.qcow2 backing_fmt=qcow2 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
Formatting 'TEST_DIR/10-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/9-snapshot-v1.qcow2 backing_fmt=qcow2 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16
{"return": {}}
+
+=== Create a couple of snapshots using blockdev-snapshot ===
+
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/10-snapshot-v0.IMGFMT
+{"return": {}}
+{"return": {}}
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/11-snapshot-v0.IMGFMT
+{"return": {}}
+{"return": {}}
+
+=== Invalid command - cannot create a snapshot using a file BDS ===
+
+{"error": {"class": "GenericError", "desc": "The snapshot does not support backing images"}}
+
+=== Invalid command - snapshot node used as active layer ===
+
+{"error": {"class": "GenericError", "desc": "The snapshot is already in use by virtio0"}}
+{"error": {"class": "GenericError", "desc": "The snapshot is already in use by virtio0"}}
+{"error": {"class": "GenericError", "desc": "The snapshot is already in use by virtio1"}}
+
+=== Invalid command - snapshot node used as backing hd ===
+
+{"error": {"class": "GenericError", "desc": "Node 'snap_11' is busy: node is used as backing hd of 'virtio0'"}}
+
+=== Invalid command - snapshot node has a backing image ===
+
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/12-snapshot-v0.IMGFMT
+{"return": {}}
+{"error": {"class": "GenericError", "desc": "The snapshot already has a backing image"}}
+
+=== Invalid command - The node does not exist ===
+
+{"error": {"class": "GenericError", "desc": "Cannot find device=snap_14 nor node_name=snap_14"}}
+{"error": {"class": "GenericError", "desc": "Cannot find device=nodevice nor node_name=nodevice"}}
*** done
diff --git a/tests/qemu-iotests/118 b/tests/qemu-iotests/118
new file mode 100755
index 0000000..a2bcd54
--- /dev/null
+++ b/tests/qemu-iotests/118
@@ -0,0 +1,720 @@
+#!/usr/bin/env python
+#
+# Test case for the QMP 'change' command and all other associated
+# commands
+#
+# 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 stat
+import time
+import iotests
+from iotests import qemu_img
+
+old_img = os.path.join(iotests.test_dir, 'test0.img')
+new_img = os.path.join(iotests.test_dir, 'test1.img')
+
+class ChangeBaseClass(iotests.QMPTestCase):
+ has_opened = False
+ has_closed = False
+
+ def process_events(self):
+ for event in self.vm.get_qmp_events(wait=False):
+ if (event['event'] == 'DEVICE_TRAY_MOVED' and
+ event['data']['device'] == 'drive0'):
+ if event['data']['tray-open'] == False:
+ self.has_closed = True
+ else:
+ self.has_opened = True
+
+ def wait_for_open(self):
+ timeout = time.clock() + 3
+ while not self.has_opened and time.clock() < timeout:
+ self.process_events()
+ if not self.has_opened:
+ self.fail('Timeout while waiting for the tray to open')
+
+ def wait_for_close(self):
+ timeout = time.clock() + 3
+ while not self.has_closed and time.clock() < timeout:
+ self.process_events()
+ if not self.has_opened:
+ self.fail('Timeout while waiting for the tray to close')
+
+class GeneralChangeTestsBaseClass(ChangeBaseClass):
+ def test_change(self):
+ result = self.vm.qmp('change', device='drive0', target=new_img,
+ arg=iotests.imgfmt)
+ self.assert_qmp(result, 'return', {})
+
+ self.wait_for_open()
+ self.wait_for_close()
+
+ result = self.vm.qmp('query-block')
+ self.assert_qmp(result, 'return[0]/tray_open', False)
+ self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
+
+ def test_blockdev_change_medium(self):
+ result = self.vm.qmp('blockdev-change-medium', device='drive0',
+ filename=new_img,
+ format=iotests.imgfmt)
+ self.assert_qmp(result, 'return', {})
+
+ self.wait_for_open()
+ self.wait_for_close()
+
+ result = self.vm.qmp('query-block')
+ self.assert_qmp(result, 'return[0]/tray_open', False)
+ self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
+
+ def test_eject(self):
+ result = self.vm.qmp('eject', device='drive0', force=True)
+ self.assert_qmp(result, 'return', {})
+
+ self.wait_for_open()
+
+ result = self.vm.qmp('query-block')
+ self.assert_qmp(result, 'return[0]/tray_open', True)
+ self.assert_qmp_absent(result, 'return[0]/inserted')
+
+ def test_tray_eject_change(self):
+ result = self.vm.qmp('eject', device='drive0', force=True)
+ self.assert_qmp(result, 'return', {})
+
+ self.wait_for_open()
+
+ result = self.vm.qmp('query-block')
+ self.assert_qmp(result, 'return[0]/tray_open', True)
+ self.assert_qmp_absent(result, 'return[0]/inserted')
+
+ result = self.vm.qmp('blockdev-change-medium', device='drive0',
+ filename=new_img,
+ format=iotests.imgfmt)
+ self.assert_qmp(result, 'return', {})
+
+ self.wait_for_close()
+
+ result = self.vm.qmp('query-block')
+ self.assert_qmp(result, 'return[0]/tray_open', False)
+ self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
+
+ def test_tray_open_close(self):
+ result = self.vm.qmp('blockdev-open-tray', device='drive0', force=True)
+ self.assert_qmp(result, 'return', {})
+
+ self.wait_for_open()
+
+ result = self.vm.qmp('query-block')
+ self.assert_qmp(result, 'return[0]/tray_open', True)
+ if self.was_empty == True:
+ self.assert_qmp_absent(result, 'return[0]/inserted')
+ else:
+ self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
+
+ result = self.vm.qmp('blockdev-close-tray', device='drive0')
+ self.assert_qmp(result, 'return', {})
+
+ if self.has_real_tray or not self.was_empty:
+ self.wait_for_close()
+
+ result = self.vm.qmp('query-block')
+ if self.has_real_tray or not self.was_empty:
+ self.assert_qmp(result, 'return[0]/tray_open', False)
+ else:
+ self.assert_qmp(result, 'return[0]/tray_open', True)
+ if self.was_empty == True:
+ self.assert_qmp_absent(result, 'return[0]/inserted')
+ else:
+ self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
+
+ def test_tray_eject_close(self):
+ result = self.vm.qmp('eject', device='drive0', force=True)
+ self.assert_qmp(result, 'return', {})
+
+ self.wait_for_open()
+
+ result = self.vm.qmp('query-block')
+ self.assert_qmp(result, 'return[0]/tray_open', True)
+ self.assert_qmp_absent(result, 'return[0]/inserted')
+
+ result = self.vm.qmp('blockdev-close-tray', device='drive0')
+ self.assert_qmp(result, 'return', {})
+
+ if self.has_real_tray:
+ self.wait_for_close()
+
+ result = self.vm.qmp('query-block')
+ if self.has_real_tray:
+ self.assert_qmp(result, 'return[0]/tray_open', False)
+ else:
+ self.assert_qmp(result, 'return[0]/tray_open', True)
+ self.assert_qmp_absent(result, 'return[0]/inserted')
+
+ def test_tray_open_change(self):
+ result = self.vm.qmp('blockdev-open-tray', device='drive0', force=True)
+ self.assert_qmp(result, 'return', {})
+
+ self.wait_for_open()
+
+ result = self.vm.qmp('query-block')
+ self.assert_qmp(result, 'return[0]/tray_open', True)
+ if self.was_empty == True:
+ self.assert_qmp_absent(result, 'return[0]/inserted')
+ else:
+ self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
+
+ result = self.vm.qmp('blockdev-change-medium', device='drive0',
+ filename=new_img,
+ format=iotests.imgfmt)
+ self.assert_qmp(result, 'return', {})
+
+ self.wait_for_close()
+
+ result = self.vm.qmp('query-block')
+ self.assert_qmp(result, 'return[0]/tray_open', False)
+ self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
+
+ def test_cycle(self):
+ result = self.vm.qmp('blockdev-add',
+ options={'node-name': 'new',
+ 'driver': iotests.imgfmt,
+ 'file': {'filename': new_img,
+ 'driver': 'file'}})
+ self.assert_qmp(result, 'return', {})
+
+ result = self.vm.qmp('blockdev-open-tray', device='drive0', force=True)
+ self.assert_qmp(result, 'return', {})
+
+ self.wait_for_open()
+
+ result = self.vm.qmp('query-block')
+ self.assert_qmp(result, 'return[0]/tray_open', True)
+ if self.was_empty == True:
+ self.assert_qmp_absent(result, 'return[0]/inserted')
+ else:
+ self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
+
+ result = self.vm.qmp('blockdev-remove-medium', device='drive0')
+ self.assert_qmp(result, 'return', {})
+
+ result = self.vm.qmp('query-block')
+ self.assert_qmp(result, 'return[0]/tray_open', True)
+ self.assert_qmp_absent(result, 'return[0]/inserted')
+
+ result = self.vm.qmp('blockdev-insert-medium', device='drive0',
+ node_name='new')
+ self.assert_qmp(result, 'return', {})
+
+ result = self.vm.qmp('query-block')
+ self.assert_qmp(result, 'return[0]/tray_open', True)
+ self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
+
+ result = self.vm.qmp('blockdev-close-tray', device='drive0')
+ self.assert_qmp(result, 'return', {})
+
+ self.wait_for_close()
+
+ result = self.vm.qmp('query-block')
+ self.assert_qmp(result, 'return[0]/tray_open', False)
+ self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
+
+ def test_close_on_closed(self):
+ result = self.vm.qmp('blockdev-close-tray', device='drive0')
+ # Should be a no-op
+ self.assert_qmp(result, 'return', {})
+ self.assertEquals(self.vm.get_qmp_events(wait=False), [])
+
+ def test_remove_on_closed(self):
+ if self.has_opened:
+ # Empty floppy drive
+ return
+
+ result = self.vm.qmp('blockdev-remove-medium', device='drive0')
+ self.assert_qmp(result, 'error/class', 'GenericError')
+
+ def test_insert_on_closed(self):
+ if self.has_opened:
+ # Empty floppy drive
+ return
+
+ result = self.vm.qmp('blockdev-add',
+ options={'node-name': 'new',
+ 'driver': iotests.imgfmt,
+ 'file': {'filename': new_img,
+ 'driver': 'file'}})
+ self.assert_qmp(result, 'return', {})
+
+ result = self.vm.qmp('blockdev-insert-medium', device='drive0',
+ node_name='new')
+ self.assert_qmp(result, 'error/class', 'GenericError')
+
+class TestInitiallyFilled(GeneralChangeTestsBaseClass):
+ was_empty = False
+
+ def setUp(self, media, interface):
+ qemu_img('create', '-f', iotests.imgfmt, old_img, '1440k')
+ qemu_img('create', '-f', iotests.imgfmt, new_img, '1440k')
+ self.vm = iotests.VM().add_drive(old_img, 'media=%s' % media, interface)
+ self.vm.launch()
+
+ def tearDown(self):
+ self.vm.shutdown()
+ os.remove(old_img)
+ os.remove(new_img)
+
+ def test_insert_on_filled(self):
+ result = self.vm.qmp('blockdev-add',
+ options={'node-name': 'new',
+ 'driver': iotests.imgfmt,
+ 'file': {'filename': new_img,
+ 'driver': 'file'}})
+ self.assert_qmp(result, 'return', {})
+
+ result = self.vm.qmp('blockdev-open-tray', device='drive0')
+ self.assert_qmp(result, 'return', {})
+
+ self.wait_for_open()
+
+ result = self.vm.qmp('blockdev-insert-medium', device='drive0',
+ node_name='new')
+ self.assert_qmp(result, 'error/class', 'GenericError')
+
+class TestInitiallyEmpty(GeneralChangeTestsBaseClass):
+ was_empty = True
+
+ def setUp(self, media, interface):
+ qemu_img('create', '-f', iotests.imgfmt, new_img, '1440k')
+ self.vm = iotests.VM().add_drive(None, 'media=%s' % media, interface)
+ self.vm.launch()
+
+ def tearDown(self):
+ self.vm.shutdown()
+ os.remove(new_img)
+
+ def test_remove_on_empty(self):
+ result = self.vm.qmp('blockdev-open-tray', device='drive0')
+ self.assert_qmp(result, 'return', {})
+
+ self.wait_for_open()
+
+ result = self.vm.qmp('blockdev-remove-medium', device='drive0')
+ # Should be a no-op
+ self.assert_qmp(result, 'return', {})
+
+class TestCDInitiallyFilled(TestInitiallyFilled):
+ TestInitiallyFilled = TestInitiallyFilled
+ has_real_tray = True
+
+ def setUp(self):
+ self.TestInitiallyFilled.setUp(self, 'cdrom', 'ide')
+
+class TestCDInitiallyEmpty(TestInitiallyEmpty):
+ TestInitiallyEmpty = TestInitiallyEmpty
+ has_real_tray = True
+
+ def setUp(self):
+ self.TestInitiallyEmpty.setUp(self, 'cdrom', 'ide')
+
+class TestFloppyInitiallyFilled(TestInitiallyFilled):
+ TestInitiallyFilled = TestInitiallyFilled
+ has_real_tray = False
+
+ def setUp(self):
+ self.TestInitiallyFilled.setUp(self, 'disk', 'floppy')
+
+class TestFloppyInitiallyEmpty(TestInitiallyEmpty):
+ TestInitiallyEmpty = TestInitiallyEmpty
+ has_real_tray = False
+
+ def setUp(self):
+ self.TestInitiallyEmpty.setUp(self, 'disk', 'floppy')
+ # FDDs not having a real tray and there not being a medium inside the
+ # tray at startup means the tray will be considered open
+ self.has_opened = True
+
+class TestChangeReadOnly(ChangeBaseClass):
+ def setUp(self):
+ qemu_img('create', '-f', iotests.imgfmt, old_img, '1440k')
+ qemu_img('create', '-f', iotests.imgfmt, new_img, '1440k')
+ self.vm = iotests.VM()
+
+ def tearDown(self):
+ self.vm.shutdown()
+ os.chmod(old_img, 0666)
+ os.chmod(new_img, 0666)
+ os.remove(old_img)
+ os.remove(new_img)
+
+ def test_ro_ro_retain(self):
+ os.chmod(old_img, 0444)
+ os.chmod(new_img, 0444)
+ self.vm.add_drive(old_img, 'media=disk,read-only=on', 'floppy')
+ self.vm.launch()
+
+ result = self.vm.qmp('query-block')
+ self.assert_qmp(result, 'return[0]/tray_open', False)
+ self.assert_qmp(result, 'return[0]/inserted/ro', True)
+ self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
+
+ result = self.vm.qmp('blockdev-change-medium', device='drive0',
+ filename=new_img,
+ format=iotests.imgfmt,
+ read_only_mode='retain')
+ self.assert_qmp(result, 'return', {})
+
+ self.wait_for_open()
+ self.wait_for_close()
+
+ result = self.vm.qmp('query-block')
+ self.assert_qmp(result, 'return[0]/tray_open', False)
+ self.assert_qmp(result, 'return[0]/inserted/ro', True)
+ self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
+
+ def test_ro_rw_retain(self):
+ os.chmod(old_img, 0444)
+ self.vm.add_drive(old_img, 'media=disk,read-only=on', 'floppy')
+ self.vm.launch()
+
+ result = self.vm.qmp('query-block')
+ self.assert_qmp(result, 'return[0]/tray_open', False)
+ self.assert_qmp(result, 'return[0]/inserted/ro', True)
+ self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
+
+ result = self.vm.qmp('blockdev-change-medium', device='drive0',
+ filename=new_img,
+ format=iotests.imgfmt,
+ read_only_mode='retain')
+ self.assert_qmp(result, 'return', {})
+
+ self.wait_for_open()
+ self.wait_for_close()
+
+ result = self.vm.qmp('query-block')
+ self.assert_qmp(result, 'return[0]/tray_open', False)
+ self.assert_qmp(result, 'return[0]/inserted/ro', True)
+ self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
+
+ def test_rw_ro_retain(self):
+ os.chmod(new_img, 0444)
+ self.vm.add_drive(old_img, 'media=disk', 'floppy')
+ self.vm.launch()
+
+ result = self.vm.qmp('query-block')
+ self.assert_qmp(result, 'return[0]/tray_open', False)
+ self.assert_qmp(result, 'return[0]/inserted/ro', False)
+ self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
+
+ result = self.vm.qmp('blockdev-change-medium', device='drive0',
+ filename=new_img,
+ format=iotests.imgfmt,
+ read_only_mode='retain')
+ self.assert_qmp(result, 'error/class', 'GenericError')
+
+ self.assertEquals(self.vm.get_qmp_events(wait=False), [])
+
+ result = self.vm.qmp('query-block')
+ self.assert_qmp(result, 'return[0]/tray_open', False)
+ self.assert_qmp(result, 'return[0]/inserted/ro', False)
+ self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
+
+ def test_ro_rw(self):
+ os.chmod(old_img, 0444)
+ self.vm.add_drive(old_img, 'media=disk,read-only=on', 'floppy')
+ self.vm.launch()
+
+ result = self.vm.qmp('query-block')
+ self.assert_qmp(result, 'return[0]/tray_open', False)
+ self.assert_qmp(result, 'return[0]/inserted/ro', True)
+ self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
+
+ result = self.vm.qmp('blockdev-change-medium',
+ device='drive0',
+ filename=new_img,
+ format=iotests.imgfmt,
+ read_only_mode='read-write')
+ self.assert_qmp(result, 'return', {})
+
+ self.wait_for_open()
+ self.wait_for_close()
+
+ result = self.vm.qmp('query-block')
+ self.assert_qmp(result, 'return[0]/tray_open', False)
+ self.assert_qmp(result, 'return[0]/inserted/ro', False)
+ self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
+
+ def test_rw_ro(self):
+ os.chmod(new_img, 0444)
+ self.vm.add_drive(old_img, 'media=disk', 'floppy')
+ self.vm.launch()
+
+ result = self.vm.qmp('query-block')
+ self.assert_qmp(result, 'return[0]/tray_open', False)
+ self.assert_qmp(result, 'return[0]/inserted/ro', False)
+ self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
+
+ result = self.vm.qmp('blockdev-change-medium',
+ device='drive0',
+ filename=new_img,
+ format=iotests.imgfmt,
+ read_only_mode='read-only')
+ self.assert_qmp(result, 'return', {})
+
+ self.wait_for_open()
+ self.wait_for_close()
+
+ result = self.vm.qmp('query-block')
+ self.assert_qmp(result, 'return[0]/tray_open', False)
+ self.assert_qmp(result, 'return[0]/inserted/ro', True)
+ self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
+
+ def test_make_rw_ro(self):
+ self.vm.add_drive(old_img, 'media=disk', 'floppy')
+ self.vm.launch()
+
+ result = self.vm.qmp('query-block')
+ self.assert_qmp(result, 'return[0]/tray_open', False)
+ self.assert_qmp(result, 'return[0]/inserted/ro', False)
+ self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
+
+ result = self.vm.qmp('blockdev-change-medium',
+ device='drive0',
+ filename=new_img,
+ format=iotests.imgfmt,
+ read_only_mode='read-only')
+ self.assert_qmp(result, 'return', {})
+
+ self.wait_for_open()
+ self.wait_for_close()
+
+ result = self.vm.qmp('query-block')
+ self.assert_qmp(result, 'return[0]/tray_open', False)
+ self.assert_qmp(result, 'return[0]/inserted/ro', True)
+ self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
+
+ def test_make_ro_rw(self):
+ os.chmod(new_img, 0444)
+ self.vm.add_drive(old_img, 'media=disk', 'floppy')
+ self.vm.launch()
+
+ result = self.vm.qmp('query-block')
+ self.assert_qmp(result, 'return[0]/tray_open', False)
+ self.assert_qmp(result, 'return[0]/inserted/ro', False)
+ self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
+
+ result = self.vm.qmp('blockdev-change-medium',
+ device='drive0',
+ filename=new_img,
+ format=iotests.imgfmt,
+ read_only_mode='read-write')
+ self.assert_qmp(result, 'error/class', 'GenericError')
+
+ self.assertEquals(self.vm.get_qmp_events(wait=False), [])
+
+ result = self.vm.qmp('query-block')
+ self.assert_qmp(result, 'return[0]/tray_open', False)
+ self.assert_qmp(result, 'return[0]/inserted/ro', False)
+ self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
+
+ def test_make_rw_ro_by_retain(self):
+ os.chmod(old_img, 0444)
+ self.vm.add_drive(old_img, 'media=disk,read-only=on', 'floppy')
+ self.vm.launch()
+
+ result = self.vm.qmp('query-block')
+ self.assert_qmp(result, 'return[0]/tray_open', False)
+ self.assert_qmp(result, 'return[0]/inserted/ro', True)
+ self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
+
+ result = self.vm.qmp('blockdev-change-medium', device='drive0',
+ filename=new_img,
+ format=iotests.imgfmt,
+ read_only_mode='retain')
+ self.assert_qmp(result, 'return', {})
+
+ self.wait_for_open()
+ self.wait_for_close()
+
+ result = self.vm.qmp('query-block')
+ self.assert_qmp(result, 'return[0]/tray_open', False)
+ self.assert_qmp(result, 'return[0]/inserted/ro', True)
+ self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
+
+ def test_make_ro_rw_by_retain(self):
+ os.chmod(new_img, 0444)
+ self.vm.add_drive(old_img, 'media=disk', 'floppy')
+ self.vm.launch()
+
+ result = self.vm.qmp('query-block')
+ self.assert_qmp(result, 'return[0]/tray_open', False)
+ self.assert_qmp(result, 'return[0]/inserted/ro', False)
+ self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
+
+ result = self.vm.qmp('blockdev-change-medium', device='drive0',
+ filename=new_img,
+ format=iotests.imgfmt,
+ read_only_mode='retain')
+ self.assert_qmp(result, 'error/class', 'GenericError')
+
+ self.assertEquals(self.vm.get_qmp_events(wait=False), [])
+
+ result = self.vm.qmp('query-block')
+ self.assert_qmp(result, 'return[0]/tray_open', False)
+ self.assert_qmp(result, 'return[0]/inserted/ro', False)
+ self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
+
+ def test_rw_ro_cycle(self):
+ os.chmod(new_img, 0444)
+ self.vm.add_drive(old_img, 'media=disk', 'floppy')
+ self.vm.launch()
+
+ result = self.vm.qmp('query-block')
+ self.assert_qmp(result, 'return[0]/tray_open', False)
+ self.assert_qmp(result, 'return[0]/inserted/ro', False)
+ self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
+
+ result = self.vm.qmp('blockdev-add',
+ options={'node-name': 'new',
+ 'driver': iotests.imgfmt,
+ 'read-only': True,
+ 'file': {'filename': new_img,
+ 'driver': 'file'}})
+ self.assert_qmp(result, 'return', {})
+
+ result = self.vm.qmp('blockdev-open-tray', device='drive0', force=True)
+ self.assert_qmp(result, 'return', {})
+
+ self.wait_for_open()
+
+ result = self.vm.qmp('query-block')
+ self.assert_qmp(result, 'return[0]/tray_open', True)
+ self.assert_qmp(result, 'return[0]/inserted/ro', False)
+ self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
+
+ result = self.vm.qmp('blockdev-remove-medium', device='drive0')
+ self.assert_qmp(result, 'return', {})
+
+ result = self.vm.qmp('query-block')
+ self.assert_qmp(result, 'return[0]/tray_open', True)
+ self.assert_qmp_absent(result, 'return[0]/inserted')
+
+ result = self.vm.qmp('blockdev-insert-medium', device='drive0',
+ node_name='new')
+ self.assert_qmp(result, 'return', {})
+
+ result = self.vm.qmp('query-block')
+ self.assert_qmp(result, 'return[0]/tray_open', True)
+ self.assert_qmp(result, 'return[0]/inserted/ro', True)
+ self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
+
+ result = self.vm.qmp('blockdev-close-tray', device='drive0')
+ self.assert_qmp(result, 'return', {})
+
+ self.wait_for_close()
+
+ result = self.vm.qmp('query-block')
+ self.assert_qmp(result, 'return[0]/tray_open', False)
+ self.assert_qmp(result, 'return[0]/inserted/ro', True)
+ self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
+
+GeneralChangeTestsBaseClass = None
+TestInitiallyFilled = None
+TestInitiallyEmpty = None
+
+
+class TestBlockJobsAfterCycle(ChangeBaseClass):
+ def setUp(self):
+ qemu_img('create', '-f', iotests.imgfmt, old_img, '1M')
+
+ self.vm = iotests.VM()
+ self.vm.launch()
+
+ result = self.vm.qmp('blockdev-add',
+ options={'id': 'drive0',
+ 'driver': 'null-co'})
+ self.assert_qmp(result, 'return', {})
+
+ result = self.vm.qmp('query-block')
+ self.assert_qmp(result, 'return[0]/tray_open', False)
+ self.assert_qmp(result, 'return[0]/inserted/image/format', 'null-co')
+
+ # For device-less BBs, calling blockdev-open-tray or blockdev-close-tray
+ # is not necessary
+ result = self.vm.qmp('blockdev-remove-medium', device='drive0')
+ self.assert_qmp(result, 'return', {})
+
+ result = self.vm.qmp('query-block')
+ self.assert_qmp_absent(result, 'return[0]/inserted')
+
+ result = self.vm.qmp('blockdev-add',
+ options={'node-name': 'node0',
+ 'driver': iotests.imgfmt,
+ 'file': {'filename': old_img,
+ 'driver': 'file'}})
+ self.assert_qmp(result, 'return', {})
+
+ result = self.vm.qmp('blockdev-insert-medium', device='drive0',
+ node_name='node0')
+ self.assert_qmp(result, 'return', {})
+
+ result = self.vm.qmp('query-block')
+ self.assert_qmp(result, 'return[0]/tray_open', False)
+ self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
+
+ def tearDown(self):
+ self.vm.shutdown()
+ os.remove(old_img)
+ try:
+ os.remove(new_img)
+ except OSError:
+ pass
+
+ def test_snapshot_and_commit(self):
+ # We need backing file support
+ if iotests.imgfmt != 'qcow2' and iotests.imgfmt != 'qed':
+ return
+
+ result = self.vm.qmp('blockdev-snapshot-sync', device='drive0',
+ snapshot_file=new_img,
+ format=iotests.imgfmt)
+ self.assert_qmp(result, 'return', {})
+
+ result = self.vm.qmp('query-block')
+ self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
+ self.assert_qmp(result,
+ 'return[0]/inserted/image/backing-image/filename',
+ old_img)
+
+ result = self.vm.qmp('block-commit', device='drive0')
+ self.assert_qmp(result, 'return', {})
+
+ self.vm.event_wait(name='BLOCK_JOB_READY')
+
+ result = self.vm.qmp('query-block-jobs')
+ self.assert_qmp(result, 'return[0]/device', 'drive0')
+
+ result = self.vm.qmp('block-job-complete', device='drive0')
+ self.assert_qmp(result, 'return', {})
+
+ self.vm.event_wait(name='BLOCK_JOB_COMPLETED')
+
+
+if __name__ == '__main__':
+ if iotests.qemu_default_machine != 'pc':
+ # We need floppy and IDE CD-ROM
+ iotests.notrun('not suitable for this machine type: %s' %
+ iotests.qemu_default_machine)
+ iotests.main()
diff --git a/tests/qemu-iotests/118.out b/tests/qemu-iotests/118.out
new file mode 100644
index 0000000..6a91713
--- /dev/null
+++ b/tests/qemu-iotests/118.out
@@ -0,0 +1,5 @@
+...........................................................
+----------------------------------------------------------------------
+Ran 59 tests
+
+OK
diff --git a/tests/qemu-iotests/124 b/tests/qemu-iotests/124
index 9ccd118..c928f01 100644
--- a/tests/qemu-iotests/124
+++ b/tests/qemu-iotests/124
@@ -36,6 +36,23 @@ def try_remove(img):
pass
+def transaction_action(action, **kwargs):
+ return {
+ 'type': action,
+ 'data': dict((k.replace('_', '-'), v) for k, v in kwargs.iteritems())
+ }
+
+
+def transaction_bitmap_clear(node, name, **kwargs):
+ return transaction_action('block-dirty-bitmap-clear',
+ node=node, name=name, **kwargs)
+
+
+def transaction_drive_backup(device, target, **kwargs):
+ return transaction_action('drive-backup', device=device, target=target,
+ **kwargs)
+
+
class Bitmap:
def __init__(self, name, drive):
self.name = name
@@ -122,9 +139,12 @@ class TestIncrementalBackup(iotests.QMPTestCase):
def do_qmp_backup(self, error='Input/output error', **kwargs):
res = self.vm.qmp('drive-backup', **kwargs)
self.assert_qmp(res, 'return', {})
+ return self.wait_qmp_backup(kwargs['device'], error)
+
+ def wait_qmp_backup(self, device, error='Input/output error'):
event = self.vm.event_wait(name="BLOCK_JOB_COMPLETED",
- match={'data': {'device': kwargs['device']}})
+ match={'data': {'device': device}})
self.assertNotEqual(event, None)
try:
@@ -139,6 +159,12 @@ class TestIncrementalBackup(iotests.QMPTestCase):
return False
+ def wait_qmp_backup_cancelled(self, device):
+ event = self.vm.event_wait(name='BLOCK_JOB_CANCELLED',
+ match={'data': {'device': device}})
+ self.assertNotEqual(event, None)
+
+
def create_anchor_backup(self, drive=None):
if drive is None:
drive = self.drives[-1]
@@ -264,6 +290,43 @@ class TestIncrementalBackup(iotests.QMPTestCase):
return self.do_incremental_simple(granularity=131072)
+ def test_incremental_transaction(self):
+ '''Test: Verify backups made from transactionally created bitmaps.
+
+ Create a bitmap "before" VM execution begins, then create a second
+ bitmap AFTER writes have already occurred. Use transactions to create
+ a full backup and synchronize both bitmaps to this backup.
+ Create an incremental backup through both bitmaps and verify that
+ both backups match the current drive0 image.
+ '''
+
+ drive0 = self.drives[0]
+ bitmap0 = self.add_bitmap('bitmap0', drive0)
+ self.hmp_io_writes(drive0['id'], (('0xab', 0, 512),
+ ('0xfe', '16M', '256k'),
+ ('0x64', '32736k', '64k')))
+ bitmap1 = self.add_bitmap('bitmap1', drive0)
+
+ result = self.vm.qmp('transaction', actions=[
+ transaction_bitmap_clear(bitmap0.drive['id'], bitmap0.name),
+ transaction_bitmap_clear(bitmap1.drive['id'], bitmap1.name),
+ transaction_drive_backup(drive0['id'], drive0['backup'],
+ sync='full', format=drive0['fmt'])
+ ])
+ self.assert_qmp(result, 'return', {})
+ self.wait_until_completed(drive0['id'])
+ self.files.append(drive0['backup'])
+
+ self.hmp_io_writes(drive0['id'], (('0x9a', 0, 512),
+ ('0x55', '8M', '352k'),
+ ('0x78', '15872k', '1M')))
+ # Both bitmaps should be correctly in sync.
+ self.create_incremental(bitmap0)
+ self.create_incremental(bitmap1)
+ self.vm.shutdown()
+ self.check_backups()
+
+
def test_incremental_failure(self):
'''Test: Verify backups made after a failure are correct.
@@ -321,6 +384,123 @@ class TestIncrementalBackup(iotests.QMPTestCase):
self.check_backups()
+ def test_transaction_failure(self):
+ '''Test: Verify backups made from a transaction that partially fails.
+
+ Add a second drive with its own unique pattern, and add a bitmap to each
+ drive. Use blkdebug to interfere with the backup on just one drive and
+ attempt to create a coherent incremental backup across both drives.
+
+ verify a failure in one but not both, then delete the failed stubs and
+ re-run the same transaction.
+
+ verify that both incrementals are created successfully.
+ '''
+
+ # Create a second drive, with pattern:
+ drive1 = self.add_node('drive1')
+ self.img_create(drive1['file'], drive1['fmt'])
+ io_write_patterns(drive1['file'], (('0x14', 0, 512),
+ ('0x5d', '1M', '32k'),
+ ('0xcd', '32M', '124k')))
+
+ # Create a blkdebug interface to this img as 'drive1'
+ 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', {})
+
+ # Create bitmaps and full backups for both drives
+ drive0 = self.drives[0]
+ dr0bm0 = self.add_bitmap('bitmap0', drive0)
+ dr1bm0 = self.add_bitmap('bitmap0', drive1)
+ self.create_anchor_backup(drive0)
+ self.create_anchor_backup(drive1)
+ self.assert_no_active_block_jobs()
+ self.assertFalse(self.vm.get_qmp_events(wait=False))
+
+ # Emulate some writes
+ self.hmp_io_writes(drive0['id'], (('0xab', 0, 512),
+ ('0xfe', '16M', '256k'),
+ ('0x64', '32736k', '64k')))
+ self.hmp_io_writes(drive1['id'], (('0xba', 0, 512),
+ ('0xef', '16M', '256k'),
+ ('0x46', '32736k', '64k')))
+
+ # Create incremental backup targets
+ target0 = self.prepare_backup(dr0bm0)
+ target1 = self.prepare_backup(dr1bm0)
+
+ # Ask for a new incremental backup per-each drive,
+ # expecting drive1's backup to fail:
+ transaction = [
+ transaction_drive_backup(drive0['id'], target0, sync='incremental',
+ format=drive0['fmt'], mode='existing',
+ bitmap=dr0bm0.name),
+ transaction_drive_backup(drive1['id'], target1, sync='incremental',
+ format=drive1['fmt'], mode='existing',
+ bitmap=dr1bm0.name)
+ ]
+ result = self.vm.qmp('transaction', actions=transaction,
+ properties={'completion-mode': 'grouped'} )
+ self.assert_qmp(result, 'return', {})
+
+ # Observe that drive0's backup is cancelled and drive1 completes with
+ # an error.
+ self.wait_qmp_backup_cancelled(drive0['id'])
+ self.assertFalse(self.wait_qmp_backup(drive1['id']))
+ error = self.vm.event_wait('BLOCK_JOB_ERROR')
+ self.assert_qmp(error, 'data', {'device': drive1['id'],
+ 'action': 'report',
+ 'operation': 'read'})
+ self.assertFalse(self.vm.get_qmp_events(wait=False))
+ self.assert_no_active_block_jobs()
+
+ # Delete drive0's successful target and eliminate our record of the
+ # unsuccessful drive1 target. Then re-run the same transaction.
+ dr0bm0.del_target()
+ dr1bm0.del_target()
+ target0 = self.prepare_backup(dr0bm0)
+ target1 = self.prepare_backup(dr1bm0)
+
+ # Re-run the exact same transaction.
+ result = self.vm.qmp('transaction', actions=transaction,
+ properties={'completion-mode':'grouped'})
+ self.assert_qmp(result, 'return', {})
+
+ # Both should complete successfully this time.
+ self.assertTrue(self.wait_qmp_backup(drive0['id']))
+ self.assertTrue(self.wait_qmp_backup(drive1['id']))
+ self.make_reference_backup(dr0bm0)
+ self.make_reference_backup(dr1bm0)
+ self.assertFalse(self.vm.get_qmp_events(wait=False))
+ self.assert_no_active_block_jobs()
+
+ # And the images should of course validate.
+ 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)
diff --git a/tests/qemu-iotests/124.out b/tests/qemu-iotests/124.out
index 2f7d390..dae404e 100644
--- a/tests/qemu-iotests/124.out
+++ b/tests/qemu-iotests/124.out
@@ -1,5 +1,5 @@
-.......
+.........
----------------------------------------------------------------------
-Ran 7 tests
+Ran 9 tests
OK
diff --git a/tests/qemu-iotests/136 b/tests/qemu-iotests/136
new file mode 100644
index 0000000..e8c6937
--- /dev/null
+++ b/tests/qemu-iotests/136
@@ -0,0 +1,349 @@
+#!/usr/bin/env python
+#
+# Tests for block device statistics
+#
+# Copyright (C) 2015 Igalia, S.L.
+# Author: Alberto Garcia <berto@igalia.com>
+#
+# 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 iotests
+import os
+
+interval_length = 10
+nsec_per_sec = 1000000000
+op_latency = nsec_per_sec / 1000 # See qtest_latency_ns in accounting.c
+bad_sector = 8192
+bad_offset = bad_sector * 512
+blkdebug_file = os.path.join(iotests.test_dir, 'blkdebug.conf')
+
+class BlockDeviceStatsTestCase(iotests.QMPTestCase):
+ test_img = "null-aio://"
+ total_rd_bytes = 0
+ total_rd_ops = 0
+ total_wr_bytes = 0
+ total_wr_ops = 0
+ total_wr_merged = 0
+ total_flush_ops = 0
+ failed_rd_ops = 0
+ failed_wr_ops = 0
+ invalid_rd_ops = 0
+ invalid_wr_ops = 0
+ wr_highest_offset = 0
+ account_invalid = False
+ account_failed = False
+
+ def blockstats(self, device):
+ result = self.vm.qmp("query-blockstats")
+ for r in result['return']:
+ if r['device'] == device:
+ return r['stats']
+ raise Exception("Device not found for blockstats: %s" % device)
+
+ def create_blkdebug_file(self):
+ file = open(blkdebug_file, 'w')
+ file.write('''
+[inject-error]
+event = "read_aio"
+errno = "5"
+sector = "%d"
+
+[inject-error]
+event = "write_aio"
+errno = "5"
+sector = "%d"
+''' % (bad_sector, bad_sector))
+ file.close()
+
+ def setUp(self):
+ drive_args = []
+ drive_args.append("stats-intervals.0=%d" % interval_length)
+ drive_args.append("stats-account-invalid=%s" %
+ (self.account_invalid and "on" or "off"))
+ drive_args.append("stats-account-failed=%s" %
+ (self.account_failed and "on" or "off"))
+ self.create_blkdebug_file()
+ self.vm = iotests.VM().add_drive('blkdebug:%s:%s ' %
+ (blkdebug_file, self.test_img),
+ ','.join(drive_args))
+ self.vm.launch()
+ # Set an initial value for the clock
+ self.vm.qtest("clock_step %d" % nsec_per_sec)
+
+ def tearDown(self):
+ self.vm.shutdown()
+ os.remove(blkdebug_file)
+
+ def accounted_ops(self, read = False, write = False, flush = False):
+ ops = 0
+ if write:
+ ops += self.total_wr_ops
+ if self.account_failed:
+ ops += self.failed_wr_ops
+ if self.account_invalid:
+ ops += self.invalid_wr_ops
+ if read:
+ ops += self.total_rd_ops
+ if self.account_failed:
+ ops += self.failed_rd_ops
+ if self.account_invalid:
+ ops += self.invalid_rd_ops
+ if flush:
+ ops += self.total_flush_ops
+ return ops
+
+ def accounted_latency(self, read = False, write = False, flush = False):
+ latency = 0
+ if write:
+ latency += self.total_wr_ops * op_latency
+ if self.account_failed:
+ latency += self.failed_wr_ops * op_latency
+ if read:
+ latency += self.total_rd_ops * op_latency
+ if self.account_failed:
+ latency += self.failed_rd_ops * op_latency
+ if flush:
+ latency += self.total_flush_ops * op_latency
+ return latency
+
+ def check_values(self):
+ stats = self.blockstats('drive0')
+
+ # Check that the totals match with what we have calculated
+ self.assertEqual(self.total_rd_bytes, stats['rd_bytes'])
+ self.assertEqual(self.total_wr_bytes, stats['wr_bytes'])
+ self.assertEqual(self.total_rd_ops, stats['rd_operations'])
+ self.assertEqual(self.total_wr_ops, stats['wr_operations'])
+ self.assertEqual(self.total_flush_ops, stats['flush_operations'])
+ self.assertEqual(self.wr_highest_offset, stats['wr_highest_offset'])
+ self.assertEqual(self.failed_rd_ops, stats['failed_rd_operations'])
+ self.assertEqual(self.failed_wr_ops, stats['failed_wr_operations'])
+ self.assertEqual(self.invalid_rd_ops, stats['invalid_rd_operations'])
+ self.assertEqual(self.invalid_wr_ops, stats['invalid_wr_operations'])
+ self.assertEqual(self.account_invalid, stats['account_invalid'])
+ self.assertEqual(self.account_failed, stats['account_failed'])
+ self.assertEqual(self.total_wr_merged, stats['wr_merged'])
+
+ # Check that there's exactly one interval with the length we defined
+ self.assertEqual(1, len(stats['timed_stats']))
+ timed_stats = stats['timed_stats'][0]
+ self.assertEqual(interval_length, timed_stats['interval_length'])
+
+ total_rd_latency = self.accounted_latency(read = True)
+ if (total_rd_latency != 0):
+ self.assertEqual(total_rd_latency, stats['rd_total_time_ns'])
+ self.assertEqual(op_latency, timed_stats['min_rd_latency_ns'])
+ self.assertEqual(op_latency, timed_stats['max_rd_latency_ns'])
+ self.assertEqual(op_latency, timed_stats['avg_rd_latency_ns'])
+ self.assertLess(0, timed_stats['avg_rd_queue_depth'])
+ else:
+ self.assertEqual(0, stats['rd_total_time_ns'])
+ self.assertEqual(0, timed_stats['min_rd_latency_ns'])
+ self.assertEqual(0, timed_stats['max_rd_latency_ns'])
+ self.assertEqual(0, timed_stats['avg_rd_latency_ns'])
+ self.assertEqual(0, timed_stats['avg_rd_queue_depth'])
+
+ # min read latency <= avg read latency <= max read latency
+ self.assertLessEqual(timed_stats['min_rd_latency_ns'],
+ timed_stats['avg_rd_latency_ns'])
+ self.assertLessEqual(timed_stats['avg_rd_latency_ns'],
+ timed_stats['max_rd_latency_ns'])
+
+ total_wr_latency = self.accounted_latency(write = True)
+ if (total_wr_latency != 0):
+ self.assertEqual(total_wr_latency, stats['wr_total_time_ns'])
+ self.assertEqual(op_latency, timed_stats['min_wr_latency_ns'])
+ self.assertEqual(op_latency, timed_stats['max_wr_latency_ns'])
+ self.assertEqual(op_latency, timed_stats['avg_wr_latency_ns'])
+ self.assertLess(0, timed_stats['avg_wr_queue_depth'])
+ else:
+ self.assertEqual(0, stats['wr_total_time_ns'])
+ self.assertEqual(0, timed_stats['min_wr_latency_ns'])
+ self.assertEqual(0, timed_stats['max_wr_latency_ns'])
+ self.assertEqual(0, timed_stats['avg_wr_latency_ns'])
+ self.assertEqual(0, timed_stats['avg_wr_queue_depth'])
+
+ # min write latency <= avg write latency <= max write latency
+ self.assertLessEqual(timed_stats['min_wr_latency_ns'],
+ timed_stats['avg_wr_latency_ns'])
+ self.assertLessEqual(timed_stats['avg_wr_latency_ns'],
+ timed_stats['max_wr_latency_ns'])
+
+ total_flush_latency = self.accounted_latency(flush = True)
+ if (total_flush_latency != 0):
+ self.assertEqual(total_flush_latency, stats['flush_total_time_ns'])
+ self.assertEqual(op_latency, timed_stats['min_flush_latency_ns'])
+ self.assertEqual(op_latency, timed_stats['max_flush_latency_ns'])
+ self.assertEqual(op_latency, timed_stats['avg_flush_latency_ns'])
+ else:
+ self.assertEqual(0, stats['flush_total_time_ns'])
+ self.assertEqual(0, timed_stats['min_flush_latency_ns'])
+ self.assertEqual(0, timed_stats['max_flush_latency_ns'])
+ self.assertEqual(0, timed_stats['avg_flush_latency_ns'])
+
+ # min flush latency <= avg flush latency <= max flush latency
+ self.assertLessEqual(timed_stats['min_flush_latency_ns'],
+ timed_stats['avg_flush_latency_ns'])
+ self.assertLessEqual(timed_stats['avg_flush_latency_ns'],
+ timed_stats['max_flush_latency_ns'])
+
+ # idle_time_ns must be > 0 if we have performed any operation
+ if (self.accounted_ops(read = True, write = True, flush = True) != 0):
+ self.assertLess(0, stats['idle_time_ns'])
+ else:
+ self.assertFalse(stats.has_key('idle_time_ns'))
+
+ # This test does not alter these, so they must be all 0
+ self.assertEqual(0, stats['rd_merged'])
+ self.assertEqual(0, stats['failed_flush_operations'])
+ self.assertEqual(0, stats['invalid_flush_operations'])
+
+ def do_test_stats(self, rd_size = 0, rd_ops = 0, wr_size = 0, wr_ops = 0,
+ flush_ops = 0, invalid_rd_ops = 0, invalid_wr_ops = 0,
+ failed_rd_ops = 0, failed_wr_ops = 0, wr_merged = 0):
+ # The 'ops' list will contain all the requested I/O operations
+ ops = []
+ for i in range(rd_ops):
+ ops.append("aio_read %d %d" % (i * rd_size, rd_size))
+
+ for i in range(wr_ops):
+ ops.append("aio_write %d %d" % (i * wr_size, wr_size))
+
+ for i in range(flush_ops):
+ ops.append("aio_flush")
+
+ highest_offset = wr_ops * wr_size
+
+ # Two types of invalid operations: unaligned length and unaligned offset
+ for i in range(invalid_rd_ops / 2):
+ ops.append("aio_read 0 511")
+
+ for i in range(invalid_rd_ops / 2, invalid_rd_ops):
+ ops.append("aio_read 13 512")
+
+ for i in range(invalid_wr_ops / 2):
+ ops.append("aio_write 0 511")
+
+ for i in range(invalid_wr_ops / 2, invalid_wr_ops):
+ ops.append("aio_write 13 512")
+
+ for i in range(failed_rd_ops):
+ ops.append("aio_read %d 512" % bad_offset)
+
+ for i in range(failed_wr_ops):
+ ops.append("aio_write %d 512" % bad_offset)
+
+ if failed_wr_ops > 0:
+ highest_offset = max(highest_offset, bad_offset + 512)
+
+ for i in range(wr_merged):
+ first = i * wr_size * 2
+ second = first + wr_size
+ ops.append("multiwrite %d %d ; %d %d" %
+ (first, wr_size, second, wr_size))
+
+ highest_offset = max(highest_offset, wr_merged * wr_size * 2)
+
+ # Now perform all operations
+ for op in ops:
+ self.vm.hmp_qemu_io("drive0", op)
+
+ # Update the expected totals
+ self.total_rd_bytes += rd_ops * rd_size
+ self.total_rd_ops += rd_ops
+ self.total_wr_bytes += wr_ops * wr_size
+ self.total_wr_ops += wr_ops
+ self.total_wr_merged += wr_merged
+ self.total_flush_ops += flush_ops
+ self.invalid_rd_ops += invalid_rd_ops
+ self.invalid_wr_ops += invalid_wr_ops
+ self.failed_rd_ops += failed_rd_ops
+ self.failed_wr_ops += failed_wr_ops
+
+ self.wr_highest_offset = max(self.wr_highest_offset, highest_offset)
+
+ # Advance the clock so idle_time_ns has a meaningful value
+ self.vm.qtest("clock_step %d" % nsec_per_sec)
+
+ # And check that the actual statistics match the expected ones
+ self.check_values()
+
+ def test_read_only(self):
+ test_values = [[512, 1],
+ [65536, 1],
+ [512, 12],
+ [65536, 12]]
+ for i in test_values:
+ self.do_test_stats(rd_size = i[0], rd_ops = i[1])
+
+ def test_write_only(self):
+ test_values = [[512, 1],
+ [65536, 1],
+ [512, 12],
+ [65536, 12]]
+ for i in test_values:
+ self.do_test_stats(wr_size = i[0], wr_ops = i[1])
+
+ def test_invalid(self):
+ self.do_test_stats(invalid_rd_ops = 7)
+ self.do_test_stats(invalid_wr_ops = 3)
+ self.do_test_stats(invalid_rd_ops = 4, invalid_wr_ops = 5)
+
+ def test_failed(self):
+ self.do_test_stats(failed_rd_ops = 8)
+ self.do_test_stats(failed_wr_ops = 6)
+ self.do_test_stats(failed_rd_ops = 5, failed_wr_ops = 12)
+
+ def test_flush(self):
+ self.do_test_stats(flush_ops = 8)
+
+ def test_merged(self):
+ for i in range(5):
+ self.do_test_stats(wr_merged = i * 3)
+
+ def test_all(self):
+ # rd_size, rd_ops, wr_size, wr_ops, flush_ops
+ # invalid_rd_ops, invalid_wr_ops,
+ # failed_rd_ops, failed_wr_ops
+ # wr_merged
+ test_values = [[512, 1, 512, 1, 1, 4, 7, 5, 2, 1],
+ [65536, 1, 2048, 12, 7, 7, 5, 2, 5, 5],
+ [32768, 9, 8192, 1, 4, 3, 2, 4, 6, 4],
+ [16384, 11, 3584, 16, 9, 8, 6, 7, 3, 4]]
+ for i in test_values:
+ self.do_test_stats(*i)
+
+ def test_no_op(self):
+ # All values must be sane before doing any I/O
+ self.check_values()
+
+
+class BlockDeviceStatsTestAccountInvalid(BlockDeviceStatsTestCase):
+ account_invalid = True
+ account_failed = False
+
+class BlockDeviceStatsTestAccountFailed(BlockDeviceStatsTestCase):
+ account_invalid = False
+ account_failed = True
+
+class BlockDeviceStatsTestAccountBoth(BlockDeviceStatsTestCase):
+ account_invalid = True
+ account_failed = True
+
+class BlockDeviceStatsTestCoroutine(BlockDeviceStatsTestCase):
+ test_img = "null-co://"
+
+if __name__ == '__main__':
+ iotests.main(supported_fmts=["raw"])
diff --git a/tests/qemu-iotests/136.out b/tests/qemu-iotests/136.out
new file mode 100644
index 0000000..0a5e958
--- /dev/null
+++ b/tests/qemu-iotests/136.out
@@ -0,0 +1,5 @@
+........................................
+----------------------------------------------------------------------
+Ran 40 tests
+
+OK
diff --git a/tests/qemu-iotests/137.out b/tests/qemu-iotests/137.out
index cf55a41..88c702c 100644
--- a/tests/qemu-iotests/137.out
+++ b/tests/qemu-iotests/137.out
@@ -31,7 +31,11 @@ Cache clean interval too big
Unsupported value 'blubb' for qcow2 option 'overlap-check'. Allowed are any of the following: none, constant, cached, all
wrote 512/512 bytes at offset 0
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-./common.config: Killed ( exec "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@" )
+./common.config: Killed ( if [ "${VALGRIND_QEMU}" == "y" ]; then
+ exec valgrind --log-file="${VALGRIND_LOGFILE}" --error-exitcode=99 "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@";
+else
+ exec "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@";
+fi )
incompatible_features 0x0
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
wrote 65536/65536 bytes at offset 0
diff --git a/tests/qemu-iotests/139 b/tests/qemu-iotests/139
new file mode 100644
index 0000000..42f78c7
--- /dev/null
+++ b/tests/qemu-iotests/139
@@ -0,0 +1,416 @@
+#!/usr/bin/env python
+#
+# Test cases for the QMP 'x-blockdev-del' command
+#
+# Copyright (C) 2015 Igalia, S.L.
+# Author: Alberto Garcia <berto@igalia.com>
+#
+# 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
+
+base_img = os.path.join(iotests.test_dir, 'base.img')
+new_img = os.path.join(iotests.test_dir, 'new.img')
+
+class TestBlockdevDel(iotests.QMPTestCase):
+
+ def setUp(self):
+ iotests.qemu_img('create', '-f', iotests.imgfmt, base_img, '1M')
+ self.vm = iotests.VM()
+ self.vm.launch()
+
+ def tearDown(self):
+ self.vm.shutdown()
+ os.remove(base_img)
+ if os.path.isfile(new_img):
+ os.remove(new_img)
+
+ # Check whether a BlockBackend exists
+ def checkBlockBackend(self, backend, node, must_exist = True):
+ result = self.vm.qmp('query-block')
+ backends = filter(lambda x: x['device'] == backend, result['return'])
+ self.assertLessEqual(len(backends), 1)
+ self.assertEqual(must_exist, len(backends) == 1)
+ if must_exist:
+ if node:
+ self.assertEqual(backends[0]['inserted']['node-name'], node)
+ else:
+ self.assertFalse(backends[0].has_key('inserted'))
+
+ # Check whether a BlockDriverState exists
+ def checkBlockDriverState(self, node, must_exist = True):
+ result = self.vm.qmp('query-named-block-nodes')
+ nodes = filter(lambda x: x['node-name'] == node, result['return'])
+ self.assertLessEqual(len(nodes), 1)
+ self.assertEqual(must_exist, len(nodes) == 1)
+
+ # Add a new BlockBackend (with its attached BlockDriverState)
+ def addBlockBackend(self, backend, node):
+ file_node = '%s_file' % node
+ self.checkBlockBackend(backend, node, False)
+ self.checkBlockDriverState(node, False)
+ self.checkBlockDriverState(file_node, False)
+ opts = {'driver': iotests.imgfmt,
+ 'id': backend,
+ 'node-name': node,
+ 'file': {'driver': 'file',
+ 'node-name': file_node,
+ 'filename': base_img}}
+ result = self.vm.qmp('blockdev-add', conv_keys = False, options = opts)
+ self.assert_qmp(result, 'return', {})
+ self.checkBlockBackend(backend, node)
+ self.checkBlockDriverState(node)
+ self.checkBlockDriverState(file_node)
+
+ # Add a BlockDriverState without a BlockBackend
+ def addBlockDriverState(self, node):
+ file_node = '%s_file' % node
+ self.checkBlockDriverState(node, False)
+ self.checkBlockDriverState(file_node, False)
+ opts = {'driver': iotests.imgfmt,
+ 'node-name': node,
+ 'file': {'driver': 'file',
+ 'node-name': file_node,
+ 'filename': base_img}}
+ result = self.vm.qmp('blockdev-add', conv_keys = False, options = opts)
+ self.assert_qmp(result, 'return', {})
+ self.checkBlockDriverState(node)
+ self.checkBlockDriverState(file_node)
+
+ # Add a BlockDriverState that will be used as overlay for the base_img BDS
+ def addBlockDriverStateOverlay(self, node):
+ self.checkBlockDriverState(node, False)
+ iotests.qemu_img('create', '-f', iotests.imgfmt,
+ '-b', base_img, new_img, '1M')
+ opts = {'driver': iotests.imgfmt,
+ 'node-name': node,
+ 'backing': '',
+ 'file': {'driver': 'file',
+ 'filename': new_img}}
+ result = self.vm.qmp('blockdev-add', conv_keys = False, options = opts)
+ self.assert_qmp(result, 'return', {})
+ self.checkBlockDriverState(node)
+
+ # Delete a BlockBackend
+ def delBlockBackend(self, backend, node, expect_error = False,
+ destroys_media = True):
+ self.checkBlockBackend(backend, node)
+ if node:
+ self.checkBlockDriverState(node)
+ result = self.vm.qmp('x-blockdev-del', id = backend)
+ if expect_error:
+ self.assert_qmp(result, 'error/class', 'GenericError')
+ if node:
+ self.checkBlockDriverState(node)
+ else:
+ self.assert_qmp(result, 'return', {})
+ if node:
+ self.checkBlockDriverState(node, not destroys_media)
+ self.checkBlockBackend(backend, node, must_exist = expect_error)
+
+ # Delete a BlockDriverState
+ def delBlockDriverState(self, node, expect_error = False):
+ self.checkBlockDriverState(node)
+ result = self.vm.qmp('x-blockdev-del', node_name = node)
+ if expect_error:
+ self.assert_qmp(result, 'error/class', 'GenericError')
+ else:
+ self.assert_qmp(result, 'return', {})
+ self.checkBlockDriverState(node, expect_error)
+
+ # Add a device model
+ def addDeviceModel(self, device, backend):
+ result = self.vm.qmp('device_add', id = device,
+ driver = 'virtio-blk-pci', drive = backend)
+ self.assert_qmp(result, 'return', {})
+
+ # Delete a device model
+ def delDeviceModel(self, device):
+ result = self.vm.qmp('device_del', id = device)
+ self.assert_qmp(result, 'return', {})
+
+ result = self.vm.qmp('system_reset')
+ self.assert_qmp(result, 'return', {})
+
+ device_path = '/machine/peripheral/%s/virtio-backend' % device
+ event = self.vm.event_wait(name="DEVICE_DELETED",
+ match={'data': {'path': device_path}})
+ self.assertNotEqual(event, None)
+
+ event = self.vm.event_wait(name="DEVICE_DELETED",
+ match={'data': {'device': device}})
+ self.assertNotEqual(event, None)
+
+ # Remove a BlockDriverState
+ def ejectDrive(self, backend, node, expect_error = False,
+ destroys_media = True):
+ self.checkBlockBackend(backend, node)
+ self.checkBlockDriverState(node)
+ result = self.vm.qmp('eject', device = backend)
+ if expect_error:
+ self.assert_qmp(result, 'error/class', 'GenericError')
+ self.checkBlockDriverState(node)
+ self.checkBlockBackend(backend, node)
+ else:
+ self.assert_qmp(result, 'return', {})
+ self.checkBlockDriverState(node, not destroys_media)
+ self.checkBlockBackend(backend, None)
+
+ # Insert a BlockDriverState
+ def insertDrive(self, backend, node):
+ self.checkBlockBackend(backend, None)
+ self.checkBlockDriverState(node)
+ result = self.vm.qmp('blockdev-insert-medium',
+ device = backend, node_name = node)
+ self.assert_qmp(result, 'return', {})
+ self.checkBlockBackend(backend, node)
+ self.checkBlockDriverState(node)
+
+ # Create a snapshot using 'blockdev-snapshot-sync'
+ def createSnapshotSync(self, node, overlay):
+ self.checkBlockDriverState(node)
+ self.checkBlockDriverState(overlay, False)
+ opts = {'node-name': node,
+ 'snapshot-file': new_img,
+ 'snapshot-node-name': overlay,
+ 'format': iotests.imgfmt}
+ result = self.vm.qmp('blockdev-snapshot-sync', conv_keys=False, **opts)
+ self.assert_qmp(result, 'return', {})
+ self.checkBlockDriverState(node)
+ self.checkBlockDriverState(overlay)
+
+ # Create a snapshot using 'blockdev-snapshot'
+ def createSnapshot(self, node, overlay):
+ self.checkBlockDriverState(node)
+ self.checkBlockDriverState(overlay)
+ result = self.vm.qmp('blockdev-snapshot',
+ node = node, overlay = overlay)
+ self.assert_qmp(result, 'return', {})
+ self.checkBlockDriverState(node)
+ self.checkBlockDriverState(overlay)
+
+ # Create a mirror
+ def createMirror(self, backend, node, new_node):
+ self.checkBlockBackend(backend, node)
+ self.checkBlockDriverState(new_node, False)
+ opts = {'device': backend,
+ 'target': new_img,
+ 'node-name': new_node,
+ 'sync': 'top',
+ 'format': iotests.imgfmt}
+ result = self.vm.qmp('drive-mirror', conv_keys=False, **opts)
+ self.assert_qmp(result, 'return', {})
+ self.checkBlockBackend(backend, node)
+ self.checkBlockDriverState(new_node)
+
+ # Complete an existing block job
+ def completeBlockJob(self, backend, node_before, node_after):
+ self.checkBlockBackend(backend, node_before)
+ result = self.vm.qmp('block-job-complete', device=backend)
+ self.assert_qmp(result, 'return', {})
+ self.wait_until_completed(backend)
+ self.checkBlockBackend(backend, node_after)
+
+ # Add a BlkDebug node
+ # Note that the purpose of this is to test the x-blockdev-del
+ # sanity checks, not to create a usable blkdebug drive
+ def addBlkDebug(self, debug, node):
+ self.checkBlockDriverState(node, False)
+ self.checkBlockDriverState(debug, False)
+ image = {'driver': iotests.imgfmt,
+ 'node-name': node,
+ 'file': {'driver': 'file',
+ 'filename': base_img}}
+ opts = {'driver': 'blkdebug',
+ 'node-name': debug,
+ 'image': image}
+ result = self.vm.qmp('blockdev-add', conv_keys = False, options = opts)
+ self.assert_qmp(result, 'return', {})
+ self.checkBlockDriverState(node)
+ self.checkBlockDriverState(debug)
+
+ # Add a BlkVerify node
+ # Note that the purpose of this is to test the x-blockdev-del
+ # sanity checks, not to create a usable blkverify drive
+ def addBlkVerify(self, blkverify, test, raw):
+ self.checkBlockDriverState(test, False)
+ self.checkBlockDriverState(raw, False)
+ self.checkBlockDriverState(blkverify, False)
+ iotests.qemu_img('create', '-f', iotests.imgfmt, new_img, '1M')
+ node_0 = {'driver': iotests.imgfmt,
+ 'node-name': test,
+ 'file': {'driver': 'file',
+ 'filename': base_img}}
+ node_1 = {'driver': iotests.imgfmt,
+ 'node-name': raw,
+ 'file': {'driver': 'file',
+ 'filename': new_img}}
+ opts = {'driver': 'blkverify',
+ 'node-name': blkverify,
+ 'test': node_0,
+ 'raw': node_1}
+ result = self.vm.qmp('blockdev-add', conv_keys = False, options = opts)
+ self.assert_qmp(result, 'return', {})
+ self.checkBlockDriverState(test)
+ self.checkBlockDriverState(raw)
+ self.checkBlockDriverState(blkverify)
+
+ # Add a Quorum node
+ def addQuorum(self, quorum, child0, child1):
+ self.checkBlockDriverState(child0, False)
+ self.checkBlockDriverState(child1, False)
+ self.checkBlockDriverState(quorum, False)
+ iotests.qemu_img('create', '-f', iotests.imgfmt, new_img, '1M')
+ child_0 = {'driver': iotests.imgfmt,
+ 'node-name': child0,
+ 'file': {'driver': 'file',
+ 'filename': base_img}}
+ child_1 = {'driver': iotests.imgfmt,
+ 'node-name': child1,
+ 'file': {'driver': 'file',
+ 'filename': new_img}}
+ opts = {'driver': 'quorum',
+ 'node-name': quorum,
+ 'vote-threshold': 1,
+ 'children': [ child_0, child_1 ]}
+ result = self.vm.qmp('blockdev-add', conv_keys = False, options = opts)
+ self.assert_qmp(result, 'return', {})
+ self.checkBlockDriverState(child0)
+ self.checkBlockDriverState(child1)
+ self.checkBlockDriverState(quorum)
+
+ ########################
+ # The tests start here #
+ ########################
+
+ def testWrongParameters(self):
+ self.addBlockBackend('drive0', 'node0')
+ result = self.vm.qmp('x-blockdev-del')
+ self.assert_qmp(result, 'error/class', 'GenericError')
+ result = self.vm.qmp('x-blockdev-del', id='drive0', node_name='node0')
+ self.assert_qmp(result, 'error/class', 'GenericError')
+ self.delBlockBackend('drive0', 'node0')
+
+ def testBlockBackend(self):
+ self.addBlockBackend('drive0', 'node0')
+ # You cannot delete a BDS that is attached to a backend
+ self.delBlockDriverState('node0', expect_error = True)
+ self.delBlockBackend('drive0', 'node0')
+
+ def testBlockDriverState(self):
+ self.addBlockDriverState('node0')
+ # You cannot delete a file BDS directly
+ self.delBlockDriverState('node0_file', expect_error = True)
+ self.delBlockDriverState('node0')
+
+ def testEject(self):
+ self.addBlockBackend('drive0', 'node0')
+ self.ejectDrive('drive0', 'node0')
+ self.delBlockBackend('drive0', None)
+
+ def testDeviceModel(self):
+ self.addBlockBackend('drive0', 'node0')
+ self.addDeviceModel('device0', 'drive0')
+ self.ejectDrive('drive0', 'node0', expect_error = True)
+ self.delBlockBackend('drive0', 'node0', expect_error = True)
+ self.delDeviceModel('device0')
+ self.delBlockBackend('drive0', 'node0')
+
+ def testAttachMedia(self):
+ # This creates a BlockBackend and removes its media
+ self.addBlockBackend('drive0', 'node0')
+ self.ejectDrive('drive0', 'node0')
+ # This creates a new BlockDriverState and inserts it into the backend
+ self.addBlockDriverState('node1')
+ self.insertDrive('drive0', 'node1')
+ # The backend can't be removed: the new BDS has an extra reference
+ self.delBlockBackend('drive0', 'node1', expect_error = True)
+ self.delBlockDriverState('node1', expect_error = True)
+ # The BDS still exists after being ejected, but now it can be removed
+ self.ejectDrive('drive0', 'node1', destroys_media = False)
+ self.delBlockDriverState('node1')
+ self.delBlockBackend('drive0', None)
+
+ def testSnapshotSync(self):
+ self.addBlockBackend('drive0', 'node0')
+ self.createSnapshotSync('node0', 'overlay0')
+ # This fails because node0 is now being used as a backing image
+ self.delBlockDriverState('node0', expect_error = True)
+ # This succeeds because overlay0 only has the backend reference
+ self.delBlockBackend('drive0', 'overlay0')
+ self.checkBlockDriverState('node0', False)
+
+ def testSnapshot(self):
+ self.addBlockBackend('drive0', 'node0')
+ self.addBlockDriverStateOverlay('overlay0')
+ self.createSnapshot('node0', 'overlay0')
+ self.delBlockBackend('drive0', 'overlay0', expect_error = True)
+ self.delBlockDriverState('node0', expect_error = True)
+ self.delBlockDriverState('overlay0', expect_error = True)
+ self.ejectDrive('drive0', 'overlay0', destroys_media = False)
+ self.delBlockBackend('drive0', None)
+ self.delBlockDriverState('node0', expect_error = True)
+ self.delBlockDriverState('overlay0')
+ self.checkBlockDriverState('node0', False)
+
+ def testMirror(self):
+ self.addBlockBackend('drive0', 'node0')
+ self.createMirror('drive0', 'node0', 'mirror0')
+ # The block job prevents removing the device
+ self.delBlockBackend('drive0', 'node0', expect_error = True)
+ self.delBlockDriverState('node0', expect_error = True)
+ self.delBlockDriverState('mirror0', expect_error = True)
+ self.wait_ready('drive0')
+ self.completeBlockJob('drive0', 'node0', 'mirror0')
+ self.assert_no_active_block_jobs()
+ self.checkBlockDriverState('node0', False)
+ # This succeeds because the backend now points to mirror0
+ self.delBlockBackend('drive0', 'mirror0')
+
+ def testBlkDebug(self):
+ self.addBlkDebug('debug0', 'node0')
+ # 'node0' is used by the blkdebug node
+ self.delBlockDriverState('node0', expect_error = True)
+ # But we can remove the blkdebug node directly
+ self.delBlockDriverState('debug0')
+ self.checkBlockDriverState('node0', False)
+
+ def testBlkVerify(self):
+ self.addBlkVerify('verify0', 'node0', 'node1')
+ # We cannot remove the children of a blkverify device
+ self.delBlockDriverState('node0', expect_error = True)
+ self.delBlockDriverState('node1', expect_error = True)
+ # But we can remove the blkverify node directly
+ self.delBlockDriverState('verify0')
+ self.checkBlockDriverState('node0', False)
+ self.checkBlockDriverState('node1', False)
+
+ def testQuorum(self):
+ if not 'quorum' in iotests.qemu_img_pipe('--help'):
+ return
+ self.addQuorum('quorum0', 'node0', 'node1')
+ # We cannot remove the children of a Quorum device
+ self.delBlockDriverState('node0', expect_error = True)
+ self.delBlockDriverState('node1', expect_error = True)
+ # But we can remove the Quorum node directly
+ self.delBlockDriverState('quorum0')
+ self.checkBlockDriverState('node0', False)
+ self.checkBlockDriverState('node1', False)
+
+
+if __name__ == '__main__':
+ iotests.main(supported_fmts=["qcow2"])
diff --git a/tests/qemu-iotests/139.out b/tests/qemu-iotests/139.out
new file mode 100644
index 0000000..281b69e
--- /dev/null
+++ b/tests/qemu-iotests/139.out
@@ -0,0 +1,5 @@
+............
+----------------------------------------------------------------------
+Ran 12 tests
+
+OK
diff --git a/tests/qemu-iotests/common b/tests/qemu-iotests/common
index 25c351b..ff84f4b 100644
--- a/tests/qemu-iotests/common
+++ b/tests/qemu-iotests/common
@@ -41,7 +41,6 @@ sortme=false
expunge=true
have_test_arg=false
randomize=false
-valgrind=false
cachemode=false
rm -f $tmp.list $tmp.tmp $tmp.sed
@@ -53,6 +52,7 @@ export CACHEMODE="writeback"
export QEMU_IO_OPTIONS=""
export CACHEMODE_IS_DEFAULT=true
export QEMU_OPTIONS="-nodefaults"
+export VALGRIND_QEMU=
for r
do
@@ -278,7 +278,7 @@ testlist options
;;
-valgrind)
- valgrind=true
+ VALGRIND_QEMU='y'
xpand=false
;;
@@ -436,8 +436,3 @@ fi
if [ "$IMGPROTO" = "nbd" ] ; then
[ "$QEMU_NBD" = "" ] && _fatal "qemu-nbd not found"
fi
-
-if $valgrind; then
- export REAL_QEMU_IO="$QEMU_IO_PROG"
- export QEMU_IO_PROG=valgrind_qemu_io
-fi
diff --git a/tests/qemu-iotests/common.config b/tests/qemu-iotests/common.config
index 596bb2b..3ed51b8 100644
--- a/tests/qemu-iotests/common.config
+++ b/tests/qemu-iotests/common.config
@@ -44,6 +44,8 @@ export HOST_OPTIONS=${HOST_OPTIONS:=local.config}
export CHECK_OPTIONS=${CHECK_OPTIONS:="-g auto"}
export PWD=`pwd`
+export _QEMU_HANDLE=0
+
# $1 = prog to look for, $2* = default pathnames if not found in $PATH
set_prog_path()
{
@@ -105,7 +107,12 @@ fi
_qemu_wrapper()
{
- (exec "$QEMU_PROG" $QEMU_OPTIONS "$@")
+ (
+ if [ -n "${QEMU_NEED_PID}" ]; then
+ echo $BASHPID > "${TEST_DIR}/qemu-${_QEMU_HANDLE}.pid"
+ fi
+ exec "$QEMU_PROG" $QEMU_OPTIONS "$@"
+ )
}
_qemu_img_wrapper()
@@ -115,12 +122,31 @@ _qemu_img_wrapper()
_qemu_io_wrapper()
{
- (exec "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@")
+ local VALGRIND_LOGFILE=/tmp/$$.valgrind
+ local RETVAL
+ (
+ if [ "${VALGRIND_QEMU}" == "y" ]; then
+ exec valgrind --log-file="${VALGRIND_LOGFILE}" --error-exitcode=99 "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@"
+ else
+ exec "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@"
+ fi
+ )
+ RETVAL=$?
+ if [ "${VALGRIND_QEMU}" == "y" ]; then
+ if [ $RETVAL == 99 ]; then
+ cat "${VALGRIND_LOGFILE}"
+ fi
+ rm -f "${VALGRIND_LOGFILE}"
+ fi
+ (exit $RETVAL)
}
_qemu_nbd_wrapper()
{
- (exec "$QEMU_NBD_PROG" $QEMU_NBD_OPTIONS "$@")
+ (
+ echo $BASHPID > "${TEST_DIR}/qemu-nbd.pid"
+ exec "$QEMU_NBD_PROG" $QEMU_NBD_OPTIONS "$@"
+ )
}
export QEMU=_qemu_wrapper
diff --git a/tests/qemu-iotests/common.qemu b/tests/qemu-iotests/common.qemu
index e3faa53..8bf3969 100644
--- a/tests/qemu-iotests/common.qemu
+++ b/tests/qemu-iotests/common.qemu
@@ -30,8 +30,6 @@ QEMU_COMM_TIMEOUT=10
QEMU_FIFO_IN="${TEST_DIR}/qmp-in-$$"
QEMU_FIFO_OUT="${TEST_DIR}/qmp-out-$$"
-QEMU_PID=
-_QEMU_HANDLE=0
QEMU_HANDLE=0
# If bash version is >= 4.1, these will be overwritten and dynamic
@@ -153,11 +151,11 @@ function _launch_qemu()
mkfifo "${fifo_out}"
mkfifo "${fifo_in}"
+ QEMU_NEED_PID='y'\
${QEMU} -nographic -serial none ${comm} -machine accel=qtest "${@}" \
>"${fifo_out}" \
2>&1 \
<"${fifo_in}" &
- QEMU_PID[${_QEMU_HANDLE}]=$!
if [[ "${BASH_VERSINFO[0]}" -ge "5" ||
("${BASH_VERSINFO[0]}" -ge "4" && "${BASH_VERSINFO[1]}" -ge "1") ]]
@@ -196,10 +194,18 @@ function _cleanup_qemu()
# QEMU_PID[], QEMU_IN[], QEMU_OUT[] all use same indices
for i in "${!QEMU_OUT[@]}"
do
- if [ -z "${wait}" ]; then
- kill -KILL ${QEMU_PID[$i]} 2>/dev/null
+ local QEMU_PID
+ if [ -f "${TEST_DIR}/qemu-${i}.pid" ]; then
+ read QEMU_PID < "${TEST_DIR}/qemu-${i}.pid"
+ rm -f "${TEST_DIR}/qemu-${i}.pid"
+ if [ -z "${wait}" ] && [ -n "${QEMU_PID}" ]; then
+ kill -KILL ${QEMU_PID} 2>/dev/null
+ fi
+ if [ -n "${QEMU_PID}" ]; then
+ wait ${QEMU_PID} 2>/dev/null # silent kill
+ fi
fi
- wait ${QEMU_PID[$i]} 2>/dev/null # silent kill
+
if [ -n "${wait}" ]; then
cat <&${QEMU_OUT[$i]} | _filter_testdir | _filter_qemu \
| _filter_qemu_io | _filter_qmp
diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc
index 28e4bea..d9913f8 100644
--- a/tests/qemu-iotests/common.rc
+++ b/tests/qemu-iotests/common.rc
@@ -70,16 +70,6 @@ else
TEST_IMG=$IMGPROTO:$TEST_DIR/t.$IMGFMT
fi
-function valgrind_qemu_io()
-{
- valgrind --log-file=/tmp/$$.valgrind --error-exitcode=99 $REAL_QEMU_IO "$@"
- if [ $? != 0 ]; then
- cat /tmp/$$.valgrind
- fi
- rm -f /tmp/$$.valgrind
-}
-
-
_optstr_add()
{
if [ -n "$1" ]; then
@@ -154,7 +144,6 @@ _make_test_img()
# Start an NBD server on the image file, which is what we'll be talking to
if [ $IMGPROTO = "nbd" ]; then
eval "$QEMU_NBD -v -t -b 127.0.0.1 -p 10810 -f $IMGFMT $TEST_IMG_FILE &"
- QEMU_NBD_PID=$!
sleep 1 # FIXME: qemu-nbd needs to be listening before we continue
fi
}
@@ -175,8 +164,11 @@ _cleanup_test_img()
case "$IMGPROTO" in
nbd)
- if [ -n "$QEMU_NBD_PID" ]; then
- kill $QEMU_NBD_PID
+ if [ -f "${TEST_DIR}/qemu-nbd.pid" ]; then
+ local QEMU_NBD_PID
+ read QEMU_NBD_PID < "${TEST_DIR}/qemu-nbd.pid"
+ kill ${QEMU_NBD_PID}
+ rm -f "${TEST_DIR}/qemu-nbd.pid"
fi
rm -f "$TEST_IMG_FILE"
;;
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
index 30c784e..5a08808 100644
--- a/tests/qemu-iotests/group
+++ b/tests/qemu-iotests/group
@@ -122,6 +122,7 @@
114 rw auto quick
115 rw auto
116 rw auto quick
+118 rw auto
119 rw auto quick
120 rw auto quick
121 rw auto
@@ -135,5 +136,7 @@
132 rw auto quick
134 rw auto quick
135 rw auto
+136 rw auto
137 rw auto
138 rw auto quick
+139 rw auto quick
diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c
new file mode 100644
index 0000000..34747e9
--- /dev/null
+++ b/tests/test-blockjob-txn.c
@@ -0,0 +1,250 @@
+/*
+ * Blockjob transactions tests
+ *
+ * Copyright Red Hat, Inc. 2015
+ *
+ * Authors:
+ * Stefan Hajnoczi <stefanha@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+
+#include <glib.h>
+#include "qapi/error.h"
+#include "qemu/main-loop.h"
+#include "block/blockjob.h"
+
+typedef struct {
+ BlockJob common;
+ unsigned int iterations;
+ bool use_timer;
+ int rc;
+ int *result;
+} TestBlockJob;
+
+static const BlockJobDriver test_block_job_driver = {
+ .instance_size = sizeof(TestBlockJob),
+};
+
+static void test_block_job_complete(BlockJob *job, void *opaque)
+{
+ BlockDriverState *bs = job->bs;
+ int rc = (intptr_t)opaque;
+
+ if (block_job_is_cancelled(job)) {
+ rc = -ECANCELED;
+ }
+
+ block_job_completed(job, rc);
+ bdrv_unref(bs);
+}
+
+static void coroutine_fn test_block_job_run(void *opaque)
+{
+ TestBlockJob *s = opaque;
+ BlockJob *job = &s->common;
+
+ while (s->iterations--) {
+ if (s->use_timer) {
+ block_job_sleep_ns(job, QEMU_CLOCK_REALTIME, 0);
+ } else {
+ block_job_yield(job);
+ }
+
+ if (block_job_is_cancelled(job)) {
+ break;
+ }
+ }
+
+ block_job_defer_to_main_loop(job, test_block_job_complete,
+ (void *)(intptr_t)s->rc);
+}
+
+typedef struct {
+ TestBlockJob *job;
+ int *result;
+} TestBlockJobCBData;
+
+static void test_block_job_cb(void *opaque, int ret)
+{
+ TestBlockJobCBData *data = opaque;
+ if (!ret && block_job_is_cancelled(&data->job->common)) {
+ ret = -ECANCELED;
+ }
+ *data->result = ret;
+ g_free(data);
+}
+
+/* Create a block job that completes with a given return code after a given
+ * number of event loop iterations. The return code is stored in the given
+ * result pointer.
+ *
+ * The event loop iterations can either be handled automatically with a 0 delay
+ * timer, or they can be stepped manually by entering the coroutine.
+ */
+static BlockJob *test_block_job_start(unsigned int iterations,
+ bool use_timer,
+ int rc, int *result)
+{
+ BlockDriverState *bs;
+ TestBlockJob *s;
+ TestBlockJobCBData *data;
+
+ data = g_new0(TestBlockJobCBData, 1);
+ bs = bdrv_new();
+ s = block_job_create(&test_block_job_driver, bs, 0, test_block_job_cb,
+ data, &error_abort);
+ s->iterations = iterations;
+ s->use_timer = use_timer;
+ s->rc = rc;
+ s->result = result;
+ s->common.co = qemu_coroutine_create(test_block_job_run);
+ data->job = s;
+ data->result = result;
+ qemu_coroutine_enter(s->common.co, s);
+ return &s->common;
+}
+
+static void test_single_job(int expected)
+{
+ BlockJob *job;
+ BlockJobTxn *txn;
+ int result = -EINPROGRESS;
+
+ txn = block_job_txn_new();
+ job = test_block_job_start(1, true, expected, &result);
+ block_job_txn_add_job(txn, job);
+
+ if (expected == -ECANCELED) {
+ block_job_cancel(job);
+ }
+
+ while (result == -EINPROGRESS) {
+ aio_poll(qemu_get_aio_context(), true);
+ }
+ g_assert_cmpint(result, ==, expected);
+
+ block_job_txn_unref(txn);
+}
+
+static void test_single_job_success(void)
+{
+ test_single_job(0);
+}
+
+static void test_single_job_failure(void)
+{
+ test_single_job(-EIO);
+}
+
+static void test_single_job_cancel(void)
+{
+ test_single_job(-ECANCELED);
+}
+
+static void test_pair_jobs(int expected1, int expected2)
+{
+ BlockJob *job1;
+ BlockJob *job2;
+ BlockJobTxn *txn;
+ int result1 = -EINPROGRESS;
+ int result2 = -EINPROGRESS;
+
+ txn = block_job_txn_new();
+ job1 = test_block_job_start(1, true, expected1, &result1);
+ block_job_txn_add_job(txn, job1);
+ job2 = test_block_job_start(2, true, expected2, &result2);
+ block_job_txn_add_job(txn, job2);
+
+ if (expected1 == -ECANCELED) {
+ block_job_cancel(job1);
+ }
+ if (expected2 == -ECANCELED) {
+ block_job_cancel(job2);
+ }
+
+ while (result1 == -EINPROGRESS || result2 == -EINPROGRESS) {
+ aio_poll(qemu_get_aio_context(), true);
+ }
+
+ /* Failure or cancellation of one job cancels the other job */
+ if (expected1 != 0) {
+ expected2 = -ECANCELED;
+ } else if (expected2 != 0) {
+ expected1 = -ECANCELED;
+ }
+
+ g_assert_cmpint(result1, ==, expected1);
+ g_assert_cmpint(result2, ==, expected2);
+
+ block_job_txn_unref(txn);
+}
+
+static void test_pair_jobs_success(void)
+{
+ test_pair_jobs(0, 0);
+}
+
+static void test_pair_jobs_failure(void)
+{
+ /* Test both orderings. The two jobs run for a different number of
+ * iterations so the code path is different depending on which job fails
+ * first.
+ */
+ test_pair_jobs(-EIO, 0);
+ test_pair_jobs(0, -EIO);
+}
+
+static void test_pair_jobs_cancel(void)
+{
+ test_pair_jobs(-ECANCELED, 0);
+ test_pair_jobs(0, -ECANCELED);
+}
+
+static void test_pair_jobs_fail_cancel_race(void)
+{
+ BlockJob *job1;
+ BlockJob *job2;
+ BlockJobTxn *txn;
+ int result1 = -EINPROGRESS;
+ int result2 = -EINPROGRESS;
+
+ txn = block_job_txn_new();
+ job1 = test_block_job_start(1, true, -ECANCELED, &result1);
+ block_job_txn_add_job(txn, job1);
+ job2 = test_block_job_start(2, false, 0, &result2);
+ block_job_txn_add_job(txn, job2);
+
+ block_job_cancel(job1);
+
+ /* Now make job2 finish before the main loop kicks jobs. This simulates
+ * the race between a pending kick and another job completing.
+ */
+ block_job_enter(job2);
+ block_job_enter(job2);
+
+ while (result1 == -EINPROGRESS || result2 == -EINPROGRESS) {
+ aio_poll(qemu_get_aio_context(), true);
+ }
+
+ g_assert_cmpint(result1, ==, -ECANCELED);
+ g_assert_cmpint(result2, ==, -ECANCELED);
+
+ block_job_txn_unref(txn);
+}
+
+int main(int argc, char **argv)
+{
+ qemu_init_main_loop(&error_abort);
+
+ g_test_init(&argc, &argv, NULL);
+ g_test_add_func("/single/success", test_single_job_success);
+ g_test_add_func("/single/failure", test_single_job_failure);
+ g_test_add_func("/single/cancel", test_single_job_cancel);
+ g_test_add_func("/pair/success", test_pair_jobs_success);
+ g_test_add_func("/pair/failure", test_pair_jobs_failure);
+ g_test_add_func("/pair/cancel", test_pair_jobs_cancel);
+ g_test_add_func("/pair/fail-cancel-race", test_pair_jobs_fail_cancel_race);
+ return g_test_run();
+}
diff --git a/tests/test-qmp-commands.c b/tests/test-qmp-commands.c
index f23d8ea..888fb5f 100644
--- a/tests/test-qmp-commands.c
+++ b/tests/test-qmp-commands.c
@@ -225,8 +225,7 @@ static void test_dealloc_partial(void)
assert(ud2->dict1 == NULL);
/* confirm & release construction error */
- assert(err != NULL);
- error_free(err);
+ error_free_or_abort(&err);
/* tear down partial object */
qapi_free_UserDefTwo(ud2);
diff --git a/tests/test-qmp-input-strict.c b/tests/test-qmp-input-strict.c
index 53a7693..f1c2e3b 100644
--- a/tests/test-qmp-input-strict.c
+++ b/tests/test-qmp-input-strict.c
@@ -40,31 +40,42 @@ static void validate_teardown(TestInputVisitorData *data,
}
}
-/* This is provided instead of a test setup function so that the JSON
- string used by the tests are kept in the test functions (and not
- int main()) */
-static GCC_FMT_ATTR(2, 3)
-Visitor *validate_test_init(TestInputVisitorData *data,
- const char *json_string, ...)
+/* The various test_init functions are provided instead of a test setup
+ function so that the JSON string used by the tests are kept in the test
+ functions (and not in main()). */
+static Visitor *validate_test_init_internal(TestInputVisitorData *data,
+ const char *json_string,
+ va_list *ap)
{
Visitor *v;
- va_list ap;
- va_start(ap, json_string);
- data->obj = qobject_from_jsonv(json_string, &ap);
- va_end(ap);
+ validate_teardown(data, NULL);
- g_assert(data->obj != NULL);
+ data->obj = qobject_from_jsonv(json_string, ap);
+ g_assert(data->obj);
data->qiv = qmp_input_visitor_new_strict(data->obj);
- g_assert(data->qiv != NULL);
+ g_assert(data->qiv);
v = qmp_input_get_visitor(data->qiv);
- g_assert(v != NULL);
+ g_assert(v);
return v;
}
+static GCC_FMT_ATTR(2, 3)
+Visitor *validate_test_init(TestInputVisitorData *data,
+ const char *json_string, ...)
+{
+ Visitor *v;
+ va_list ap;
+
+ va_start(ap, json_string);
+ v = validate_test_init_internal(data, json_string, &ap);
+ va_end(ap);
+ return v;
+}
+
/* similar to validate_test_init(), but does not expect a string
* literal/format json_string argument and so can be used for
* programatically generated strings (and we can't pass in programatically
@@ -75,67 +86,19 @@ Visitor *validate_test_init(TestInputVisitorData *data,
static Visitor *validate_test_init_raw(TestInputVisitorData *data,
const char *json_string)
{
- Visitor *v;
-
- data->obj = qobject_from_json(json_string);
- g_assert(data->obj != NULL);
-
- data->qiv = qmp_input_visitor_new_strict(data->obj);
- g_assert(data->qiv != NULL);
-
- v = qmp_input_get_visitor(data->qiv);
- g_assert(v != NULL);
-
- return v;
+ return validate_test_init_internal(data, json_string, NULL);
}
-typedef struct TestStruct
-{
- int64_t integer;
- bool boolean;
- char *string;
-} TestStruct;
-
-static void visit_type_TestStruct(Visitor *v, TestStruct **obj,
- const char *name, Error **errp)
-{
- Error *err = NULL;
-
- visit_start_struct(v, (void **)obj, "TestStruct", name, sizeof(TestStruct),
- &err);
- if (err) {
- goto out;
- }
-
- visit_type_int(v, &(*obj)->integer, "integer", &err);
- if (err) {
- goto out_end;
- }
- visit_type_bool(v, &(*obj)->boolean, "boolean", &err);
- if (err) {
- goto out_end;
- }
- visit_type_str(v, &(*obj)->string, "string", &err);
-
-out_end:
- error_propagate(errp, err);
- err = NULL;
- visit_end_struct(v, &err);
-out:
- error_propagate(errp, err);
-}
static void test_validate_struct(TestInputVisitorData *data,
const void *unused)
{
TestStruct *p = NULL;
- Error *err = NULL;
Visitor *v;
v = validate_test_init(data, "{ 'integer': -42, 'boolean': true, 'string': 'foo' }");
- visit_type_TestStruct(v, &p, NULL, &err);
- g_assert(!err);
+ visit_type_TestStruct(v, &p, NULL, &error_abort);
g_free(p->string);
g_free(p);
}
@@ -144,7 +107,6 @@ static void test_validate_struct_nested(TestInputVisitorData *data,
const void *unused)
{
UserDefTwo *udp = NULL;
- Error *err = NULL;
Visitor *v;
v = validate_test_init(data, "{ 'string0': 'string0', "
@@ -152,8 +114,7 @@ static void test_validate_struct_nested(TestInputVisitorData *data,
"'dict2': { 'userdef': { 'integer': 42, "
"'string': 'string' }, 'string': 'string2'}}}");
- visit_type_UserDefTwo(v, &udp, NULL, &err);
- g_assert(!err);
+ visit_type_UserDefTwo(v, &udp, NULL, &error_abort);
qapi_free_UserDefTwo(udp);
}
@@ -161,13 +122,11 @@ static void test_validate_list(TestInputVisitorData *data,
const void *unused)
{
UserDefOneList *head = NULL;
- Error *err = NULL;
Visitor *v;
v = validate_test_init(data, "[ { 'string': 'string0', 'integer': 42 }, { 'string': 'string1', 'integer': 43 }, { 'string': 'string2', 'integer': 44 } ]");
- visit_type_UserDefOneList(v, &head, NULL, &err);
- g_assert(!err);
+ visit_type_UserDefOneList(v, &head, NULL, &error_abort);
qapi_free_UserDefOneList(head);
}
@@ -176,12 +135,10 @@ static void test_validate_union_native_list(TestInputVisitorData *data,
{
UserDefNativeListUnion *tmp = NULL;
Visitor *v;
- Error *err = NULL;
v = validate_test_init(data, "{ 'type': 'integer', 'data' : [ 1, 2 ] }");
- visit_type_UserDefNativeListUnion(v, &tmp, NULL, &err);
- g_assert(!err);
+ visit_type_UserDefNativeListUnion(v, &tmp, NULL, &error_abort);
qapi_free_UserDefNativeListUnion(tmp);
}
@@ -190,7 +147,6 @@ static void test_validate_union_flat(TestInputVisitorData *data,
{
UserDefFlatUnion *tmp = NULL;
Visitor *v;
- Error *err = NULL;
v = validate_test_init(data,
"{ 'enum1': 'value1', "
@@ -198,8 +154,7 @@ static void test_validate_union_flat(TestInputVisitorData *data,
"'string': 'str', "
"'boolean': true }");
- visit_type_UserDefFlatUnion(v, &tmp, NULL, &err);
- g_assert(!err);
+ visit_type_UserDefFlatUnion(v, &tmp, NULL, &error_abort);
qapi_free_UserDefFlatUnion(tmp);
}
@@ -208,12 +163,10 @@ static void test_validate_alternate(TestInputVisitorData *data,
{
UserDefAlternate *tmp = NULL;
Visitor *v;
- Error *err = NULL;
v = validate_test_init(data, "42");
- visit_type_UserDefAlternate(v, &tmp, NULL, &err);
- g_assert(!err);
+ visit_type_UserDefAlternate(v, &tmp, NULL, &error_abort);
qapi_free_UserDefAlternate(tmp);
}
@@ -227,7 +180,7 @@ static void test_validate_fail_struct(TestInputVisitorData *data,
v = validate_test_init(data, "{ 'integer': -42, 'boolean': true, 'string': 'foo', 'extra': 42 }");
visit_type_TestStruct(v, &p, NULL, &err);
- g_assert(err);
+ error_free_or_abort(&err);
if (p) {
g_free(p->string);
}
@@ -244,7 +197,7 @@ static void test_validate_fail_struct_nested(TestInputVisitorData *data,
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_UserDefTwo(v, &udp, NULL, &err);
- g_assert(err);
+ error_free_or_abort(&err);
qapi_free_UserDefTwo(udp);
}
@@ -258,7 +211,7 @@ static void test_validate_fail_list(TestInputVisitorData *data,
v = validate_test_init(data, "[ { 'string': 'string0', 'integer': 42 }, { 'string': 'string1', 'integer': 43 }, { 'string': 'string2', 'integer': 44, 'extra': 'ggg' } ]");
visit_type_UserDefOneList(v, &head, NULL, &err);
- g_assert(err);
+ error_free_or_abort(&err);
qapi_free_UserDefOneList(head);
}
@@ -273,7 +226,7 @@ static void test_validate_fail_union_native_list(TestInputVisitorData *data,
"{ 'type': 'integer', 'data' : [ 'string' ] }");
visit_type_UserDefNativeListUnion(v, &tmp, NULL, &err);
- g_assert(err);
+ error_free_or_abort(&err);
qapi_free_UserDefNativeListUnion(tmp);
}
@@ -287,7 +240,7 @@ static void test_validate_fail_union_flat(TestInputVisitorData *data,
v = validate_test_init(data, "{ 'string': 'c', 'integer': 41, 'boolean': true }");
visit_type_UserDefFlatUnion(v, &tmp, NULL, &err);
- g_assert(err);
+ error_free_or_abort(&err);
qapi_free_UserDefFlatUnion(tmp);
}
@@ -302,7 +255,7 @@ static void test_validate_fail_union_flat_no_discrim(TestInputVisitorData *data,
v = validate_test_init(data, "{ 'integer': 42, 'string': 'c', 'string1': 'd', 'string2': 'e' }");
visit_type_UserDefFlatUnion2(v, &tmp, NULL, &err);
- g_assert(err);
+ error_free_or_abort(&err);
qapi_free_UserDefFlatUnion2(tmp);
}
@@ -316,7 +269,7 @@ static void test_validate_fail_alternate(TestInputVisitorData *data,
v = validate_test_init(data, "3.14");
visit_type_UserDefAlternate(v, &tmp, NULL, &err);
- g_assert(err);
+ error_free_or_abort(&err);
qapi_free_UserDefAlternate(tmp);
}
@@ -324,16 +277,11 @@ static void do_test_validate_qmp_introspect(TestInputVisitorData *data,
const char *schema_json)
{
SchemaInfoList *schema = NULL;
- Error *err = NULL;
Visitor *v;
v = validate_test_init_raw(data, schema_json);
- visit_type_SchemaInfoList(v, &schema, NULL, &err);
- if (err) {
- fprintf(stderr, "%s", error_get_pretty(err));
- }
- g_assert(!err);
+ visit_type_SchemaInfoList(v, &schema, NULL, &error_abort);
g_assert(schema);
qapi_free_SchemaInfoList(schema);
diff --git a/tests/test-qmp-input-visitor.c b/tests/test-qmp-input-visitor.c
index de65982..d48ebdd 100644
--- a/tests/test-qmp-input-visitor.c
+++ b/tests/test-qmp-input-visitor.c
@@ -36,28 +36,39 @@ static void visitor_input_teardown(TestInputVisitorData *data,
}
}
-/* This is provided instead of a test setup function so that the JSON
- string used by the tests are kept in the test functions (and not
- int main()) */
-static GCC_FMT_ATTR(2, 3)
-Visitor *visitor_input_test_init(TestInputVisitorData *data,
- const char *json_string, ...)
+/* The various test_init functions are provided instead of a test setup
+ function so that the JSON string used by the tests are kept in the test
+ functions (and not in main()). */
+static Visitor *visitor_input_test_init_internal(TestInputVisitorData *data,
+ const char *json_string,
+ va_list *ap)
{
Visitor *v;
- va_list ap;
- va_start(ap, json_string);
- data->obj = qobject_from_jsonv(json_string, &ap);
- va_end(ap);
+ visitor_input_teardown(data, NULL);
- g_assert(data->obj != NULL);
+ data->obj = qobject_from_jsonv(json_string, ap);
+ g_assert(data->obj);
data->qiv = qmp_input_visitor_new(data->obj);
- g_assert(data->qiv != NULL);
+ g_assert(data->qiv);
v = qmp_input_get_visitor(data->qiv);
- g_assert(v != NULL);
+ g_assert(v);
+
+ return v;
+}
+
+static GCC_FMT_ATTR(2, 3)
+Visitor *visitor_input_test_init(TestInputVisitorData *data,
+ const char *json_string, ...)
+{
+ Visitor *v;
+ va_list ap;
+ va_start(ap, json_string);
+ v = visitor_input_test_init_internal(data, json_string, &ap);
+ va_end(ap);
return v;
}
@@ -71,32 +82,18 @@ Visitor *visitor_input_test_init(TestInputVisitorData *data,
static Visitor *visitor_input_test_init_raw(TestInputVisitorData *data,
const char *json_string)
{
- Visitor *v;
-
- data->obj = qobject_from_json(json_string);
-
- g_assert(data->obj != NULL);
-
- data->qiv = qmp_input_visitor_new(data->obj);
- g_assert(data->qiv != NULL);
-
- v = qmp_input_get_visitor(data->qiv);
- g_assert(v != NULL);
-
- return v;
+ return visitor_input_test_init_internal(data, json_string, NULL);
}
static void test_visitor_in_int(TestInputVisitorData *data,
const void *unused)
{
int64_t res = 0, value = -42;
- Error *err = NULL;
Visitor *v;
v = visitor_input_test_init(data, "%" PRId64, value);
- visit_type_int(v, &res, NULL, &err);
- g_assert(!err);
+ visit_type_int(v, &res, NULL, &error_abort);
g_assert_cmpint(res, ==, value);
}
@@ -114,21 +111,18 @@ static void test_visitor_in_int_overflow(TestInputVisitorData *data,
v = visitor_input_test_init(data, "%f", DBL_MAX);
visit_type_int(v, &res, NULL, &err);
- g_assert(err);
- error_free(err);
+ error_free_or_abort(&err);
}
static void test_visitor_in_bool(TestInputVisitorData *data,
const void *unused)
{
- Error *err = NULL;
bool res = false;
Visitor *v;
v = visitor_input_test_init(data, "true");
- visit_type_bool(v, &res, NULL, &err);
- g_assert(!err);
+ visit_type_bool(v, &res, NULL, &error_abort);
g_assert_cmpint(res, ==, true);
}
@@ -136,13 +130,11 @@ static void test_visitor_in_number(TestInputVisitorData *data,
const void *unused)
{
double res = 0, value = 3.14;
- Error *err = NULL;
Visitor *v;
v = visitor_input_test_init(data, "%f", value);
- visit_type_number(v, &res, NULL, &err);
- g_assert(!err);
+ visit_type_number(v, &res, NULL, &error_abort);
g_assert_cmpfloat(res, ==, value);
}
@@ -150,13 +142,11 @@ static void test_visitor_in_string(TestInputVisitorData *data,
const void *unused)
{
char *res = NULL, *value = (char *) "Q E M U";
- Error *err = NULL;
Visitor *v;
v = visitor_input_test_init(data, "%s", value);
- visit_type_str(v, &res, NULL, &err);
- g_assert(!err);
+ visit_type_str(v, &res, NULL, &error_abort);
g_assert_cmpstr(res, ==, value);
g_free(res);
@@ -165,7 +155,6 @@ static void test_visitor_in_string(TestInputVisitorData *data,
static void test_visitor_in_enum(TestInputVisitorData *data,
const void *unused)
{
- Error *err = NULL;
Visitor *v;
EnumOne i;
@@ -174,63 +163,21 @@ static void test_visitor_in_enum(TestInputVisitorData *data,
v = visitor_input_test_init(data, "%s", EnumOne_lookup[i]);
- visit_type_EnumOne(v, &res, NULL, &err);
- g_assert(!err);
+ visit_type_EnumOne(v, &res, NULL, &error_abort);
g_assert_cmpint(i, ==, res);
-
- visitor_input_teardown(data, NULL);
}
-
- data->obj = NULL;
- data->qiv = NULL;
}
-typedef struct TestStruct
-{
- int64_t integer;
- bool boolean;
- char *string;
-} TestStruct;
-
-static void visit_type_TestStruct(Visitor *v, TestStruct **obj,
- const char *name, Error **errp)
-{
- Error *err = NULL;
-
- visit_start_struct(v, (void **)obj, "TestStruct", name, sizeof(TestStruct),
- &err);
- if (err) {
- goto out;
- }
- visit_type_int(v, &(*obj)->integer, "integer", &err);
- if (err) {
- goto out_end;
- }
- visit_type_bool(v, &(*obj)->boolean, "boolean", &err);
- if (err) {
- goto out_end;
- }
- visit_type_str(v, &(*obj)->string, "string", &err);
-
-out_end:
- error_propagate(errp, err);
- err = NULL;
- visit_end_struct(v, &err);
-out:
- error_propagate(errp, err);
-}
static void test_visitor_in_struct(TestInputVisitorData *data,
const void *unused)
{
TestStruct *p = NULL;
- Error *err = NULL;
Visitor *v;
v = visitor_input_test_init(data, "{ 'integer': -42, 'boolean': true, 'string': 'foo' }");
- visit_type_TestStruct(v, &p, NULL, &err);
- g_assert(!err);
+ visit_type_TestStruct(v, &p, NULL, &error_abort);
g_assert_cmpint(p->integer, ==, -42);
g_assert(p->boolean == true);
g_assert_cmpstr(p->string, ==, "foo");
@@ -239,17 +186,10 @@ static void test_visitor_in_struct(TestInputVisitorData *data,
g_free(p);
}
-static void check_and_free_str(char *str, const char *cmp)
-{
- g_assert_cmpstr(str, ==, cmp);
- g_free(str);
-}
-
static void test_visitor_in_struct_nested(TestInputVisitorData *data,
const void *unused)
{
UserDefTwo *udp = NULL;
- Error *err = NULL;
Visitor *v;
v = visitor_input_test_init(data, "{ 'string0': 'string0', "
@@ -257,34 +197,28 @@ static void test_visitor_in_struct_nested(TestInputVisitorData *data,
"'dict2': { 'userdef': { 'integer': 42, "
"'string': 'string' }, 'string': 'string2'}}}");
- visit_type_UserDefTwo(v, &udp, NULL, &err);
- g_assert(!err);
+ visit_type_UserDefTwo(v, &udp, NULL, &error_abort);
- check_and_free_str(udp->string0, "string0");
- check_and_free_str(udp->dict1->string1, "string1");
+ g_assert_cmpstr(udp->string0, ==, "string0");
+ g_assert_cmpstr(udp->dict1->string1, ==, "string1");
g_assert_cmpint(udp->dict1->dict2->userdef->integer, ==, 42);
- check_and_free_str(udp->dict1->dict2->userdef->string, "string");
- check_and_free_str(udp->dict1->dict2->string, "string2");
+ g_assert_cmpstr(udp->dict1->dict2->userdef->string, ==, "string");
+ g_assert_cmpstr(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);
+ qapi_free_UserDefTwo(udp);
}
static void test_visitor_in_list(TestInputVisitorData *data,
const void *unused)
{
UserDefOneList *item, *head = NULL;
- Error *err = NULL;
Visitor *v;
int i;
v = visitor_input_test_init(data, "[ { 'string': 'string0', 'integer': 42 }, { 'string': 'string1', 'integer': 43 }, { 'string': 'string2', 'integer': 44 } ]");
- visit_type_UserDefOneList(v, &head, NULL, &err);
- g_assert(!err);
+ visit_type_UserDefOneList(v, &head, NULL, &error_abort);
g_assert(head != NULL);
for (i = 0, item = head; item; item = item->next, i++) {
@@ -296,13 +230,18 @@ static void test_visitor_in_list(TestInputVisitorData *data,
}
qapi_free_UserDefOneList(head);
+ head = NULL;
+
+ /* An empty list is valid */
+ v = visitor_input_test_init(data, "[]");
+ visit_type_UserDefOneList(v, &head, NULL, &error_abort);
+ g_assert(!head);
}
static void test_visitor_in_any(TestInputVisitorData *data,
const void *unused)
{
QObject *res = NULL;
- Error *err = NULL;
Visitor *v;
QInt *qint;
QBool *qbool;
@@ -311,16 +250,14 @@ static void test_visitor_in_any(TestInputVisitorData *data,
QObject *qobj;
v = visitor_input_test_init(data, "-42");
- visit_type_any(v, &res, NULL, &err);
- g_assert(!err);
+ visit_type_any(v, &res, NULL, &error_abort);
qint = qobject_to_qint(res);
g_assert(qint);
g_assert_cmpint(qint_get_int(qint), ==, -42);
qobject_decref(res);
v = visitor_input_test_init(data, "{ 'integer': -42, 'boolean': true, 'string': 'foo' }");
- visit_type_any(v, &res, NULL, &err);
- g_assert(!err);
+ visit_type_any(v, &res, NULL, &error_abort);
qdict = qobject_to_qdict(res);
g_assert(qdict && qdict_size(qdict) == 3);
qobj = qdict_get(qdict, "integer");
@@ -345,7 +282,6 @@ static void test_visitor_in_union_flat(TestInputVisitorData *data,
const void *unused)
{
Visitor *v;
- Error *err = NULL;
UserDefFlatUnion *tmp;
UserDefUnionBase *base;
@@ -355,8 +291,7 @@ static void test_visitor_in_union_flat(TestInputVisitorData *data,
"'string': 'str', "
"'boolean': true }");
- visit_type_UserDefFlatUnion(v, &tmp, NULL, &err);
- g_assert(err == NULL);
+ visit_type_UserDefFlatUnion(v, &tmp, NULL, &error_abort);
g_assert_cmpint(tmp->enum1, ==, ENUM_ONE_VALUE1);
g_assert_cmpstr(tmp->string, ==, "str");
g_assert_cmpint(tmp->integer, ==, 41);
@@ -380,22 +315,17 @@ static void test_visitor_in_alternate(TestInputVisitorData *data,
g_assert_cmpint(tmp->type, ==, USER_DEF_ALTERNATE_KIND_I);
g_assert_cmpint(tmp->u.i, ==, 42);
qapi_free_UserDefAlternate(tmp);
- visitor_input_teardown(data, NULL);
v = visitor_input_test_init(data, "'string'");
visit_type_UserDefAlternate(v, &tmp, NULL, &error_abort);
g_assert_cmpint(tmp->type, ==, USER_DEF_ALTERNATE_KIND_S);
g_assert_cmpstr(tmp->u.s, ==, "string");
qapi_free_UserDefAlternate(tmp);
- visitor_input_teardown(data, NULL);
v = visitor_input_test_init(data, "false");
visit_type_UserDefAlternate(v, &tmp, NULL, &err);
- g_assert(err);
- error_free(err);
- err = NULL;
+ error_free_or_abort(&err);
qapi_free_UserDefAlternate(tmp);
- visitor_input_teardown(data, NULL);
}
static void test_visitor_in_alternate_number(TestInputVisitorData *data,
@@ -414,11 +344,8 @@ static void test_visitor_in_alternate_number(TestInputVisitorData *data,
v = visitor_input_test_init(data, "42");
visit_type_AltStrBool(v, &asb, NULL, &err);
- g_assert(err);
- error_free(err);
- err = NULL;
+ error_free_or_abort(&err);
qapi_free_AltStrBool(asb);
- visitor_input_teardown(data, NULL);
/* FIXME: Order of alternate should not affect semantics; asn should
* parse the same as ans */
@@ -426,85 +353,68 @@ static void test_visitor_in_alternate_number(TestInputVisitorData *data,
visit_type_AltStrNum(v, &asn, NULL, &err);
/* FIXME g_assert_cmpint(asn->type, == ALT_STR_NUM_KIND_N); */
/* FIXME g_assert_cmpfloat(asn->u.n, ==, 42); */
- g_assert(err);
- error_free(err);
- err = NULL;
+ error_free_or_abort(&err);
qapi_free_AltStrNum(asn);
- visitor_input_teardown(data, NULL);
v = visitor_input_test_init(data, "42");
visit_type_AltNumStr(v, &ans, NULL, &error_abort);
g_assert_cmpint(ans->type, ==, ALT_NUM_STR_KIND_N);
g_assert_cmpfloat(ans->u.n, ==, 42);
qapi_free_AltNumStr(ans);
- visitor_input_teardown(data, NULL);
v = visitor_input_test_init(data, "42");
visit_type_AltStrInt(v, &asi, NULL, &error_abort);
g_assert_cmpint(asi->type, ==, ALT_STR_INT_KIND_I);
g_assert_cmpint(asi->u.i, ==, 42);
qapi_free_AltStrInt(asi);
- visitor_input_teardown(data, NULL);
v = visitor_input_test_init(data, "42");
visit_type_AltIntNum(v, &ain, NULL, &error_abort);
g_assert_cmpint(ain->type, ==, ALT_INT_NUM_KIND_I);
g_assert_cmpint(ain->u.i, ==, 42);
qapi_free_AltIntNum(ain);
- visitor_input_teardown(data, NULL);
v = visitor_input_test_init(data, "42");
visit_type_AltNumInt(v, &ani, NULL, &error_abort);
g_assert_cmpint(ani->type, ==, ALT_NUM_INT_KIND_I);
g_assert_cmpint(ani->u.i, ==, 42);
qapi_free_AltNumInt(ani);
- visitor_input_teardown(data, NULL);
/* Parsing a double */
v = visitor_input_test_init(data, "42.5");
visit_type_AltStrBool(v, &asb, NULL, &err);
- g_assert(err);
- error_free(err);
- err = NULL;
+ error_free_or_abort(&err);
qapi_free_AltStrBool(asb);
- visitor_input_teardown(data, NULL);
v = visitor_input_test_init(data, "42.5");
visit_type_AltStrNum(v, &asn, NULL, &error_abort);
g_assert_cmpint(asn->type, ==, ALT_STR_NUM_KIND_N);
g_assert_cmpfloat(asn->u.n, ==, 42.5);
qapi_free_AltStrNum(asn);
- visitor_input_teardown(data, NULL);
v = visitor_input_test_init(data, "42.5");
visit_type_AltNumStr(v, &ans, NULL, &error_abort);
g_assert_cmpint(ans->type, ==, ALT_NUM_STR_KIND_N);
g_assert_cmpfloat(ans->u.n, ==, 42.5);
qapi_free_AltNumStr(ans);
- visitor_input_teardown(data, NULL);
v = visitor_input_test_init(data, "42.5");
visit_type_AltStrInt(v, &asi, NULL, &err);
- g_assert(err);
- error_free(err);
- err = NULL;
+ error_free_or_abort(&err);
qapi_free_AltStrInt(asi);
- visitor_input_teardown(data, NULL);
v = visitor_input_test_init(data, "42.5");
visit_type_AltIntNum(v, &ain, NULL, &error_abort);
g_assert_cmpint(ain->type, ==, ALT_INT_NUM_KIND_N);
g_assert_cmpfloat(ain->u.n, ==, 42.5);
qapi_free_AltIntNum(ain);
- visitor_input_teardown(data, NULL);
v = visitor_input_test_init(data, "42.5");
visit_type_AltNumInt(v, &ani, NULL, &error_abort);
g_assert_cmpint(ani->type, ==, ALT_NUM_INT_KIND_N);
g_assert_cmpfloat(ani->u.n, ==, 42.5);
qapi_free_AltNumInt(ani);
- visitor_input_teardown(data, NULL);
}
static void test_native_list_integer_helper(TestInputVisitorData *data,
@@ -512,7 +422,6 @@ static void test_native_list_integer_helper(TestInputVisitorData *data,
UserDefNativeListUnionKind kind)
{
UserDefNativeListUnion *cvalue = NULL;
- Error *err = NULL;
Visitor *v;
GString *gstr_list = g_string_new("");
GString *gstr_union = g_string_new("");
@@ -529,8 +438,7 @@ static void test_native_list_integer_helper(TestInputVisitorData *data,
gstr_list->str);
v = visitor_input_test_init_raw(data, gstr_union->str);
- visit_type_UserDefNativeListUnion(v, &cvalue, NULL, &err);
- g_assert(err == NULL);
+ visit_type_UserDefNativeListUnion(v, &cvalue, NULL, &error_abort);
g_assert(cvalue != NULL);
g_assert_cmpint(cvalue->type, ==, kind);
@@ -675,7 +583,6 @@ static void test_visitor_in_native_list_bool(TestInputVisitorData *data,
{
UserDefNativeListUnion *cvalue = NULL;
boolList *elem = NULL;
- Error *err = NULL;
Visitor *v;
GString *gstr_list = g_string_new("");
GString *gstr_union = g_string_new("");
@@ -692,8 +599,7 @@ static void test_visitor_in_native_list_bool(TestInputVisitorData *data,
gstr_list->str);
v = visitor_input_test_init_raw(data, gstr_union->str);
- visit_type_UserDefNativeListUnion(v, &cvalue, NULL, &err);
- g_assert(err == NULL);
+ visit_type_UserDefNativeListUnion(v, &cvalue, NULL, &error_abort);
g_assert(cvalue != NULL);
g_assert_cmpint(cvalue->type, ==, USER_DEF_NATIVE_LIST_UNION_KIND_BOOLEAN);
@@ -711,7 +617,6 @@ static void test_visitor_in_native_list_string(TestInputVisitorData *data,
{
UserDefNativeListUnion *cvalue = NULL;
strList *elem = NULL;
- Error *err = NULL;
Visitor *v;
GString *gstr_list = g_string_new("");
GString *gstr_union = g_string_new("");
@@ -727,8 +632,7 @@ static void test_visitor_in_native_list_string(TestInputVisitorData *data,
gstr_list->str);
v = visitor_input_test_init_raw(data, gstr_union->str);
- visit_type_UserDefNativeListUnion(v, &cvalue, NULL, &err);
- g_assert(err == NULL);
+ visit_type_UserDefNativeListUnion(v, &cvalue, NULL, &error_abort);
g_assert(cvalue != NULL);
g_assert_cmpint(cvalue->type, ==, USER_DEF_NATIVE_LIST_UNION_KIND_STRING);
@@ -750,7 +654,6 @@ static void test_visitor_in_native_list_number(TestInputVisitorData *data,
{
UserDefNativeListUnion *cvalue = NULL;
numberList *elem = NULL;
- Error *err = NULL;
Visitor *v;
GString *gstr_list = g_string_new("");
GString *gstr_union = g_string_new("");
@@ -766,8 +669,7 @@ static void test_visitor_in_native_list_number(TestInputVisitorData *data,
gstr_list->str);
v = visitor_input_test_init_raw(data, gstr_union->str);
- visit_type_UserDefNativeListUnion(v, &cvalue, NULL, &err);
- g_assert(err == NULL);
+ visit_type_UserDefNativeListUnion(v, &cvalue, NULL, &error_abort);
g_assert(cvalue != NULL);
g_assert_cmpint(cvalue->type, ==, USER_DEF_NATIVE_LIST_UNION_KIND_NUMBER);
@@ -802,18 +704,69 @@ static void test_visitor_in_errors(TestInputVisitorData *data,
TestStruct *p = NULL;
Error *err = NULL;
Visitor *v;
+ strList *q = NULL;
- v = visitor_input_test_init(data, "{ 'integer': false, 'boolean': 'foo', 'string': -42 }");
+ v = visitor_input_test_init(data, "{ 'integer': false, 'boolean': 'foo', "
+ "'string': -42 }");
visit_type_TestStruct(v, &p, NULL, &err);
- g_assert(err);
+ error_free_or_abort(&err);
/* FIXME - a failed parse should not leave a partially-allocated p
* for us to clean up; this could cause callers to leak memory. */
g_assert(p->string == NULL);
- error_free(err);
g_free(p->string);
g_free(p);
+
+ v = visitor_input_test_init(data, "[ '1', '2', false, '3' ]");
+ visit_type_strList(v, &q, NULL, &err);
+ error_free_or_abort(&err);
+ assert(q);
+ qapi_free_strList(q);
+}
+
+static void test_visitor_in_wrong_type(TestInputVisitorData *data,
+ const void *unused)
+{
+ TestStruct *p = NULL;
+ Visitor *v;
+ strList *q = NULL;
+ int64_t i;
+ Error *err = NULL;
+
+ /* Make sure arrays and structs cannot be confused */
+
+ v = visitor_input_test_init(data, "[]");
+ visit_type_TestStruct(v, &p, NULL, &err);
+ error_free_or_abort(&err);
+ g_assert(!p);
+
+ v = visitor_input_test_init(data, "{}");
+ visit_type_strList(v, &q, NULL, &err);
+ error_free_or_abort(&err);
+ assert(!q);
+
+ /* Make sure primitives and struct cannot be confused */
+
+ v = visitor_input_test_init(data, "1");
+ visit_type_TestStruct(v, &p, NULL, &err);
+ error_free_or_abort(&err);
+ g_assert(!p);
+
+ v = visitor_input_test_init(data, "{}");
+ visit_type_int(v, &i, NULL, &err);
+ error_free_or_abort(&err);
+
+ /* Make sure primitives and arrays cannot be confused */
+
+ v = visitor_input_test_init(data, "1");
+ visit_type_strList(v, &q, NULL, &err);
+ error_free_or_abort(&err);
+ assert(!q);
+
+ v = visitor_input_test_init(data, "[]");
+ visit_type_int(v, &i, NULL, &err);
+ error_free_or_abort(&err);
}
int main(int argc, char **argv)
@@ -848,6 +801,8 @@ int main(int argc, char **argv)
&in_visitor_data, test_visitor_in_alternate);
input_visitor_test_add("/visitor/input/errors",
&in_visitor_data, test_visitor_in_errors);
+ input_visitor_test_add("/visitor/input/wrong-type",
+ &in_visitor_data, test_visitor_in_wrong_type);
input_visitor_test_add("/visitor/input/alternate-number",
&in_visitor_data, test_visitor_in_alternate_number);
input_visitor_test_add("/visitor/input/native_list/int",
diff --git a/tests/test-qmp-output-visitor.c b/tests/test-qmp-output-visitor.c
index 09d0dd8..0d0c859 100644
--- a/tests/test-qmp-output-visitor.c
+++ b/tests/test-qmp-output-visitor.c
@@ -45,11 +45,9 @@ static void test_visitor_out_int(TestOutputVisitorData *data,
const void *unused)
{
int64_t value = -42;
- Error *err = NULL;
QObject *obj;
- visit_type_int(data->ov, &value, NULL, &err);
- g_assert(!err);
+ visit_type_int(data->ov, &value, NULL, &error_abort);
obj = qmp_output_get_qobject(data->qov);
g_assert(obj != NULL);
@@ -62,12 +60,10 @@ static void test_visitor_out_int(TestOutputVisitorData *data,
static void test_visitor_out_bool(TestOutputVisitorData *data,
const void *unused)
{
- Error *err = NULL;
bool value = true;
QObject *obj;
- visit_type_bool(data->ov, &value, NULL, &err);
- g_assert(!err);
+ visit_type_bool(data->ov, &value, NULL, &error_abort);
obj = qmp_output_get_qobject(data->qov);
g_assert(obj != NULL);
@@ -81,11 +77,9 @@ static void test_visitor_out_number(TestOutputVisitorData *data,
const void *unused)
{
double value = 3.14;
- Error *err = NULL;
QObject *obj;
- visit_type_number(data->ov, &value, NULL, &err);
- g_assert(!err);
+ visit_type_number(data->ov, &value, NULL, &error_abort);
obj = qmp_output_get_qobject(data->qov);
g_assert(obj != NULL);
@@ -99,11 +93,9 @@ static void test_visitor_out_string(TestOutputVisitorData *data,
const void *unused)
{
char *string = (char *) "Q E M U";
- Error *err = NULL;
QObject *obj;
- visit_type_str(data->ov, &string, NULL, &err);
- g_assert(!err);
+ visit_type_str(data->ov, &string, NULL, &error_abort);
obj = qmp_output_get_qobject(data->qov);
g_assert(obj != NULL);
@@ -117,12 +109,10 @@ static void test_visitor_out_no_string(TestOutputVisitorData *data,
const void *unused)
{
char *string = NULL;
- Error *err = NULL;
QObject *obj;
/* A null string should return "" */
- visit_type_str(data->ov, &string, NULL, &err);
- g_assert(!err);
+ visit_type_str(data->ov, &string, NULL, &error_abort);
obj = qmp_output_get_qobject(data->qov);
g_assert(obj != NULL);
@@ -135,13 +125,11 @@ static void test_visitor_out_no_string(TestOutputVisitorData *data,
static void test_visitor_out_enum(TestOutputVisitorData *data,
const void *unused)
{
- Error *err = NULL;
QObject *obj;
EnumOne i;
for (i = 0; i < ENUM_ONE_MAX; i++) {
- visit_type_EnumOne(data->ov, &i, "unused", &err);
- g_assert(!err);
+ visit_type_EnumOne(data->ov, &i, "unused", &error_abort);
obj = qmp_output_get_qobject(data->qov);
g_assert(obj != NULL);
@@ -166,41 +154,6 @@ static void test_visitor_out_enum_errors(TestOutputVisitorData *data,
}
}
-typedef struct TestStruct
-{
- int64_t integer;
- bool boolean;
- char *string;
-} TestStruct;
-
-static void visit_type_TestStruct(Visitor *v, TestStruct **obj,
- const char *name, Error **errp)
-{
- Error *err = NULL;
-
- visit_start_struct(v, (void **)obj, "TestStruct", name, sizeof(TestStruct),
- &err);
- if (err) {
- goto out;
- }
-
- visit_type_int(v, &(*obj)->integer, "integer", &err);
- if (err) {
- goto out_end;
- }
- visit_type_bool(v, &(*obj)->boolean, "boolean", &err);
- if (err) {
- goto out_end;
- }
- visit_type_str(v, &(*obj)->string, "string", &err);
-
-out_end:
- error_propagate(errp, err);
- err = NULL;
- visit_end_struct(v, &err);
-out:
- error_propagate(errp, err);
-}
static void test_visitor_out_struct(TestOutputVisitorData *data,
const void *unused)
@@ -209,12 +162,10 @@ static void test_visitor_out_struct(TestOutputVisitorData *data,
.boolean = false,
.string = (char *) "foo"};
TestStruct *p = &test_struct;
- Error *err = NULL;
QObject *obj;
QDict *qdict;
- visit_type_TestStruct(data->ov, &p, NULL, &err);
- g_assert(!err);
+ visit_type_TestStruct(data->ov, &p, NULL, &error_abort);
obj = qmp_output_get_qobject(data->qov);
g_assert(obj != NULL);
@@ -233,7 +184,6 @@ static void test_visitor_out_struct_nested(TestOutputVisitorData *data,
const void *unused)
{
int64_t value = 42;
- Error *err = NULL;
UserDefTwo *ud2;
QObject *obj;
QDict *qdict, *dict1, *dict2, *dict3, *userdef;
@@ -260,8 +210,7 @@ static void test_visitor_out_struct_nested(TestOutputVisitorData *data,
ud2->dict1->dict3->userdef->integer = value;
ud2->dict1->dict3->string = g_strdup(strings[3]);
- visit_type_UserDefTwo(data->ov, &ud2, "unused", &err);
- g_assert(!err);
+ visit_type_UserDefTwo(data->ov, &ud2, "unused", &error_abort);
obj = qmp_output_get_qobject(data->qov);
g_assert(obj != NULL);
@@ -314,57 +263,33 @@ static void test_visitor_out_struct_errors(TestOutputVisitorData *data,
}
}
-typedef struct TestStructList
-{
- union {
- TestStruct *value;
- uint64_t padding;
- };
- struct TestStructList *next;
-} TestStructList;
-
-static void visit_type_TestStructList(Visitor *v, TestStructList **obj,
- const char *name, Error **errp)
-{
- GenericList *i, **head = (GenericList **)obj;
-
- visit_start_list(v, name, errp);
-
- for (*head = i = visit_next_list(v, head, errp); i; i = visit_next_list(v, &i, errp)) {
- TestStructList *native_i = (TestStructList *)i;
- visit_type_TestStruct(v, &native_i->value, NULL, errp);
- }
-
- visit_end_list(v, errp);
-}
static void test_visitor_out_list(TestOutputVisitorData *data,
const void *unused)
{
- char *value_str = (char *) "list value";
+ const char *value_str = "list value";
TestStructList *p, *head = NULL;
const int max_items = 10;
bool value_bool = true;
int value_int = 10;
- Error *err = NULL;
QListEntry *entry;
QObject *obj;
QList *qlist;
int i;
+ /* Build the list in reverse order... */
for (i = 0; i < max_items; i++) {
p = g_malloc0(sizeof(*p));
p->value = g_malloc0(sizeof(*p->value));
- p->value->integer = value_int;
+ p->value->integer = value_int + (max_items - i - 1);
p->value->boolean = value_bool;
- p->value->string = value_str;
+ p->value->string = g_strdup(value_str);
p->next = head;
head = p;
}
- visit_type_TestStructList(data->ov, &head, NULL, &err);
- g_assert(!err);
+ visit_type_TestStructList(data->ov, &head, NULL, &error_abort);
obj = qmp_output_get_qobject(data->qov);
g_assert(obj != NULL);
@@ -373,6 +298,7 @@ static void test_visitor_out_list(TestOutputVisitorData *data,
qlist = qobject_to_qlist(obj);
g_assert(!qlist_empty(qlist));
+ /* ...and ensure that the visitor sees it in order */
i = 0;
QLIST_FOREACH_ENTRY(qlist, entry) {
QDict *qdict;
@@ -380,7 +306,7 @@ static void test_visitor_out_list(TestOutputVisitorData *data,
g_assert(qobject_type(entry->value) == QTYPE_QDICT);
qdict = qobject_to_qdict(entry->value);
g_assert_cmpint(qdict_size(qdict), ==, 3);
- g_assert_cmpint(qdict_get_int(qdict, "integer"), ==, value_int);
+ g_assert_cmpint(qdict_get_int(qdict, "integer"), ==, value_int + i);
g_assert_cmpint(qdict_get_bool(qdict, "boolean"), ==, value_bool);
g_assert_cmpstr(qdict_get_str(qdict, "string"), ==, value_str);
i++;
@@ -388,13 +314,7 @@ static void test_visitor_out_list(TestOutputVisitorData *data,
g_assert_cmpint(i, ==, max_items);
QDECREF(qlist);
-
- for (p = head; p;) {
- TestStructList *tmp = p->next;
- g_free(p->value);
- g_free(p);
- p = tmp;
- }
+ qapi_free_TestStructList(head);
}
static void test_visitor_out_list_qapi_free(TestOutputVisitorData *data,
@@ -429,7 +349,6 @@ static void test_visitor_out_any(TestOutputVisitorData *data,
const void *unused)
{
QObject *qobj;
- Error *err = NULL;
QInt *qint;
QBool *qbool;
QString *qstring;
@@ -437,8 +356,7 @@ static void test_visitor_out_any(TestOutputVisitorData *data,
QObject *obj;
qobj = QOBJECT(qint_from_int(-42));
- visit_type_any(data->ov, &qobj, NULL, &err);
- g_assert(!err);
+ visit_type_any(data->ov, &qobj, NULL, &error_abort);
obj = qmp_output_get_qobject(data->qov);
g_assert(obj != NULL);
g_assert(qobject_type(obj) == QTYPE_QINT);
@@ -451,8 +369,8 @@ static void test_visitor_out_any(TestOutputVisitorData *data,
qdict_put(qdict, "boolean", qbool_from_bool(true));
qdict_put(qdict, "string", qstring_from_str("foo"));
qobj = QOBJECT(qdict);
- visit_type_any(data->ov, &qobj, NULL, &err);
- g_assert(!err);
+ visit_type_any(data->ov, &qobj, NULL, &error_abort);
+ qobject_decref(qobj);
obj = qmp_output_get_qobject(data->qov);
g_assert(obj != NULL);
qdict = qobject_to_qdict(obj);
@@ -473,7 +391,6 @@ static void test_visitor_out_any(TestOutputVisitorData *data,
g_assert(qstring);
g_assert_cmpstr(qstring_get_str(qstring), ==, "foo");
qobject_decref(obj);
- qobject_decref(qobj);
}
static void test_visitor_out_union_flat(TestOutputVisitorData *data,
@@ -482,8 +399,6 @@ static void test_visitor_out_union_flat(TestOutputVisitorData *data,
QObject *arg;
QDict *qdict;
- Error *err = NULL;
-
UserDefFlatUnion *tmp = g_malloc0(sizeof(UserDefFlatUnion));
tmp->enum1 = ENUM_ONE_VALUE1;
tmp->string = g_strdup("str");
@@ -491,8 +406,7 @@ static void test_visitor_out_union_flat(TestOutputVisitorData *data,
tmp->integer = 41;
tmp->u.value1->boolean = true;
- visit_type_UserDefFlatUnion(data->ov, &tmp, NULL, &err);
- g_assert(err == NULL);
+ visit_type_UserDefFlatUnion(data->ov, &tmp, NULL, &error_abort);
arg = qmp_output_get_qobject(data->qov);
g_assert(qobject_type(arg) == QTYPE_QDICT);
@@ -511,20 +425,33 @@ static void test_visitor_out_alternate(TestOutputVisitorData *data,
const void *unused)
{
QObject *arg;
- Error *err = NULL;
+ UserDefAlternate *tmp;
- UserDefAlternate *tmp = g_malloc0(sizeof(UserDefAlternate));
+ tmp = g_new0(UserDefAlternate, 1);
tmp->type = USER_DEF_ALTERNATE_KIND_I;
tmp->u.i = 42;
- visit_type_UserDefAlternate(data->ov, &tmp, NULL, &err);
- g_assert(err == NULL);
+ visit_type_UserDefAlternate(data->ov, &tmp, NULL, &error_abort);
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_UserDefAlternate(tmp);
+ qobject_decref(arg);
+
+ tmp = g_new0(UserDefAlternate, 1);
+ tmp->type = USER_DEF_ALTERNATE_KIND_S;
+ tmp->u.s = g_strdup("hello");
+
+ visit_type_UserDefAlternate(data->ov, &tmp, NULL, &error_abort);
+ arg = qmp_output_get_qobject(data->qov);
+
+ g_assert(qobject_type(arg) == QTYPE_QSTRING);
+ g_assert_cmpstr(qstring_get_str(qobject_to_qstring(arg)), ==, "hello");
+
+ qapi_free_UserDefAlternate(tmp);
+ qobject_decref(arg);
}
static void test_visitor_out_empty(TestOutputVisitorData *data,
@@ -758,14 +685,12 @@ static void test_native_list(TestOutputVisitorData *data,
UserDefNativeListUnionKind kind)
{
UserDefNativeListUnion *cvalue = g_new0(UserDefNativeListUnion, 1);
- Error *err = NULL;
QObject *obj;
cvalue->type = kind;
init_native_list(cvalue);
- visit_type_UserDefNativeListUnion(data->ov, &cvalue, NULL, &err);
- g_assert(err == NULL);
+ visit_type_UserDefNativeListUnion(data->ov, &cvalue, NULL, &error_abort);
obj = qmp_output_get_qobject(data->qov);
check_native_list(obj, cvalue->type);
diff --git a/tests/test-timed-average.c b/tests/test-timed-average.c
new file mode 100644
index 0000000..a049799
--- /dev/null
+++ b/tests/test-timed-average.c
@@ -0,0 +1,90 @@
+/*
+ * Timed average computation tests
+ *
+ * Copyright Nodalink, EURL. 2014
+ *
+ * Authors:
+ * BenoƮt Canet <benoit.canet@nodalink.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+
+#include <glib.h>
+#include <unistd.h>
+
+#include "qemu/timed-average.h"
+
+/* This is the clock for QEMU_CLOCK_VIRTUAL */
+static int64_t my_clock_value;
+
+int64_t cpu_get_clock(void)
+{
+ return my_clock_value;
+}
+
+static void account(TimedAverage *ta)
+{
+ timed_average_account(ta, 1);
+ timed_average_account(ta, 5);
+ timed_average_account(ta, 2);
+ timed_average_account(ta, 4);
+ timed_average_account(ta, 3);
+}
+
+static void test_average(void)
+{
+ TimedAverage ta;
+ uint64_t result;
+ int i;
+
+ /* we will compute some average on a period of 1 second */
+ timed_average_init(&ta, QEMU_CLOCK_VIRTUAL, NANOSECONDS_PER_SECOND);
+
+ result = timed_average_min(&ta);
+ g_assert(result == 0);
+ result = timed_average_avg(&ta);
+ g_assert(result == 0);
+ result = timed_average_max(&ta);
+ g_assert(result == 0);
+
+ for (i = 0; i < 100; i++) {
+ account(&ta);
+ result = timed_average_min(&ta);
+ g_assert(result == 1);
+ result = timed_average_avg(&ta);
+ g_assert(result == 3);
+ result = timed_average_max(&ta);
+ g_assert(result == 5);
+ my_clock_value += NANOSECONDS_PER_SECOND / 10;
+ }
+
+ my_clock_value += NANOSECONDS_PER_SECOND * 100;
+
+ result = timed_average_min(&ta);
+ g_assert(result == 0);
+ result = timed_average_avg(&ta);
+ g_assert(result == 0);
+ result = timed_average_max(&ta);
+ g_assert(result == 0);
+
+ for (i = 0; i < 100; i++) {
+ account(&ta);
+ result = timed_average_min(&ta);
+ g_assert(result == 1);
+ result = timed_average_avg(&ta);
+ g_assert(result == 3);
+ result = timed_average_max(&ta);
+ g_assert(result == 5);
+ my_clock_value += NANOSECONDS_PER_SECOND / 10;
+ }
+}
+
+int main(int argc, char **argv)
+{
+ /* tests in the same order as the header function declarations */
+ g_test_init(&argc, &argv, NULL);
+ g_test_add_func("/timed-average/average", test_average);
+ return g_test_run();
+}
+
diff --git a/tests/test-visitor-serialization.c b/tests/test-visitor-serialization.c
index 634563b..9f67f9e 100644
--- a/tests/test-visitor-serialization.c
+++ b/tests/test-visitor-serialization.c
@@ -186,40 +186,6 @@ static void visit_primitive_list(Visitor *v, void **native, Error **errp)
}
}
-typedef struct TestStruct
-{
- int64_t integer;
- bool boolean;
- char *string;
-} TestStruct;
-
-static void visit_type_TestStruct(Visitor *v, TestStruct **obj,
- const char *name, Error **errp)
-{
- Error *err = NULL;
-
- visit_start_struct(v, (void **)obj, NULL, name, sizeof(TestStruct), &err);
- if (err) {
- goto out;
- }
-
- visit_type_int(v, &(*obj)->integer, "integer", &err);
- if (err) {
- goto out_end;
- }
- visit_type_bool(v, &(*obj)->boolean, "boolean", &err);
- if (err) {
- goto out_end;
- }
- visit_type_str(v, &(*obj)->string, "string", &err);
-
-out_end:
- error_propagate(errp, err);
- err = NULL;
- visit_end_struct(v, &err);
-out:
- error_propagate(errp, err);
-}
static TestStruct *struct_create(void)
{
@@ -336,14 +302,13 @@ static void test_primitives(gconstpointer opaque)
const SerializeOps *ops = args->ops;
PrimitiveType *pt = args->test_data;
PrimitiveType *pt_copy = g_malloc0(sizeof(*pt_copy));
- Error *err = NULL;
void *serialize_data;
pt_copy->type = pt->type;
- ops->serialize(pt, &serialize_data, visit_primitive_type, &err);
- ops->deserialize((void **)&pt_copy, serialize_data, visit_primitive_type, &err);
+ ops->serialize(pt, &serialize_data, visit_primitive_type, &error_abort);
+ ops->deserialize((void **)&pt_copy, serialize_data, visit_primitive_type,
+ &error_abort);
- g_assert(err == NULL);
g_assert(pt_copy != NULL);
if (pt->type == PTYPE_STRING) {
g_assert_cmpstr(pt->value.string, ==, pt_copy->value.string);
@@ -379,7 +344,6 @@ static void test_primitive_lists(gconstpointer opaque)
PrimitiveList pl = { .value = { NULL } };
PrimitiveList pl_copy = { .value = { NULL } };
PrimitiveList *pl_copy_ptr = &pl_copy;
- Error *err = NULL;
void *serialize_data;
void *cur_head = NULL;
int i;
@@ -526,10 +490,11 @@ static void test_primitive_lists(gconstpointer opaque)
}
}
- ops->serialize((void **)&pl, &serialize_data, visit_primitive_list, &err);
- ops->deserialize((void **)&pl_copy_ptr, serialize_data, visit_primitive_list, &err);
+ ops->serialize((void **)&pl, &serialize_data, visit_primitive_list,
+ &error_abort);
+ ops->deserialize((void **)&pl_copy_ptr, serialize_data,
+ visit_primitive_list, &error_abort);
- g_assert(err == NULL);
i = 0;
/* compare our deserialized list of primitives to the original */
@@ -686,10 +651,8 @@ static void test_primitive_lists(gconstpointer opaque)
g_assert_cmpint(i, ==, 33);
ops->cleanup(serialize_data);
- dealloc_helper(&pl, visit_primitive_list, &err);
- g_assert(!err);
- dealloc_helper(&pl_copy, visit_primitive_list, &err);
- g_assert(!err);
+ dealloc_helper(&pl, visit_primitive_list, &error_abort);
+ dealloc_helper(&pl_copy, visit_primitive_list, &error_abort);
g_free(args);
}
@@ -699,13 +662,12 @@ static void test_struct(gconstpointer opaque)
const SerializeOps *ops = args->ops;
TestStruct *ts = struct_create();
TestStruct *ts_copy = NULL;
- Error *err = NULL;
void *serialize_data;
- ops->serialize(ts, &serialize_data, visit_struct, &err);
- ops->deserialize((void **)&ts_copy, serialize_data, visit_struct, &err);
+ ops->serialize(ts, &serialize_data, visit_struct, &error_abort);
+ ops->deserialize((void **)&ts_copy, serialize_data, visit_struct,
+ &error_abort);
- g_assert(err == NULL);
struct_compare(ts, ts_copy);
struct_cleanup(ts);
@@ -721,14 +683,12 @@ static void test_nested_struct(gconstpointer opaque)
const SerializeOps *ops = args->ops;
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->serialize(udnp, &serialize_data, visit_nested_struct, &error_abort);
ops->deserialize((void **)&udnp_copy, serialize_data, visit_nested_struct,
- &err);
+ &error_abort);
- g_assert(err == NULL);
nested_struct_compare(udnp, udnp_copy);
nested_struct_cleanup(udnp);
@@ -743,7 +703,6 @@ static void test_nested_struct_list(gconstpointer opaque)
TestArgs *args = (TestArgs *) opaque;
const SerializeOps *ops = args->ops;
UserDefTwoList *listp = NULL, *tmp, *tmp_copy, *listp_copy = NULL;
- Error *err = NULL;
void *serialize_data;
int i = 0;
@@ -754,11 +713,10 @@ static void test_nested_struct_list(gconstpointer opaque)
listp = tmp;
}
- ops->serialize(listp, &serialize_data, visit_nested_struct_list, &err);
+ ops->serialize(listp, &serialize_data, visit_nested_struct_list,
+ &error_abort);
ops->deserialize((void **)&listp_copy, serialize_data,
- visit_nested_struct_list, &err);
-
- g_assert(err == NULL);
+ visit_nested_struct_list, &error_abort);
tmp = listp;
tmp_copy = listp_copy;
diff --git a/tests/vhost-user-bridge.c b/tests/vhost-user-bridge.c
index fa18ad5..864f69e 100644
--- a/tests/vhost-user-bridge.c
+++ b/tests/vhost-user-bridge.c
@@ -188,7 +188,7 @@ typedef enum VhostUserRequest {
VHOST_USER_GET_FEATURES = 1,
VHOST_USER_SET_FEATURES = 2,
VHOST_USER_SET_OWNER = 3,
- VHOST_USER_RESET_DEVICE = 4,
+ VHOST_USER_RESET_OWNER = 4,
VHOST_USER_SET_MEM_TABLE = 5,
VHOST_USER_SET_LOG_BASE = 6,
VHOST_USER_SET_LOG_FD = 7,
@@ -274,7 +274,7 @@ static const char *vubr_request_str[] = {
[VHOST_USER_GET_FEATURES] = "VHOST_USER_GET_FEATURES",
[VHOST_USER_SET_FEATURES] = "VHOST_USER_SET_FEATURES",
[VHOST_USER_SET_OWNER] = "VHOST_USER_SET_OWNER",
- [VHOST_USER_RESET_DEVICE] = "VHOST_USER_RESET_DEVICE",
+ [VHOST_USER_RESET_OWNER] = "VHOST_USER_RESET_OWNER",
[VHOST_USER_SET_MEM_TABLE] = "VHOST_USER_SET_MEM_TABLE",
[VHOST_USER_SET_LOG_BASE] = "VHOST_USER_SET_LOG_BASE",
[VHOST_USER_SET_LOG_FD] = "VHOST_USER_SET_LOG_FD",
@@ -921,7 +921,7 @@ vubr_execute_request(VubrDev *dev, VhostUserMsg *vmsg)
return vubr_set_features_exec(dev, vmsg);
case VHOST_USER_SET_OWNER:
return vubr_set_owner_exec(dev, vmsg);
- case VHOST_USER_RESET_DEVICE:
+ case VHOST_USER_RESET_OWNER:
return vubr_reset_device_exec(dev, vmsg);
case VHOST_USER_SET_MEM_TABLE:
return vubr_set_mem_table_exec(dev, vmsg);
diff --git a/tests/vhost-user-test.c b/tests/vhost-user-test.c
index b6dde75..01cfc7e 100644
--- a/tests/vhost-user-test.c
+++ b/tests/vhost-user-test.c
@@ -57,7 +57,7 @@ typedef enum VhostUserRequest {
VHOST_USER_GET_FEATURES = 1,
VHOST_USER_SET_FEATURES = 2,
VHOST_USER_SET_OWNER = 3,
- VHOST_USER_RESET_DEVICE = 4,
+ VHOST_USER_RESET_OWNER = 4,
VHOST_USER_SET_MEM_TABLE = 5,
VHOST_USER_SET_LOG_BASE = 6,
VHOST_USER_SET_LOG_FD = 7,
@@ -86,6 +86,11 @@ typedef struct VhostUserMemory {
VhostUserMemoryRegion regions[VHOST_MEMORY_MAX_NREGIONS];
} VhostUserMemory;
+typedef struct VhostUserLog {
+ uint64_t mmap_size;
+ uint64_t mmap_offset;
+} VhostUserLog;
+
typedef struct VhostUserMsg {
VhostUserRequest request;
@@ -94,10 +99,13 @@ typedef struct VhostUserMsg {
uint32_t flags;
uint32_t size; /* the following payload size */
union {
+#define VHOST_USER_VRING_IDX_MASK (0xff)
+#define VHOST_USER_VRING_NOFD_MASK (0x1<<8)
uint64_t u64;
struct vhost_vring_state state;
struct vhost_vring_addr addr;
VhostUserMemory memory;
+ VhostUserLog log;
} payload;
} QEMU_PACKED VhostUserMsg;
@@ -307,7 +315,7 @@ static void chr_read(void *opaque, const uint8_t *buf, int size)
g_cond_signal(&s->data_cond);
break;
- case VHOST_USER_RESET_DEVICE:
+ case VHOST_USER_RESET_OWNER:
s->fds_num = 0;
break;
OpenPOWER on IntegriCloud