diff options
Diffstat (limited to 'tests')
61 files changed, 2831 insertions, 280 deletions
diff --git a/tests/.gitignore b/tests/.gitignore index 18e58b2183..fb62d2299b 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -86,6 +86,7 @@ test-thread-pool test-throttle test-timed-average test-uuid +test-util-sockets test-visitor-serialization test-vmstate test-write-threshold diff --git a/tests/Makefile.include b/tests/Makefile.include index ef9b88c369..0b277036df 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -146,6 +146,7 @@ ifneq (,$(findstring qemu-ga,$(TOOLS))) check-unit-$(CONFIG_LINUX) += tests/test-qga$(EXESUF) endif check-unit-y += tests/test-timed-average$(EXESUF) +check-unit-y += tests/test-util-sockets$(EXESUF) check-unit-y += tests/test-io-task$(EXESUF) check-unit-y += tests/test-io-channel-socket$(EXESUF) check-unit-y += tests/test-io-channel-file$(EXESUF) @@ -399,6 +400,7 @@ check-qtest-s390x-y += tests/virtio-console-test$(EXESUF) check-qtest-s390x-y += tests/virtio-serial-test$(EXESUF) check-qtest-s390x-y += tests/cpu-plug-test$(EXESUF) +check-qtest-generic-y += tests/machine-none-test$(EXESUF) check-qtest-generic-y += tests/qom-test$(EXESUF) check-qtest-generic-y += tests/test-hmp$(EXESUF) @@ -713,9 +715,11 @@ tests/test-crypto-tlscredsx509$(EXESUF): tests/test-crypto-tlscredsx509.o \ tests/test-crypto-tlssession.o-cflags := $(TASN1_CFLAGS) tests/test-crypto-tlssession$(EXESUF): tests/test-crypto-tlssession.o \ tests/crypto-tls-x509-helpers.o tests/pkix_asn1_tab.o $(test-crypto-obj-y) +tests/test-util-sockets$(EXESUF): tests/test-util-sockets.o \ + tests/socket-helpers.o $(test-util-obj-y) tests/test-io-task$(EXESUF): tests/test-io-task.o $(test-io-obj-y) tests/test-io-channel-socket$(EXESUF): tests/test-io-channel-socket.o \ - tests/io-channel-helpers.o $(test-io-obj-y) + tests/io-channel-helpers.o tests/socket-helpers.o $(test-io-obj-y) tests/tpm-crb-test$(EXESUF): tests/tpm-crb-test.o tests/tpm-emu.o $(test-io-obj-y) tests/tpm-tis-test$(EXESUF): tests/tpm-tis-test.o tests/tpm-emu.o $(test-io-obj-y) tests/test-io-channel-file$(EXESUF): tests/test-io-channel-file.o \ @@ -794,6 +798,7 @@ tests/display-vga-test$(EXESUF): tests/display-vga-test.o tests/ipoctal232-test$(EXESUF): tests/ipoctal232-test.o tests/qom-test$(EXESUF): tests/qom-test.o tests/test-hmp$(EXESUF): tests/test-hmp.o +tests/machine-none-test$(EXESUF): tests/machine-none-test.o tests/drive_del-test$(EXESUF): tests/drive_del-test.o $(libqos-virtio-obj-y) tests/qdev-monitor-test$(EXESUF): tests/qdev-monitor-test.o $(libqos-pc-obj-y) tests/nvme-test$(EXESUF): tests/nvme-test.o @@ -926,14 +931,14 @@ $(patsubst %, check-%, $(check-qapi-schema-y)): check-%.json: $(SRC_PATH)/%.json $^ >$*.test.out 2>$*.test.err; \ echo $$? >$*.test.exit, \ "TEST","$*.out") - @diff $(SRC_PATH)/$*.out $*.test.out @# Sanitize error messages (make them independent of build directory) - @perl -p -e 's|\Q$(SRC_PATH)\E/||g' $*.test.err | diff $(SRC_PATH)/$*.err - - @diff $(SRC_PATH)/$*.exit $*.test.exit + @perl -p -e 's|\Q$(SRC_PATH)\E/||g' $*.test.err | diff -u $(SRC_PATH)/$*.err - + @diff -u $(SRC_PATH)/$*.out $*.test.out + @diff -u $(SRC_PATH)/$*.exit $*.test.exit .PHONY: check-tests/qapi-schema/doc-good.texi check-tests/qapi-schema/doc-good.texi: tests/qapi-schema/doc-good.test.texi - @diff -q $(SRC_PATH)/tests/qapi-schema/doc-good.texi $< + @diff -u $(SRC_PATH)/tests/qapi-schema/doc-good.texi $< .PHONY: check-decodetree check-decodetree: diff --git a/tests/boot-serial-test.c b/tests/boot-serial-test.c index ece25c694f..5b24cd26c1 100644 --- a/tests/boot-serial-test.c +++ b/tests/boot-serial-test.c @@ -75,11 +75,13 @@ typedef struct testdef { static testdef_t tests[] = { { "alpha", "clipper", "", "PCI:" }, { "ppc", "ppce500", "", "U-Boot" }, - { "ppc", "prep", "", "Open Hack'Ware BIOS" }, + { "ppc", "prep", "-m 96", "Memory size: 96 MB" }, + { "ppc", "40p", "-boot d", "Booting from device d" }, { "ppc", "g3beige", "", "PowerPC,750" }, { "ppc", "mac99", "", "PowerPC,G4" }, { "ppc64", "ppce500", "", "U-Boot" }, - { "ppc64", "prep", "", "Open Hack'Ware BIOS" }, + { "ppc64", "prep", "-boot e", "Booting from device e" }, + { "ppc64", "40p", "-m 192", "Memory size: 192 MB" }, { "ppc64", "mac99", "", "PowerPC,970FX" }, { "ppc64", "pseries", "", "Open Firmware" }, { "ppc64", "powernv", "-cpu POWER8", "OPAL" }, diff --git a/tests/check-qdict.c b/tests/check-qdict.c index a3faea8bfc..2e73c2f86e 100644 --- a/tests/check-qdict.c +++ b/tests/check-qdict.c @@ -51,7 +51,7 @@ static void qdict_put_obj_test(void) g_assert(qdict_size(qdict) == 1); ent = QLIST_FIRST(&qdict->table[12345 % QDICT_BUCKET_MAX]); - qn = qobject_to_qnum(ent->value); + qn = qobject_to(QNum, ent->value); g_assert_cmpint(qnum_get_int(qn), ==, num); QDECREF(qdict); @@ -81,7 +81,7 @@ static void qdict_get_test(void) obj = qdict_get(tests_dict, key); g_assert(obj != NULL); - qn = qobject_to_qnum(obj); + qn = qobject_to(QNum, obj); g_assert_cmpint(qnum_get_int(qn), ==, value); QDECREF(tests_dict); @@ -216,7 +216,7 @@ static void qdict_del_test(void) static void qobject_to_qdict_test(void) { QDict *tests_dict = qdict_new(); - g_assert(qobject_to_qdict(QOBJECT(tests_dict)) == tests_dict); + g_assert(qobject_to(QDict, QOBJECT(tests_dict)) == tests_dict); QDECREF(tests_dict); } @@ -381,9 +381,9 @@ static void qdict_array_split_test(void) qdict_array_split(test_dict, &test_list); - dict1 = qobject_to_qdict(qlist_pop(test_list)); - dict2 = qobject_to_qdict(qlist_pop(test_list)); - int1 = qobject_to_qnum(qlist_pop(test_list)); + dict1 = qobject_to(QDict, qlist_pop(test_list)); + dict2 = qobject_to(QDict, qlist_pop(test_list)); + int1 = qobject_to(QNum, qlist_pop(test_list)); g_assert(dict1); g_assert(dict2); @@ -450,7 +450,7 @@ static void qdict_array_split_test(void) qdict_array_split(test_dict, &test_list); - int1 = qobject_to_qnum(qlist_pop(test_list)); + int1 = qobject_to(QNum, qlist_pop(test_list)); g_assert(int1); g_assert(qlist_empty(test_list)); @@ -607,7 +607,7 @@ static void qdict_crumple_test_recursive(void) qdict_put_str(src, "vnc.acl..name", "acl0"); qdict_put_str(src, "vnc.acl.rule..name", "acl0"); - dst = qobject_to_qdict(qdict_crumple(src, &error_abort)); + dst = qobject_to(QDict, qdict_crumple(src, &error_abort)); g_assert(dst); g_assert_cmpint(qdict_size(dst), ==, 1); @@ -629,14 +629,14 @@ static void qdict_crumple_test_recursive(void) g_assert(rules); g_assert_cmpint(qlist_size(rules), ==, 2); - rule = qobject_to_qdict(qlist_pop(rules)); + rule = qobject_to(QDict, qlist_pop(rules)); g_assert(rule); g_assert_cmpint(qdict_size(rule), ==, 2); g_assert_cmpstr("fred", ==, qdict_get_str(rule, "match")); g_assert_cmpstr("allow", ==, qdict_get_str(rule, "policy")); QDECREF(rule); - rule = qobject_to_qdict(qlist_pop(rules)); + rule = qobject_to(QDict, qlist_pop(rules)); g_assert(rule); g_assert_cmpint(qdict_size(rule), ==, 2); g_assert_cmpstr("bob", ==, qdict_get_str(rule, "match")); diff --git a/tests/check-qjson.c b/tests/check-qjson.c index a18ea47cb7..997f4d3d2c 100644 --- a/tests/check-qjson.c +++ b/tests/check-qjson.c @@ -60,7 +60,7 @@ static void escaped_string(void) QString *str; obj = qobject_from_json(test_cases[i].encoded, &error_abort); - str = qobject_to_qstring(obj); + str = qobject_to(QString, obj); g_assert(str); g_assert_cmpstr(qstring_get_str(str), ==, test_cases[i].decoded); @@ -92,7 +92,7 @@ static void simple_string(void) QString *str; obj = qobject_from_json(test_cases[i].encoded, &error_abort); - str = qobject_to_qstring(obj); + str = qobject_to(QString, obj); g_assert(str); g_assert(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0); @@ -123,7 +123,7 @@ static void single_quote_string(void) QString *str; obj = qobject_from_json(test_cases[i].encoded, &error_abort); - str = qobject_to_qstring(obj); + str = qobject_to(QString, obj); g_assert(str); g_assert(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0); @@ -817,7 +817,7 @@ static void utf8_string(void) obj = qobject_from_json(json_in, utf8_out ? &error_abort : NULL); if (utf8_out) { - str = qobject_to_qstring(obj); + str = qobject_to(QString, obj); g_assert(str); g_assert_cmpstr(qstring_get_str(str), ==, utf8_out); } else { @@ -843,7 +843,7 @@ static void utf8_string(void) */ if (0 && json_out != json_in) { obj = qobject_from_json(json_out, &error_abort); - str = qobject_to_qstring(obj); + str = qobject_to(QString, obj); g_assert(str); g_assert_cmpstr(qstring_get_str(str), ==, utf8_out); } @@ -864,8 +864,8 @@ static void vararg_string(void) for (i = 0; test_cases[i].decoded; i++) { QString *str; - str = qobject_to_qstring(qobject_from_jsonf("%s", - test_cases[i].decoded)); + str = qobject_to(QString, + qobject_from_jsonf("%s", test_cases[i].decoded)); g_assert(str); g_assert(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0); @@ -893,8 +893,9 @@ static void simple_number(void) QNum *qnum; int64_t val; - qnum = qobject_to_qnum(qobject_from_json(test_cases[i].encoded, - &error_abort)); + qnum = qobject_to(QNum, + qobject_from_json(test_cases[i].encoded, + &error_abort)); g_assert(qnum); g_assert(qnum_get_try_int(qnum, &val)); g_assert_cmpint(val, ==, test_cases[i].decoded); @@ -920,7 +921,7 @@ static void large_number(void) uint64_t val; int64_t ival; - qnum = qobject_to_qnum(qobject_from_json(maxu64, &error_abort)); + qnum = qobject_to(QNum, qobject_from_json(maxu64, &error_abort)); g_assert(qnum); g_assert_cmpuint(qnum_get_uint(qnum), ==, 18446744073709551615U); g_assert(!qnum_get_try_int(qnum, &ival)); @@ -930,7 +931,7 @@ static void large_number(void) QDECREF(str); QDECREF(qnum); - qnum = qobject_to_qnum(qobject_from_json(gtu64, &error_abort)); + qnum = qobject_to(QNum, qobject_from_json(gtu64, &error_abort)); g_assert(qnum); g_assert_cmpfloat(qnum_get_double(qnum), ==, 18446744073709552e3); g_assert(!qnum_get_try_uint(qnum, &val)); @@ -941,7 +942,7 @@ static void large_number(void) QDECREF(str); QDECREF(qnum); - qnum = qobject_to_qnum(qobject_from_json(lti64, &error_abort)); + qnum = qobject_to(QNum, qobject_from_json(lti64, &error_abort)); g_assert(qnum); g_assert_cmpfloat(qnum_get_double(qnum), ==, -92233720368547758e2); g_assert(!qnum_get_try_uint(qnum, &val)); @@ -973,7 +974,7 @@ static void float_number(void) QNum *qnum; obj = qobject_from_json(test_cases[i].encoded, &error_abort); - qnum = qobject_to_qnum(obj); + qnum = qobject_to(QNum, obj); g_assert(qnum); g_assert(qnum_get_double(qnum) == test_cases[i].decoded); @@ -997,17 +998,17 @@ static void vararg_number(void) double valuef = 2.323423423; int64_t val; - qnum = qobject_to_qnum(qobject_from_jsonf("%d", value)); + qnum = qobject_to(QNum, qobject_from_jsonf("%d", value)); g_assert(qnum_get_try_int(qnum, &val)); g_assert_cmpint(val, ==, value); QDECREF(qnum); - qnum = qobject_to_qnum(qobject_from_jsonf("%lld", value_ll)); + qnum = qobject_to(QNum, qobject_from_jsonf("%lld", value_ll)); g_assert(qnum_get_try_int(qnum, &val)); g_assert_cmpint(val, ==, value_ll); QDECREF(qnum); - qnum = qobject_to_qnum(qobject_from_jsonf("%f", valuef)); + qnum = qobject_to(QNum, qobject_from_jsonf("%f", valuef)); g_assert(qnum_get_double(qnum) == valuef); QDECREF(qnum); } @@ -1020,7 +1021,7 @@ static void keyword_literal(void) QString *str; obj = qobject_from_json("true", &error_abort); - qbool = qobject_to_qbool(obj); + qbool = qobject_to(QBool, obj); g_assert(qbool); g_assert(qbool_get_bool(qbool) == true); @@ -1031,7 +1032,7 @@ static void keyword_literal(void) QDECREF(qbool); obj = qobject_from_json("false", &error_abort); - qbool = qobject_to_qbool(obj); + qbool = qobject_to(QBool, obj); g_assert(qbool); g_assert(qbool_get_bool(qbool) == false); @@ -1041,13 +1042,13 @@ static void keyword_literal(void) QDECREF(qbool); - qbool = qobject_to_qbool(qobject_from_jsonf("%i", false)); + qbool = qobject_to(QBool, qobject_from_jsonf("%i", false)); g_assert(qbool); g_assert(qbool_get_bool(qbool) == false); QDECREF(qbool); /* Test that non-zero values other than 1 get collapsed to true */ - qbool = qobject_to_qbool(qobject_from_jsonf("%i", 2)); + qbool = qobject_to(QBool, qobject_from_jsonf("%i", 2)); g_assert(qbool); g_assert(qbool_get_bool(qbool) == true); QDECREF(qbool); diff --git a/tests/check-qlist.c b/tests/check-qlist.c index 259980d523..a1c69ed648 100644 --- a/tests/check-qlist.c +++ b/tests/check-qlist.c @@ -56,7 +56,7 @@ static void qobject_to_qlist_test(void) qlist = qlist_new(); - g_assert(qobject_to_qlist(QOBJECT(qlist)) == qlist); + g_assert(qobject_to(QList, QOBJECT(qlist)) == qlist); QDECREF(qlist); } @@ -71,7 +71,7 @@ static void iter_func(QObject *obj, void *opaque) g_assert(opaque == NULL); - qi = qobject_to_qnum(obj); + qi = qobject_to(QNum, obj); g_assert(qi != NULL); g_assert(qnum_get_try_int(qi, &val)); diff --git a/tests/check-qlit.c b/tests/check-qlit.c index 5d0f65b9c7..96bbb06f2c 100644 --- a/tests/check-qlit.c +++ b/tests/check-qlit.c @@ -9,9 +9,11 @@ #include "qemu/osdep.h" +#include "qapi/qmp/qbool.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qlist.h" #include "qapi/qmp/qlit.h" +#include "qapi/qmp/qnum.h" #include "qapi/qmp/qstring.h" static QLitObject qlit = QLIT_QDICT(((QLitDictEntry[]) { @@ -57,17 +59,43 @@ static void qlit_equal_qobject_test(void) g_assert(!qlit_equal_qobject(&qlit_foo, qobj)); - qdict_put(qobject_to_qdict(qobj), "bee", qlist_new()); + qdict_put(qobject_to(QDict, qobj), "bee", qlist_new()); g_assert(!qlit_equal_qobject(&qlit, qobj)); qobject_decref(qobj); } +static void qobject_from_qlit_test(void) +{ + QObject *obj, *qobj = qobject_from_qlit(&qlit); + QDict *qdict; + QList *bee; + + qdict = qobject_to(QDict, qobj); + g_assert_cmpint(qdict_get_int(qdict, "foo"), ==, 42); + g_assert_cmpstr(qdict_get_str(qdict, "bar"), ==, "hello world"); + g_assert(qobject_type(qdict_get(qdict, "baz")) == QTYPE_QNULL); + + bee = qdict_get_qlist(qdict, "bee"); + obj = qlist_pop(bee); + g_assert_cmpint(qnum_get_int(qobject_to(QNum, obj)), ==, 43); + qobject_decref(obj); + obj = qlist_pop(bee); + g_assert_cmpint(qnum_get_int(qobject_to(QNum, obj)), ==, 44); + qobject_decref(obj); + obj = qlist_pop(bee); + g_assert(qbool_get_bool(qobject_to(QBool, obj))); + qobject_decref(obj); + + qobject_decref(qobj); +} + int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); g_test_add_func("/qlit/equal_qobject", qlit_equal_qobject_test); + g_test_add_func("/qlit/qobject_from_qlit", qobject_from_qlit_test); return g_test_run(); } diff --git a/tests/check-qnum.c b/tests/check-qnum.c index 2b667f7ad7..9187da734b 100644 --- a/tests/check-qnum.c +++ b/tests/check-qnum.c @@ -126,11 +126,11 @@ static void qobject_to_qnum_test(void) QNum *qn; qn = qnum_from_int(0); - g_assert(qobject_to_qnum(QOBJECT(qn)) == qn); + g_assert(qobject_to(QNum, QOBJECT(qn)) == qn); QDECREF(qn); qn = qnum_from_double(0); - g_assert(qobject_to_qnum(QOBJECT(qn)) == qn); + g_assert(qobject_to(QNum, QOBJECT(qn)) == qn); QDECREF(qn); } diff --git a/tests/check-qobject.c b/tests/check-qobject.c index 7a3670643c..7629b8071b 100644 --- a/tests/check-qobject.c +++ b/tests/check-qobject.c @@ -275,7 +275,7 @@ static void qobject_is_equal_dict_test(void) dict_different_null_key, dict_longer, dict_shorter, dict_nested); - dict_crumpled = qobject_to_qdict(qdict_crumple(dict_1, &local_err)); + dict_crumpled = qobject_to(QDict, qdict_crumple(dict_1, &local_err)); g_assert(!local_err); check_equal(dict_crumpled, dict_nested); diff --git a/tests/check-qstring.c b/tests/check-qstring.c index 112ec08967..9c4dd3f94f 100644 --- a/tests/check-qstring.c +++ b/tests/check-qstring.c @@ -79,7 +79,7 @@ static void qobject_to_qstring_test(void) QString *qstring; qstring = qstring_from_str("foo"); - g_assert(qobject_to_qstring(QOBJECT(qstring)) == qstring); + g_assert(qobject_to(QString, QOBJECT(qstring)) == qstring); QDECREF(qstring); } diff --git a/tests/device-introspect-test.c b/tests/device-introspect-test.c index b80058fe98..a01321aced 100644 --- a/tests/device-introspect-test.c +++ b/tests/device-introspect-test.c @@ -52,7 +52,7 @@ static QDict *qom_type_index(QList *types) QListEntry *e; QLIST_FOREACH_ENTRY(types, e) { - QDict *d = qobject_to_qdict(qlist_entry_obj(e)); + QDict *d = qobject_to(QDict, qlist_entry_obj(e)); const char *name = qdict_get_str(d, "name"); QINCREF(d); qdict_put(index, name, d); @@ -85,7 +85,7 @@ static QDict *type_list_find(QList *types, const char *name) QListEntry *e; QLIST_FOREACH_ENTRY(types, e) { - QDict *d = qobject_to_qdict(qlist_entry_obj(e)); + QDict *d = qobject_to(QDict, qlist_entry_obj(e)); const char *ename = qdict_get_str(d, "name"); if (!strcmp(ename, name)) { return d; @@ -151,7 +151,7 @@ static void test_qom_list_parents(const char *parent) index = qom_type_index(types); QLIST_FOREACH_ENTRY(types, e) { - QDict *d = qobject_to_qdict(qlist_entry_obj(e)); + QDict *d = qobject_to(QDict, qlist_entry_obj(e)); const char *name = qdict_get_str(d, "name"); g_assert(qom_has_parent(index, name, parent)); @@ -173,7 +173,7 @@ static void test_qom_list_fields(void) non_abstract = qom_list_types(NULL, false); QLIST_FOREACH_ENTRY(all_types, e) { - QDict *d = qobject_to_qdict(qlist_entry_obj(e)); + QDict *d = qobject_to(QDict, qlist_entry_obj(e)); const char *name = qdict_get_str(d, "name"); bool abstract = qdict_haskey(d, "abstract") ? qdict_get_bool(d, "abstract") : @@ -216,8 +216,8 @@ static void test_device_intro_concrete(void) types = device_type_list(false); QLIST_FOREACH_ENTRY(types, entry) { - type = qdict_get_try_str(qobject_to_qdict(qlist_entry_obj(entry)), - "name"); + type = qdict_get_try_str(qobject_to(QDict, qlist_entry_obj(entry)), + "name"); g_assert(type); test_one_device(type); } @@ -238,7 +238,7 @@ static void test_abstract_interfaces(void) index = qom_type_index(all_types); QLIST_FOREACH_ENTRY(all_types, e) { - QDict *d = qobject_to_qdict(qlist_entry_obj(e)); + QDict *d = qobject_to(QDict, qlist_entry_obj(e)); const char *name = qdict_get_str(d, "name"); /* diff --git a/tests/libqtest.c b/tests/libqtest.c index 13c910069b..200b2b9e92 100644 --- a/tests/libqtest.c +++ b/tests/libqtest.c @@ -430,7 +430,7 @@ static void qmp_response(JSONMessageParser *parser, GQueue *tokens) } g_assert(!qmp->response); - qmp->response = qobject_to_qdict(obj); + qmp->response = qobject_to(QDict, obj); g_assert(qmp->response); } @@ -1008,11 +1008,11 @@ void qtest_cb_for_every_machine(void (*cb)(const char *machine)) g_assert(list); for (p = qlist_first(list); p; p = qlist_next(p)) { - minfo = qobject_to_qdict(qlist_entry_obj(p)); + minfo = qobject_to(QDict, qlist_entry_obj(p)); g_assert(minfo); qobj = qdict_get(minfo, "name"); g_assert(qobj); - qstr = qobject_to_qstring(qobj); + qstr = qobject_to(QString, qobj); g_assert(qstr); mname = qstring_get_str(qstr); cb(mname); diff --git a/tests/machine-none-test.c b/tests/machine-none-test.c new file mode 100644 index 0000000000..efdd4be986 --- /dev/null +++ b/tests/machine-none-test.c @@ -0,0 +1,103 @@ +/* + * Machine 'none' tests. + * + * Copyright (c) 2018 Red Hat Inc. + * + * Authors: + * Igor Mammedov <imammedo@redhat.com>, + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" + +#include "qemu-common.h" +#include "qemu/cutils.h" +#include "libqtest.h" +#include "qapi/qmp/qdict.h" + + +struct arch2cpu { + const char *arch; + const char *cpu_model; +}; + +static struct arch2cpu cpus_map[] = { + /* tested targets list */ + { "arm", "cortex-a15" }, + { "aarch64", "cortex-a57" }, + { "x86_64", "qemu64,apic-id=0" }, + { "i386", "qemu32,apic-id=0" }, + { "alpha", "ev67" }, + { "cris", "crisv32" }, + { "lm32", "lm32-full" }, + { "m68k", "m5206" }, + /* FIXME: { "microblaze", "any" }, doesn't work with -M none -cpu any */ + /* FIXME: { "microblazeel", "any" }, doesn't work with -M none -cpu any */ + { "mips", "4Kc" }, + { "mipsel", "4Kc" }, + { "mips64", "20Kc" }, + { "mips64el", "20Kc" }, + { "moxie", "MoxieLite" }, + { "nios2", "FIXME" }, + { "or1k", "or1200" }, + { "ppc", "604" }, + { "ppc64", "power8e_v2.1" }, + { "ppcemb", "440epb" }, + { "s390x", "qemu" }, + { "sh4", "sh7750r" }, + { "sh4eb", "sh7751r" }, + { "sparc", "LEON2" }, + { "sparc64", "Fujitsu Sparc64" }, + { "tricore", "tc1796" }, + { "unicore32", "UniCore-II" }, + { "xtensa", "dc233c" }, + { "xtensaeb", "fsf" }, + { "hppa", "hppa" }, + { "riscv64", "rv64gcsu-v1.10.0" }, + { "riscv32", "rv32gcsu-v1.9.1" }, +}; + +static const char *get_cpu_model_by_arch(const char *arch) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(cpus_map); i++) { + if (!strcmp(arch, cpus_map[i].arch)) { + return cpus_map[i].cpu_model; + } + } + return NULL; +} + +static void test_machine_cpu_cli(void) +{ + QDict *response; + const char *arch = qtest_get_arch(); + const char *cpu_model = get_cpu_model_by_arch(arch); + + if (!cpu_model) { + if (!(!strcmp(arch, "microblaze") || !strcmp(arch, "microblazeel"))) { + fprintf(stderr, "WARNING: cpu name for target '%s' isn't defined," + " add it to cpus_map\n", arch); + } + return; /* TODO: die here to force all targets have a test */ + } + global_qtest = qtest_startf("-machine none -cpu '%s'", cpu_model); + + response = qmp("{ 'execute': 'quit' }"); + g_assert(qdict_haskey(response, "return")); + QDECREF(response); + + qtest_quit(global_qtest); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + qtest_add_func("machine/none/cpu_option", test_machine_cpu_cli); + + return g_test_run(); +} diff --git a/tests/numa-test.c b/tests/numa-test.c index 68aca9cb38..0f861d8176 100644 --- a/tests/numa-test.c +++ b/tests/numa-test.c @@ -98,7 +98,7 @@ static void test_query_cpus(const void *data) QDict *cpu, *props; int64_t cpu_idx, node; - cpu = qobject_to_qdict(e); + cpu = qobject_to(QDict, e); g_assert(qdict_haskey(cpu, "CPU")); g_assert(qdict_haskey(cpu, "props")); @@ -140,7 +140,7 @@ static void pc_numa_cpu(const void *data) QDict *cpu, *props; int64_t socket, core, thread, node; - cpu = qobject_to_qdict(e); + cpu = qobject_to(QDict, e); g_assert(qdict_haskey(cpu, "props")); props = qdict_get_qdict(cpu, "props"); @@ -193,7 +193,7 @@ static void spapr_numa_cpu(const void *data) QDict *cpu, *props; int64_t core, node; - cpu = qobject_to_qdict(e); + cpu = qobject_to(QDict, e); g_assert(qdict_haskey(cpu, "props")); props = qdict_get_qdict(cpu, "props"); @@ -236,7 +236,7 @@ static void aarch64_numa_cpu(const void *data) QDict *cpu, *props; int64_t thread, node; - cpu = qobject_to_qdict(e); + cpu = qobject_to(QDict, e); g_assert(qdict_haskey(cpu, "props")); props = qdict_get_qdict(cpu, "props"); diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py index 67e417e298..10e68b01d9 100644 --- a/tests/qapi-schema/test-qapi.py +++ b/tests/qapi-schema/test-qapi.py @@ -42,7 +42,7 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): self._print_variants(variants) def visit_command(self, name, info, arg_type, ret_type, - gen, success_response, boxed): + gen, success_response, boxed, allow_oob): print('command %s %s -> %s' % \ (name, arg_type and arg_type.name, ret_type and ret_type.name)) print(' gen=%s success_response=%s boxed=%s' % \ diff --git a/tests/qemu-iotests/030 b/tests/qemu-iotests/030 index b5f88959aa..640a6dfd10 100755 --- a/tests/qemu-iotests/030 +++ b/tests/qemu-iotests/030 @@ -86,11 +86,9 @@ class TestSingleDrive(iotests.QMPTestCase): result = self.vm.qmp('block-stream', device='drive0') self.assert_qmp(result, 'return', {}) - result = self.vm.qmp('block-job-pause', device='drive0') - self.assert_qmp(result, 'return', {}) - + self.pause_job('drive0', wait=False) self.vm.resume_drive('drive0') - self.pause_job('drive0') + self.pause_wait('drive0') result = self.vm.qmp('query-block-jobs') offset = self.dictpath(result, 'return[0]/offset') diff --git a/tests/qemu-iotests/033 b/tests/qemu-iotests/033 index a1d8357331..ee8a1338bb 100755 --- a/tests/qemu-iotests/033 +++ b/tests/qemu-iotests/033 @@ -105,6 +105,7 @@ for align in 512 4k; do done done +_cleanup_test_img # Trigger truncate that would shrink qcow2 L1 table, which is done by # clearing one entry (8 bytes) with bdrv_co_pwrite_zeroes() diff --git a/tests/qemu-iotests/055 b/tests/qemu-iotests/055 index 8a5d9fd269..3437c11507 100755 --- a/tests/qemu-iotests/055 +++ b/tests/qemu-iotests/055 @@ -86,11 +86,9 @@ class TestSingleDrive(iotests.QMPTestCase): target=target, sync='full') self.assert_qmp(result, 'return', {}) - result = self.vm.qmp('block-job-pause', device='drive0') - self.assert_qmp(result, 'return', {}) - + self.pause_job('drive0', wait=False) self.vm.resume_drive('drive0') - self.pause_job('drive0') + self.pause_wait('drive0') result = self.vm.qmp('query-block-jobs') offset = self.dictpath(result, 'return[0]/offset') @@ -303,13 +301,12 @@ class TestSingleTransaction(iotests.QMPTestCase): ]) self.assert_qmp(result, 'return', {}) - result = self.vm.qmp('block-job-pause', device='drive0') - self.assert_qmp(result, 'return', {}) + self.pause_job('drive0', wait=False) result = self.vm.qmp('block-job-set-speed', device='drive0', speed=0) self.assert_qmp(result, 'return', {}) - self.pause_job('drive0') + self.pause_wait('drive0') result = self.vm.qmp('query-block-jobs') offset = self.dictpath(result, 'return[0]/offset') @@ -534,11 +531,9 @@ class TestDriveCompression(iotests.QMPTestCase): result = self.vm.qmp(cmd, device='drive0', sync='full', compress=True, **args) self.assert_qmp(result, 'return', {}) - result = self.vm.qmp('block-job-pause', device='drive0') - self.assert_qmp(result, 'return', {}) - + self.pause_job('drive0', wait=False) self.vm.resume_drive('drive0') - self.pause_job('drive0') + self.pause_wait('drive0') result = self.vm.qmp('query-block-jobs') offset = self.dictpath(result, 'return[0]/offset') diff --git a/tests/qemu-iotests/056 b/tests/qemu-iotests/056 index 04f2c3c841..223292175a 100755 --- a/tests/qemu-iotests/056 +++ b/tests/qemu-iotests/056 @@ -29,6 +29,26 @@ backing_img = os.path.join(iotests.test_dir, 'backing.img') test_img = os.path.join(iotests.test_dir, 'test.img') target_img = os.path.join(iotests.test_dir, 'target.img') +def img_create(img, fmt=iotests.imgfmt, size='64M', **kwargs): + fullname = os.path.join(iotests.test_dir, '%s.%s' % (img, fmt)) + optargs = [] + for k,v in kwargs.iteritems(): + optargs = optargs + ['-o', '%s=%s' % (k,v)] + args = ['create', '-f', fmt] + optargs + [fullname, size] + iotests.qemu_img(*args) + return fullname + +def try_remove(img): + try: + os.remove(img) + except OSError: + pass + +def io_write_patterns(img, patterns): + for pattern in patterns: + iotests.qemu_io('-c', 'write -P%s %s %s' % pattern, img) + + class TestSyncModesNoneAndTop(iotests.QMPTestCase): image_len = 64 * 1024 * 1024 # MB @@ -108,5 +128,172 @@ class TestBeforeWriteNotifier(iotests.QMPTestCase): event = self.cancel_and_wait() self.assert_qmp(event, 'data/type', 'backup') +class BackupTest(iotests.QMPTestCase): + def setUp(self): + self.vm = iotests.VM() + self.test_img = img_create('test') + self.dest_img = img_create('dest') + self.vm.add_drive(self.test_img) + self.vm.launch() + + def tearDown(self): + self.vm.shutdown() + try_remove(self.test_img) + try_remove(self.dest_img) + + def hmp_io_writes(self, drive, patterns): + for pattern in patterns: + self.vm.hmp_qemu_io(drive, 'write -P%s %s %s' % pattern) + self.vm.hmp_qemu_io(drive, 'flush') + + def qmp_backup_and_wait(self, cmd='drive-backup', serror=None, + aerror=None, **kwargs): + if not self.qmp_backup(cmd, serror, **kwargs): + return False + return self.qmp_backup_wait(kwargs['device'], aerror) + + def qmp_backup(self, cmd='drive-backup', + error=None, **kwargs): + self.assertTrue('device' in kwargs) + res = self.vm.qmp(cmd, **kwargs) + if error: + self.assert_qmp(res, 'error/desc', error) + return False + self.assert_qmp(res, 'return', {}) + return True + + def qmp_backup_wait(self, device, error=None): + event = self.vm.event_wait(name="BLOCK_JOB_COMPLETED", + match={'data': {'device': device}}) + self.assertNotEqual(event, None) + try: + failure = self.dictpath(event, 'data/error') + except AssertionError: + # Backup succeeded. + self.assert_qmp(event, 'data/offset', event['data']['len']) + return True + else: + # Failure. + self.assert_qmp(event, 'data/error', qerror) + return False + + def test_dismiss_false(self): + res = self.vm.qmp('query-block-jobs') + self.assert_qmp(res, 'return', []) + self.qmp_backup_and_wait(device='drive0', format=iotests.imgfmt, + sync='full', target=self.dest_img, + auto_dismiss=True) + res = self.vm.qmp('query-block-jobs') + self.assert_qmp(res, 'return', []) + + def test_dismiss_true(self): + res = self.vm.qmp('query-block-jobs') + self.assert_qmp(res, 'return', []) + self.qmp_backup_and_wait(device='drive0', format=iotests.imgfmt, + sync='full', target=self.dest_img, + auto_dismiss=False) + res = self.vm.qmp('query-block-jobs') + self.assert_qmp(res, 'return[0]/status', 'concluded') + res = self.vm.qmp('block-job-dismiss', id='drive0') + self.assert_qmp(res, 'return', {}) + res = self.vm.qmp('query-block-jobs') + self.assert_qmp(res, 'return', []) + + def test_dismiss_bad_id(self): + res = self.vm.qmp('query-block-jobs') + self.assert_qmp(res, 'return', []) + res = self.vm.qmp('block-job-dismiss', id='foobar') + self.assert_qmp(res, 'error/class', 'DeviceNotActive') + + def test_dismiss_collision(self): + res = self.vm.qmp('query-block-jobs') + self.assert_qmp(res, 'return', []) + self.qmp_backup_and_wait(device='drive0', format=iotests.imgfmt, + sync='full', target=self.dest_img, + auto_dismiss=False) + res = self.vm.qmp('query-block-jobs') + self.assert_qmp(res, 'return[0]/status', 'concluded') + # Leave zombie job un-dismissed, observe a failure: + res = self.qmp_backup_and_wait(serror='Need a root block node', + device='drive0', format=iotests.imgfmt, + sync='full', target=self.dest_img, + auto_dismiss=False) + self.assertEqual(res, False) + # OK, dismiss the zombie. + res = self.vm.qmp('block-job-dismiss', id='drive0') + self.assert_qmp(res, 'return', {}) + res = self.vm.qmp('query-block-jobs') + self.assert_qmp(res, 'return', []) + # Ensure it's really gone. + self.qmp_backup_and_wait(device='drive0', format=iotests.imgfmt, + sync='full', target=self.dest_img, + auto_dismiss=False) + + def dismissal_failure(self, dismissal_opt): + res = self.vm.qmp('query-block-jobs') + self.assert_qmp(res, 'return', []) + # Give blkdebug something to chew on + self.hmp_io_writes('drive0', + (('0x9a', 0, 512), + ('0x55', '8M', '352k'), + ('0x78', '15872k', '1M'))) + # Add destination node via blkdebug + res = self.vm.qmp('blockdev-add', + node_name='target0', + driver=iotests.imgfmt, + file={ + 'driver': 'blkdebug', + 'image': { + 'driver': 'file', + 'filename': self.dest_img + }, + 'inject-error': [{ + 'event': 'write_aio', + 'errno': 5, + 'immediately': False, + 'once': True + }], + }) + self.assert_qmp(res, 'return', {}) + + res = self.qmp_backup(cmd='blockdev-backup', + device='drive0', target='target0', + on_target_error='stop', + sync='full', + auto_dismiss=dismissal_opt) + self.assertTrue(res) + event = self.vm.event_wait(name="BLOCK_JOB_ERROR", + match={'data': {'device': 'drive0'}}) + self.assertNotEqual(event, None) + # OK, job should be wedged + res = self.vm.qmp('query-block-jobs') + self.assert_qmp(res, 'return[0]/status', 'paused') + res = self.vm.qmp('block-job-dismiss', id='drive0') + self.assert_qmp(res, 'error/desc', + "Job 'drive0' in state 'paused' cannot accept" + " command verb 'dismiss'") + res = self.vm.qmp('query-block-jobs') + self.assert_qmp(res, 'return[0]/status', 'paused') + # OK, unstick job and move forward. + res = self.vm.qmp('block-job-resume', device='drive0') + self.assert_qmp(res, 'return', {}) + # And now we need to wait for it to conclude; + res = self.qmp_backup_wait(device='drive0') + self.assertTrue(res) + if not dismissal_opt: + # Job should now be languishing: + res = self.vm.qmp('query-block-jobs') + self.assert_qmp(res, 'return[0]/status', 'concluded') + res = self.vm.qmp('block-job-dismiss', id='drive0') + self.assert_qmp(res, 'return', {}) + res = self.vm.qmp('query-block-jobs') + self.assert_qmp(res, 'return', []) + + def test_dismiss_premature(self): + self.dismissal_failure(False) + + def test_dismiss_erroneous(self): + self.dismissal_failure(True) + if __name__ == '__main__': iotests.main(supported_fmts=['qcow2', 'qed']) diff --git a/tests/qemu-iotests/056.out b/tests/qemu-iotests/056.out index 8d7e996700..dae404e278 100644 --- a/tests/qemu-iotests/056.out +++ b/tests/qemu-iotests/056.out @@ -1,5 +1,5 @@ -... +......... ---------------------------------------------------------------------- -Ran 3 tests +Ran 9 tests OK diff --git a/tests/qemu-iotests/089 b/tests/qemu-iotests/089 index 0b059aba90..aa1ba4a98e 100755 --- a/tests/qemu-iotests/089 +++ b/tests/qemu-iotests/089 @@ -82,6 +82,26 @@ $QEMU_IO_PROG --cache $CACHEMODE \ $QEMU_IO -c 'read -P 42 0 512' "$TEST_IMG" | _filter_qemu_io +echo +echo "=== Testing correct handling of 'backing':null ===" +echo + +_make_test_img -b "$TEST_IMG.base" $IMG_SIZE + +# This should read 42 +$QEMU_IO -c 'read -P 42 0 512' "$TEST_IMG" | _filter_qemu_io + +# This should read 0 +$QEMU_IO -c 'read -P 0 0 512' "json:{\ + 'driver': '$IMGFMT', + 'file': { + 'driver': 'file', + 'filename': '$TEST_IMG' + }, + 'backing': null +}" | _filter_qemu_io + + # Taken from test 071 echo echo "=== Testing blkdebug ===" diff --git a/tests/qemu-iotests/089.out b/tests/qemu-iotests/089.out index 0bf5a13ec1..89e3e4340a 100644 --- a/tests/qemu-iotests/089.out +++ b/tests/qemu-iotests/089.out @@ -19,6 +19,14 @@ Pattern verification failed at offset 0, 512 bytes read 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +=== Testing correct handling of 'backing':null === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base +read 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + === Testing blkdebug === Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 diff --git a/tests/qemu-iotests/109.out b/tests/qemu-iotests/109.out index c189e2833d..8a9b93672b 100644 --- a/tests/qemu-iotests/109.out +++ b/tests/qemu-iotests/109.out @@ -19,7 +19,7 @@ read 65536/65536 bytes at offset 0 {"return": {}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}} -{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 1024, "offset": 1024, "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 1024, "offset": 1024, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}} @@ -45,7 +45,7 @@ read 65536/65536 bytes at offset 0 {"return": {}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 197120, "offset": 197120, "speed": 0, "type": "mirror"}} -{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 197120, "offset": 197120, "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 197120, "offset": 197120, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 197120, "offset": 197120, "speed": 0, "type": "mirror"}} @@ -71,7 +71,7 @@ read 65536/65536 bytes at offset 0 {"return": {}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}} -{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 327680, "offset": 327680, "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 327680, "offset": 327680, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}} @@ -97,7 +97,7 @@ read 65536/65536 bytes at offset 0 {"return": {}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}} -{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 1024, "offset": 1024, "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 1024, "offset": 1024, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}} @@ -123,7 +123,7 @@ read 65536/65536 bytes at offset 0 {"return": {}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 65536, "offset": 65536, "speed": 0, "type": "mirror"}} -{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 65536, "offset": 65536, "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 65536, "offset": 65536, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 65536, "offset": 65536, "speed": 0, "type": "mirror"}} @@ -149,7 +149,7 @@ read 65536/65536 bytes at offset 0 {"return": {}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}} -{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 2560, "offset": 2560, "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 2560, "offset": 2560, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}} @@ -174,7 +174,7 @@ read 65536/65536 bytes at offset 0 {"return": {}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}} -{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 2560, "offset": 2560, "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 2560, "offset": 2560, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}} @@ -199,7 +199,7 @@ read 65536/65536 bytes at offset 0 {"return": {}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 31457280, "offset": 31457280, "speed": 0, "type": "mirror"}} -{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 31457280, "offset": 31457280, "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 31457280, "offset": 31457280, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 31457280, "offset": 31457280, "speed": 0, "type": "mirror"}} @@ -224,7 +224,7 @@ read 65536/65536 bytes at offset 0 {"return": {}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}} -{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 327680, "offset": 327680, "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 327680, "offset": 327680, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}} @@ -249,7 +249,7 @@ read 65536/65536 bytes at offset 0 {"return": {}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 2048, "offset": 2048, "speed": 0, "type": "mirror"}} -{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 2048, "offset": 2048, "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 2048, "offset": 2048, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 2048, "offset": 2048, "speed": 0, "type": "mirror"}} @@ -265,7 +265,7 @@ Automatically detecting the format is dangerous for raw images, write operations Specify the 'raw' format explicitly to remove the restrictions. {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}} -{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 512, "offset": 512, "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 512, "offset": 512, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}} @@ -274,7 +274,7 @@ Images are identical. {"return": {}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}} -{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 512, "offset": 512, "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 512, "offset": 512, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}} diff --git a/tests/qemu-iotests/146.out b/tests/qemu-iotests/146.out index db6b296b9e..1332189d87 100644 --- a/tests/qemu-iotests/146.out +++ b/tests/qemu-iotests/146.out @@ -54,7 +54,7 @@ Formatting 'TEST_DIR/IMGFMT-create-test.IMGFMT', fmt=IMGFMT size=4294967296 === Testing Image create, force_size === -Formatting 'TEST_DIR/IMGFMT-create-test.IMGFMT', fmt=IMGFMT size=4294967296 force_size=on +Formatting 'TEST_DIR/IMGFMT-create-test.IMGFMT', fmt=IMGFMT size=4294967296 === Read created image, default opts ==== diff --git a/tests/qemu-iotests/153 b/tests/qemu-iotests/153 index adfd02695b..a0fd815483 100755 --- a/tests/qemu-iotests/153 +++ b/tests/qemu-iotests/153 @@ -178,6 +178,18 @@ rm -f "${TEST_IMG}.lnk" &>/dev/null ln -s ${TEST_IMG} "${TEST_IMG}.lnk" || echo "Failed to create link" _run_qemu_with_images "${TEST_IMG}.lnk" "${TEST_IMG}" +echo +echo "== Active commit to intermediate layer should work when base in use ==" +_launch_qemu -drive format=$IMGFMT,file="${TEST_IMG}.a",id=drive0,if=none \ + -device virtio-blk,drive=drive0 + +_send_qemu_cmd $QEMU_HANDLE \ + "{ 'execute': 'qmp_capabilities' }" \ + 'return' +_run_cmd $QEMU_IMG commit -b "${TEST_IMG}.b" "${TEST_IMG}.c" + +_cleanup_qemu + _launch_qemu _send_qemu_cmd $QEMU_HANDLE \ diff --git a/tests/qemu-iotests/153.out b/tests/qemu-iotests/153.out index 34309cfb20..bb721cb747 100644 --- a/tests/qemu-iotests/153.out +++ b/tests/qemu-iotests/153.out @@ -372,6 +372,11 @@ Is another process using the image? == Symbolic link == QEMU_PROG: -drive if=none,file=TEST_DIR/t.qcow2: Failed to get "write" lock Is another process using the image? + +== Active commit to intermediate layer should work when base in use == +{"return": {}} + +_qemu_img_wrapper commit -b TEST_DIR/t.qcow2.b TEST_DIR/t.qcow2.c {"return": {}} Adding drive diff --git a/tests/qemu-iotests/169 b/tests/qemu-iotests/169 new file mode 100755 index 0000000000..3a8db91f6f --- /dev/null +++ b/tests/qemu-iotests/169 @@ -0,0 +1,156 @@ +#!/usr/bin/env python +# +# Tests for dirty bitmaps migration. +# +# Copyright (c) 2016-2017 Virtuozzo International GmbH. All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +import os +import iotests +import time +import itertools +import operator +import new +from iotests import qemu_img + + +disk_a = os.path.join(iotests.test_dir, 'disk_a') +disk_b = os.path.join(iotests.test_dir, 'disk_b') +size = '1M' +mig_file = os.path.join(iotests.test_dir, 'mig_file') + + +class TestDirtyBitmapMigration(iotests.QMPTestCase): + def tearDown(self): + self.vm_a.shutdown() + self.vm_b.shutdown() + os.remove(disk_a) + os.remove(disk_b) + os.remove(mig_file) + + def setUp(self): + qemu_img('create', '-f', iotests.imgfmt, disk_a, size) + qemu_img('create', '-f', iotests.imgfmt, disk_b, size) + + self.vm_a = iotests.VM(path_suffix='a').add_drive(disk_a) + self.vm_a.launch() + + self.vm_b = iotests.VM(path_suffix='b') + self.vm_b.add_incoming("exec: cat '" + mig_file + "'") + + def add_bitmap(self, vm, granularity, persistent): + params = {'node': 'drive0', + 'name': 'bitmap0', + 'granularity': granularity} + if persistent: + params['persistent'] = True + params['autoload'] = True + + result = vm.qmp('block-dirty-bitmap-add', **params) + self.assert_qmp(result, 'return', {}); + + def get_bitmap_hash(self, vm): + result = vm.qmp('x-debug-block-dirty-bitmap-sha256', + node='drive0', name='bitmap0') + return result['return']['sha256'] + + def check_bitmap(self, vm, sha256): + result = vm.qmp('x-debug-block-dirty-bitmap-sha256', + node='drive0', name='bitmap0') + if sha256: + self.assert_qmp(result, 'return/sha256', sha256); + else: + self.assert_qmp(result, 'error/desc', + "Dirty bitmap 'bitmap0' not found"); + + def do_test_migration(self, persistent, migrate_bitmaps, online, + shared_storage): + granularity = 512 + + # regions = ((start, count), ...) + regions = ((0, 0x10000), + (0xf0000, 0x10000), + (0xa0201, 0x1000)) + + should_migrate = migrate_bitmaps or persistent and shared_storage + + self.vm_b.add_drive(disk_a if shared_storage else disk_b) + + if online: + os.mkfifo(mig_file) + self.vm_b.launch() + + self.add_bitmap(self.vm_a, granularity, persistent) + for r in regions: + self.vm_a.hmp_qemu_io('drive0', 'write %d %d' % r) + sha256 = self.get_bitmap_hash(self.vm_a) + + if migrate_bitmaps: + capabilities = [{'capability': 'dirty-bitmaps', 'state': True}] + + result = self.vm_a.qmp('migrate-set-capabilities', + capabilities=capabilities) + self.assert_qmp(result, 'return', {}) + + if online: + result = self.vm_b.qmp('migrate-set-capabilities', + capabilities=capabilities) + self.assert_qmp(result, 'return', {}) + + result = self.vm_a.qmp('migrate-set-capabilities', + capabilities=[{'capability': 'events', + 'state': True}]) + self.assert_qmp(result, 'return', {}) + + result = self.vm_a.qmp('migrate', uri='exec:cat>' + mig_file) + while True: + event = self.vm_a.event_wait('MIGRATION') + if event['data']['status'] == 'completed': + break + + if not online: + self.vm_a.shutdown() + self.vm_b.launch() + # TODO enable bitmap capability for vm_b in this case + + self.vm_b.event_wait("RESUME", timeout=10.0) + + self.check_bitmap(self.vm_b, sha256 if should_migrate else False) + + if should_migrate: + self.vm_b.shutdown() + self.vm_b.launch() + self.check_bitmap(self.vm_b, sha256 if persistent else False) + + +def inject_test_case(klass, name, method, *args, **kwargs): + mc = operator.methodcaller(method, *args, **kwargs) + setattr(klass, 'test_' + name, new.instancemethod(mc, None, klass)) + +for cmb in list(itertools.product((True, False), repeat=3)): + name = ('_' if cmb[0] else '_not_') + 'persistent_' + name += ('_' if cmb[1] else '_not_') + 'migbitmap_' + name += '_online' if cmb[2] else '_offline' + + # TODO fix shared-storage bitmap migration and enable cases for it + args = list(cmb) + [False] + + inject_test_case(TestDirtyBitmapMigration, name, 'do_test_migration', + *args) + + +if __name__ == '__main__': + iotests.main(supported_fmts=['qcow2']) diff --git a/tests/qemu-iotests/169.out b/tests/qemu-iotests/169.out new file mode 100644 index 0000000000..594c16f49f --- /dev/null +++ b/tests/qemu-iotests/169.out @@ -0,0 +1,5 @@ +........ +---------------------------------------------------------------------- +Ran 8 tests + +OK diff --git a/tests/qemu-iotests/181 b/tests/qemu-iotests/181 index 0c91e8f9de..5e767c6195 100755 --- a/tests/qemu-iotests/181 +++ b/tests/qemu-iotests/181 @@ -44,7 +44,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt generic # Formats that do not support live migration -_unsupported_fmt qcow vdi vhdx vmdk vpc vvfat +_unsupported_fmt qcow vdi vhdx vmdk vpc vvfat parallels _supported_proto generic _supported_os Linux diff --git a/tests/qemu-iotests/199 b/tests/qemu-iotests/199 new file mode 100755 index 0000000000..651e8df5d9 --- /dev/null +++ b/tests/qemu-iotests/199 @@ -0,0 +1,118 @@ +#!/usr/bin/env python +# +# Tests for dirty bitmaps postcopy migration. +# +# Copyright (c) 2016-2017 Virtuozzo International GmbH. All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +import os +import iotests +import time +from iotests import qemu_img + +disk_a = os.path.join(iotests.test_dir, 'disk_a') +disk_b = os.path.join(iotests.test_dir, 'disk_b') +size = '256G' +fifo = os.path.join(iotests.test_dir, 'mig_fifo') + +class TestDirtyBitmapPostcopyMigration(iotests.QMPTestCase): + + def tearDown(self): + self.vm_a.shutdown() + self.vm_b.shutdown() + os.remove(disk_a) + os.remove(disk_b) + os.remove(fifo) + + def setUp(self): + os.mkfifo(fifo) + qemu_img('create', '-f', iotests.imgfmt, disk_a, size) + qemu_img('create', '-f', iotests.imgfmt, disk_b, size) + self.vm_a = iotests.VM(path_suffix='a').add_drive(disk_a) + self.vm_b = iotests.VM(path_suffix='b').add_drive(disk_b) + self.vm_b.add_incoming("exec: cat '" + fifo + "'") + self.vm_a.launch() + self.vm_b.launch() + + def test_postcopy(self): + write_size = 0x40000000 + granularity = 512 + chunk = 4096 + + result = self.vm_a.qmp('block-dirty-bitmap-add', node='drive0', + name='bitmap', granularity=granularity) + self.assert_qmp(result, 'return', {}); + + s = 0 + while s < write_size: + self.vm_a.hmp_qemu_io('drive0', 'write %d %d' % (s, chunk)) + s += 0x10000 + s = 0x8000 + while s < write_size: + self.vm_a.hmp_qemu_io('drive0', 'write %d %d' % (s, chunk)) + s += 0x10000 + + result = self.vm_a.qmp('x-debug-block-dirty-bitmap-sha256', + node='drive0', name='bitmap') + sha256 = result['return']['sha256'] + + result = self.vm_a.qmp('block-dirty-bitmap-clear', node='drive0', + name='bitmap') + self.assert_qmp(result, 'return', {}); + s = 0 + while s < write_size: + self.vm_a.hmp_qemu_io('drive0', 'write %d %d' % (s, chunk)) + s += 0x10000 + + bitmaps_cap = {'capability': 'dirty-bitmaps', 'state': True} + events_cap = {'capability': 'events', 'state': True} + + result = self.vm_a.qmp('migrate-set-capabilities', + capabilities=[bitmaps_cap, events_cap]) + self.assert_qmp(result, 'return', {}) + + result = self.vm_b.qmp('migrate-set-capabilities', + capabilities=[bitmaps_cap]) + self.assert_qmp(result, 'return', {}) + + result = self.vm_a.qmp('migrate', uri='exec:cat>' + fifo) + self.assert_qmp(result, 'return', {}) + + result = self.vm_a.qmp('migrate-start-postcopy') + self.assert_qmp(result, 'return', {}) + + while True: + event = self.vm_a.event_wait('MIGRATION') + if event['data']['status'] == 'completed': + break + + s = 0x8000 + while s < write_size: + self.vm_b.hmp_qemu_io('drive0', 'write %d %d' % (s, chunk)) + s += 0x10000 + + result = self.vm_b.qmp('query-block'); + while len(result['return'][0]['dirty-bitmaps']) > 1: + time.sleep(2) + result = self.vm_b.qmp('query-block'); + + result = self.vm_b.qmp('x-debug-block-dirty-bitmap-sha256', + node='drive0', name='bitmap') + + self.assert_qmp(result, 'return/sha256', sha256); + +if __name__ == '__main__': + iotests.main(supported_fmts=['qcow2'], supported_cache_modes=['none']) diff --git a/tests/qemu-iotests/199.out b/tests/qemu-iotests/199.out new file mode 100644 index 0000000000..ae1213e6f8 --- /dev/null +++ b/tests/qemu-iotests/199.out @@ -0,0 +1,5 @@ +. +---------------------------------------------------------------------- +Ran 1 tests + +OK diff --git a/tests/qemu-iotests/208 b/tests/qemu-iotests/208 new file mode 100755 index 0000000000..4e82b96c82 --- /dev/null +++ b/tests/qemu-iotests/208 @@ -0,0 +1,55 @@ +#!/usr/bin/env python +# +# Copyright (C) 2018 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +# Creator/Owner: Stefan Hajnoczi <stefanha@redhat.com> +# +# Check that the runtime NBD server does not crash when stopped after +# blockdev-snapshot-sync. + +import iotests + +with iotests.FilePath('disk.img') as disk_img_path, \ + iotests.FilePath('disk-snapshot.img') as disk_snapshot_img_path, \ + iotests.FilePath('nbd.sock') as nbd_sock_path, \ + iotests.VM() as vm: + + img_size = '10M' + iotests.qemu_img_pipe('create', '-f', iotests.imgfmt, disk_img_path, img_size) + + iotests.log('Launching VM...') + (vm.add_drive(disk_img_path, 'node-name=drive0-node', interface='none') + .launch()) + + iotests.log('Starting NBD server...') + iotests.log(vm.qmp('nbd-server-start', addr={ + "type": "unix", + "data": { + "path": nbd_sock_path, + } + })) + + iotests.log('Adding NBD export...') + iotests.log(vm.qmp('nbd-server-add', device='drive0-node', writable=True)) + + iotests.log('Creating external snapshot...') + iotests.log(vm.qmp('blockdev-snapshot-sync', + node_name='drive0-node', + snapshot_node_name='drive0-snapshot-node', + snapshot_file=disk_snapshot_img_path)) + + iotests.log('Stopping NBD server...') + iotests.log(vm.qmp('nbd-server-stop')) diff --git a/tests/qemu-iotests/208.out b/tests/qemu-iotests/208.out new file mode 100644 index 0000000000..3687e9d0dd --- /dev/null +++ b/tests/qemu-iotests/208.out @@ -0,0 +1,9 @@ +Launching VM... +Starting NBD server... +{u'return': {}} +Adding NBD export... +{u'return': {}} +Creating external snapshot... +{u'return': {}} +Stopping NBD server... +{u'return': {}} diff --git a/tests/qemu-iotests/209 b/tests/qemu-iotests/209 new file mode 100755 index 0000000000..259e991ec6 --- /dev/null +++ b/tests/qemu-iotests/209 @@ -0,0 +1,34 @@ +#!/usr/bin/env python +# +# Tests for NBD BLOCK_STATUS extension +# +# Copyright (c) 2018 Virtuozzo International GmbH +# +# 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 +from iotests import qemu_img_create, qemu_io, qemu_img_verbose, qemu_nbd, \ + file_path + +iotests.verify_image_format(supported_fmts=['qcow2']) + +disk, nbd_sock = file_path('disk', 'nbd-sock') +nbd_uri = 'nbd+unix:///exp?socket=' + nbd_sock + +qemu_img_create('-f', iotests.imgfmt, disk, '1M') +qemu_io('-f', iotests.imgfmt, '-c', 'write 0 512K', disk) + +qemu_nbd('-k', nbd_sock, '-x', 'exp', '-f', iotests.imgfmt, disk) +qemu_img_verbose('map', '-f', 'raw', '--output=json', nbd_uri) diff --git a/tests/qemu-iotests/209.out b/tests/qemu-iotests/209.out new file mode 100644 index 0000000000..0d29724e84 --- /dev/null +++ b/tests/qemu-iotests/209.out @@ -0,0 +1,2 @@ +[{ "start": 0, "length": 524288, "depth": 0, "zero": false, "data": true}, +{ "start": 524288, "length": 524288, "depth": 0, "zero": true, "data": false}] diff --git a/tests/qemu-iotests/210 b/tests/qemu-iotests/210 new file mode 100755 index 0000000000..96a5213e77 --- /dev/null +++ b/tests/qemu-iotests/210 @@ -0,0 +1,210 @@ +#!/bin/bash +# +# Test luks and file image creation +# +# Copyright (C) 2018 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +# creator +owner=kwolf@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +here=`pwd` +status=1 # failure is the default! + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt luks +_supported_proto file +_supported_os Linux + +function do_run_qemu() +{ + echo Testing: "$@" + $QEMU -nographic -qmp stdio -serial none "$@" + echo +} + +function run_qemu() +{ + do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp \ + | _filter_qemu | _filter_imgfmt \ + | _filter_actual_image_size +} + +echo +echo "=== Successful image creation (defaults) ===" +echo + +size=$((128 * 1024 * 1024)) + +run_qemu -object secret,id=keysec0,data="foo" <<EOF +{ "execute": "qmp_capabilities" } +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "file", + "filename": "$TEST_IMG_FILE", + "size": 0 + } +} +{ "execute": "blockdev-add", + "arguments": { + "driver": "file", + "node-name": "imgfile", + "filename": "$TEST_IMG_FILE" + } +} +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "$IMGFMT", + "file": "imgfile", + "key-secret": "keysec0", + "size": $size, + "iter-time": 10 + } +} +{ "execute": "quit" } +EOF + +_img_info --format-specific | _filter_img_info --format-specific + +echo +echo "=== Successful image creation (with non-default options) ===" +echo + +# Choose a different size to show that we got a new image +size=$((64 * 1024 * 1024)) + +run_qemu -object secret,id=keysec0,data="foo" <<EOF +{ "execute": "qmp_capabilities" } +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "file", + "filename": "$TEST_IMG_FILE", + "size": 0 + } +} +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "$IMGFMT", + "file": { + "driver": "file", + "filename": "$TEST_IMG_FILE" + }, + "size": $size, + "key-secret": "keysec0", + "cipher-alg": "twofish-128", + "cipher-mode": "ctr", + "ivgen-alg": "plain64", + "ivgen-hash-alg": "md5", + "hash-alg": "sha1", + "iter-time": 10 + } +} +{ "execute": "quit" } +EOF + +_img_info --format-specific | _filter_img_info --format-specific + +echo +echo "=== Invalid BlockdevRef ===" +echo + +run_qemu <<EOF +{ "execute": "qmp_capabilities" } +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "$IMGFMT", + "file": "this doesn't exist", + "size": $size + } +} +{ "execute": "quit" } +EOF + +echo +echo "=== Zero size ===" +echo + +run_qemu -blockdev driver=file,filename="$TEST_IMG_FILE",node-name=node0 \ + -object secret,id=keysec0,data="foo" <<EOF +{ "execute": "qmp_capabilities" } +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "$IMGFMT", + "file": "node0", + "key-secret": "keysec0", + "size": 0, + "iter-time": 10 + } +} +{ "execute": "quit" } +EOF + +_img_info | _filter_img_info + + +echo +echo "=== Invalid sizes ===" +echo + +# TODO Negative image sizes aren't handled correctly, but this is a problem +# with QAPI's implementation of the 'size' type and affects other commands as +# well. Once this is fixed, we may want to add a test case here. + +# 1. 2^64 - 512 +# 2. 2^63 = 8 EB (qemu-img enforces image sizes less than this) +# 3. 2^63 - 512 (generally valid, but with the crypto header the file will +# exceed 63 bits) + +run_qemu -blockdev driver=file,filename="$TEST_IMG_FILE",node-name=node0 \ + -object secret,id=keysec0,data="foo" <<EOF +{ "execute": "qmp_capabilities" } +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "$IMGFMT", + "file": "node0", + "key-secret": "keysec0", + "size": 18446744073709551104 + } +} +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "$IMGFMT", + "file": "node0", + "key-secret": "keysec0", + "size": 9223372036854775808 + } +} +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "$IMGFMT", + "file": "node0", + "key-secret": "keysec0", + "size": 9223372036854775296 + } +} +{ "execute": "quit" } +EOF + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/210.out b/tests/qemu-iotests/210.out new file mode 100644 index 0000000000..8fcab65909 --- /dev/null +++ b/tests/qemu-iotests/210.out @@ -0,0 +1,136 @@ +QA output created by 210 + +=== Successful image creation (defaults) === + +Testing: -object secret,id=keysec0,data=foo +QMP_VERSION +{"return": {}} +{"return": {}} +{"return": {}} +{"return": {}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + +image: json:{"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/t.IMGFMT"}, "key-secret": "keysec0"} +file format: IMGFMT +virtual size: 128M (134217728 bytes) +Format specific information: + ivgen alg: plain64 + hash alg: sha256 + cipher alg: aes-256 + uuid: 00000000-0000-0000-0000-000000000000 + cipher mode: xts + slots: + [0]: + active: true + iters: 1024 + key offset: 4096 + stripes: 4000 + [1]: + active: false + key offset: 262144 + [2]: + active: false + key offset: 520192 + [3]: + active: false + key offset: 778240 + [4]: + active: false + key offset: 1036288 + [5]: + active: false + key offset: 1294336 + [6]: + active: false + key offset: 1552384 + [7]: + active: false + key offset: 1810432 + payload offset: 2068480 + master key iters: 1024 + +=== Successful image creation (with non-default options) === + +Testing: -object secret,id=keysec0,data=foo +QMP_VERSION +{"return": {}} +{"return": {}} +{"return": {}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + +image: json:{"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/t.IMGFMT"}, "key-secret": "keysec0"} +file format: IMGFMT +virtual size: 64M (67108864 bytes) +Format specific information: + ivgen alg: plain64 + hash alg: sha1 + cipher alg: twofish-128 + uuid: 00000000-0000-0000-0000-000000000000 + cipher mode: ctr + slots: + [0]: + active: true + iters: 1024 + key offset: 4096 + stripes: 4000 + [1]: + active: false + key offset: 69632 + [2]: + active: false + key offset: 135168 + [3]: + active: false + key offset: 200704 + [4]: + active: false + key offset: 266240 + [5]: + active: false + key offset: 331776 + [6]: + active: false + key offset: 397312 + [7]: + active: false + key offset: 462848 + payload offset: 528384 + master key iters: 1024 + +=== Invalid BlockdevRef === + +Testing: +QMP_VERSION +{"return": {}} +{"error": {"class": "GenericError", "desc": "Cannot find device=this doesn't exist nor node_name=this doesn't exist"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + + +=== Zero size === + +Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0 -object secret,id=keysec0,data=foo +QMP_VERSION +{"return": {}} +{"return": {}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + +image: json:{"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/t.IMGFMT"}, "key-secret": "keysec0"} +file format: IMGFMT +virtual size: 0 (0 bytes) + +=== Invalid sizes === + +Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0 -object secret,id=keysec0,data=foo +QMP_VERSION +{"return": {}} +{"error": {"class": "GenericError", "desc": "The requested file size is too large"}} +{"error": {"class": "GenericError", "desc": "The requested file size is too large"}} +{"error": {"class": "GenericError", "desc": "The requested file size is too large"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + +*** done diff --git a/tests/qemu-iotests/check b/tests/qemu-iotests/check index e6b6ff7a04..ec8033350d 100755 --- a/tests/qemu-iotests/check +++ b/tests/qemu-iotests/check @@ -92,7 +92,7 @@ set_prog_path() { p=`command -v $1 2> /dev/null` if [ -n "$p" -a -x "$p" ]; then - realpath -- "$(type -p "$p")" + type -p "$p" else return 1 fi @@ -284,7 +284,6 @@ testlist options -parallels) IMGFMT=parallels - IMGFMT_GENERIC=false xpand=false ;; @@ -555,7 +554,7 @@ then [ "$QEMU_PROG" = "" ] && _init_error "qemu not found" fi fi -export QEMU_PROG=$(realpath -- "$(type -p "$QEMU_PROG")") +export QEMU_PROG="$(type -p "$QEMU_PROG")" if [ -z "$QEMU_IMG_PROG" ]; then if [ -x "$build_iotests/qemu-img" ]; then @@ -566,7 +565,7 @@ if [ -z "$QEMU_IMG_PROG" ]; then _init_error "qemu-img not found" fi fi -export QEMU_IMG_PROG=$(realpath -- "$(type -p "$QEMU_IMG_PROG")") +export QEMU_IMG_PROG="$(type -p "$QEMU_IMG_PROG")" if [ -z "$QEMU_IO_PROG" ]; then if [ -x "$build_iotests/qemu-io" ]; then @@ -577,7 +576,7 @@ if [ -z "$QEMU_IO_PROG" ]; then _init_error "qemu-io not found" fi fi -export QEMU_IO_PROG=$(realpath -- "$(type -p "$QEMU_IO_PROG")") +export QEMU_IO_PROG="$(type -p "$QEMU_IO_PROG")" if [ -z $QEMU_NBD_PROG ]; then if [ -x "$build_iotests/qemu-nbd" ]; then @@ -588,7 +587,7 @@ if [ -z $QEMU_NBD_PROG ]; then _init_error "qemu-nbd not found" fi fi -export QEMU_NBD_PROG=$(realpath -- "$(type -p "$QEMU_NBD_PROG")") +export QEMU_NBD_PROG="$(type -p "$QEMU_NBD_PROG")" if [ -z "$QEMU_VXHS_PROG" ]; then export QEMU_VXHS_PROG="`set_prog_path qnio_server`" @@ -812,7 +811,7 @@ do else echo " - output mismatch (see $seq.out.bad)" mv $tmp.out $seq.out.bad - $diff -w "$reference" $(realpath $seq.out.bad) + $diff -w "$reference" "$PWD"/$seq.out.bad err=true fi fi diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc index 6fa0495e10..9a65a11026 100644 --- a/tests/qemu-iotests/common.rc +++ b/tests/qemu-iotests/common.rc @@ -332,7 +332,7 @@ _img_info() discard=0 regex_json_spec_start='^ *"format-specific": \{' - $QEMU_IMG info "$@" "$TEST_IMG" 2>&1 | \ + $QEMU_IMG info $QEMU_IMG_EXTRA_ARGS "$@" "$TEST_IMG" 2>&1 | \ sed -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \ -e "s#$TEST_DIR#TEST_DIR#g" \ -e "s#$IMGFMT#IMGFMT#g" \ diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index c401791fcd..efe0e958f2 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -169,6 +169,7 @@ 162 auto quick 163 rw auto quick 165 rw auto quick +169 rw auto quick 170 rw auto quick 171 rw auto quick 172 auto @@ -196,6 +197,7 @@ 196 rw auto quick 197 rw auto quick 198 rw auto +199 rw auto 200 rw auto 201 rw auto migration 202 rw auto quick @@ -204,3 +206,6 @@ 205 rw auto quick 206 rw auto 207 rw auto +208 rw auto quick +209 rw auto quick +210 rw auto diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index 1bcc9ca57d..b5d7945af8 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -23,12 +23,14 @@ import subprocess import string import unittest import sys -sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'scripts')) -import qtest import struct import json import signal import logging +import atexit + +sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'scripts')) +import qtest # This will not work if arguments contain spaces but is necessary if we @@ -249,6 +251,37 @@ class FilePath(object): return False +def file_path_remover(): + for path in reversed(file_path_remover.paths): + try: + os.remove(path) + except OSError: + pass + + +def file_path(*names): + ''' Another way to get auto-generated filename that cleans itself up. + + Use is as simple as: + + img_a, img_b = file_path('a.img', 'b.img') + sock = file_path('socket') + ''' + + if not hasattr(file_path_remover, 'paths'): + file_path_remover.paths = [] + atexit.register(file_path_remover) + + paths = [] + for name in names: + filename = '{0}-{1}'.format(os.getpid(), name) + path = os.path.join(test_dir, filename) + file_path_remover.paths.append(path) + paths.append(path) + + return paths[0] if len(paths) == 1 else paths + + class VM(qtest.QEMUQtestMachine): '''A QEMU VM''' @@ -473,10 +506,7 @@ class QMPTestCase(unittest.TestCase): event = self.wait_until_completed(drive=drive) self.assert_qmp(event, 'data/type', 'mirror') - def pause_job(self, job_id='job0'): - result = self.vm.qmp('block-job-pause', device=job_id) - self.assert_qmp(result, 'return', {}) - + def pause_wait(self, job_id='job0'): with Timeout(1, "Timeout waiting for job to pause"): while True: result = self.vm.qmp('query-block-jobs') @@ -484,6 +514,13 @@ class QMPTestCase(unittest.TestCase): if job['device'] == job_id and job['paused'] == True and job['busy'] == False: return job + def pause_job(self, job_id='job0', wait=True): + result = self.vm.qmp('block-job-pause', device=job_id) + self.assert_qmp(result, 'return', {}) + if wait: + return self.pause_wait(job_id) + return result + def notrun(reason): '''Skip this test suite''' @@ -504,6 +541,10 @@ def verify_platform(supported_oses=['linux']): if True not in [sys.platform.startswith(x) for x in supported_oses]: notrun('not suitable for this OS: %s' % sys.platform) +def verify_cache_mode(supported_cache_modes=[]): + if supported_cache_modes and (cachemode not in supported_cache_modes): + notrun('not suitable for this cache mode: %s' % cachemode) + def supports_quorum(): return 'quorum' in qemu_img_pipe('--help') @@ -512,7 +553,7 @@ def verify_quorum(): if not supports_quorum(): notrun('quorum support missing') -def main(supported_fmts=[], supported_oses=['linux']): +def main(supported_fmts=[], supported_oses=['linux'], supported_cache_modes=[]): '''Run tests''' global debug @@ -529,6 +570,7 @@ def main(supported_fmts=[], supported_oses=['linux']): verbosity = 1 verify_image_format(supported_fmts) verify_platform(supported_oses) + verify_cache_mode(supported_cache_modes) # We need to filter out the time taken from the output so that qemu-iotest # can reliably diff the results against master output. diff --git a/tests/qmp-test.c b/tests/qmp-test.c index 7470c6b754..07c0b87e27 100644 --- a/tests/qmp-test.c +++ b/tests/qmp-test.c @@ -20,6 +20,7 @@ #include "qapi/qobject-input-visitor.h" #include "qapi/util.h" #include "qapi/visitor.h" +#include "qapi/qmp/qstring.h" const char common_args[] = "-nodefaults -machine none"; @@ -79,6 +80,9 @@ static void test_qmp_protocol(void) QDict *resp, *q, *ret; QList *capabilities; QTestState *qts; + const QListEntry *entry; + QString *qstr; + int i; qts = qtest_init_without_qmp_handshake(common_args); @@ -88,7 +92,12 @@ static void test_qmp_protocol(void) g_assert(q); test_version(qdict_get(q, "version")); capabilities = qdict_get_qlist(q, "capabilities"); - g_assert(capabilities && qlist_empty(capabilities)); + g_assert(capabilities); + entry = qlist_first(capabilities); + g_assert(entry); + qstr = qobject_to(QString, entry->value); + g_assert(qstr); + g_assert_cmpstr(qstring_get_str(qstr), ==, "oob"); QDECREF(resp); /* Test valid command before handshake */ @@ -131,9 +140,94 @@ static void test_qmp_protocol(void) g_assert_cmpint(qdict_get_int(resp, "id"), ==, 2); QDECREF(resp); + /* + * Test command batching. In current test OOB is not enabled, we + * should be able to run as many commands in batch as we like. + * Using 16 (>8, which is OOB queue length) to make sure OOB won't + * break existing clients. Note: this test does not control the + * scheduling of QEMU's QMP command processing threads so it may + * not really trigger batching inside QEMU. This is just a + * best-effort test. + */ + for (i = 0; i < 16; i++) { + qtest_async_qmp(qts, "{ 'execute': 'query-version' }"); + } + /* Verify the replies to make sure no command is dropped. */ + for (i = 0; i < 16; i++) { + resp = qtest_qmp_receive(qts); + /* It should never be dropped. Each of them should be a reply. */ + g_assert(qdict_haskey(resp, "return")); + g_assert(!qdict_haskey(resp, "event")); + QDECREF(resp); + } + qtest_quit(qts); } +/* Tests for Out-Of-Band support. */ +static void test_qmp_oob(void) +{ + QDict *resp; + int acks = 0; + const char *cmd_id; + + global_qtest = qtest_init_without_qmp_handshake(common_args); + + /* Ignore the greeting message. */ + resp = qmp_receive(); + g_assert(qdict_get_qdict(resp, "QMP")); + QDECREF(resp); + + /* Try a fake capability, it should fail. */ + resp = qmp("{ 'execute': 'qmp_capabilities', " + " 'arguments': { 'enable': [ 'cap-does-not-exist' ] } }"); + g_assert(qdict_haskey(resp, "error")); + QDECREF(resp); + + /* Now, enable OOB in current QMP session, it should succeed. */ + resp = qmp("{ 'execute': 'qmp_capabilities', " + " 'arguments': { 'enable': [ 'oob' ] } }"); + g_assert(qdict_haskey(resp, "return")); + QDECREF(resp); + + /* + * Try any command that does not support OOB but with OOB flag. We + * should get failure. + */ + resp = qmp("{ 'execute': 'query-cpus'," + " 'control': { 'run-oob': true } }"); + g_assert(qdict_haskey(resp, "error")); + QDECREF(resp); + + /* + * First send the "x-oob-test" command with lock=true and + * oob=false, it should hang the dispatcher and main thread; + * later, we send another lock=false with oob=true to continue + * that thread processing. Finally we should receive replies from + * both commands. + */ + qmp_async("{ 'execute': 'x-oob-test'," + " 'arguments': { 'lock': true }, " + " 'id': 'lock-cmd'}"); + qmp_async("{ 'execute': 'x-oob-test', " + " 'arguments': { 'lock': false }, " + " 'control': { 'run-oob': true }, " + " 'id': 'unlock-cmd' }"); + + /* Ignore all events. Wait for 2 acks */ + while (acks < 2) { + resp = qmp_receive(); + cmd_id = qdict_get_str(resp, "id"); + if (!g_strcmp0(cmd_id, "lock-cmd") || + !g_strcmp0(cmd_id, "unlock-cmd")) { + acks++; + } + QDECREF(resp); + } + + qtest_end(); +} + static int query_error_class(const char *cmd) { static struct { @@ -318,6 +412,7 @@ int main(int argc, char *argv[]) g_test_init(&argc, &argv, NULL); qtest_add_func("qmp/protocol", test_qmp_protocol); + qtest_add_func("qmp/oob", test_qmp_oob); qmp_schema_init(&schema); add_query_tests(&schema); diff --git a/tests/qom-test.c b/tests/qom-test.c index 9dab7ac61e..a34ff6ba53 100644 --- a/tests/qom-test.c +++ b/tests/qom-test.c @@ -62,9 +62,9 @@ static void test_properties(const char *path, bool recurse) } g_assert(qdict_haskey(response, "return")); - list = qobject_to_qlist(qdict_get(response, "return")); + list = qobject_to(QList, qdict_get(response, "return")); QLIST_FOREACH_ENTRY(list, entry) { - tuple = qobject_to_qdict(qlist_entry_obj(entry)); + tuple = qobject_to(QDict, qlist_entry_obj(entry)); bool is_child = strstart(qdict_get_str(tuple, "type"), "child<", NULL); bool is_link = strstart(qdict_get_str(tuple, "type"), "link<", NULL); diff --git a/tests/rcutorture.c b/tests/rcutorture.c index 4002ecf123..49311c82ea 100644 --- a/tests/rcutorture.c +++ b/tests/rcutorture.c @@ -238,7 +238,6 @@ long long rcu_stress_count[RCU_STRESS_PIPE_LEN + 1]; static void *rcu_read_stress_test(void *arg) { int i; - int itercnt = 0; struct rcu_stress *p; int pc; long long n_reads_local = 0; @@ -269,9 +268,6 @@ static void *rcu_read_stress_test(void *arg) } rcu_stress_local[pc]++; n_reads_local++; - if ((++itercnt % 0x1000) == 0) { - synchronize_rcu(); - } } qemu_mutex_lock(&counts_mutex); n_reads += n_reads_local; diff --git a/tests/socket-helpers.c b/tests/socket-helpers.c new file mode 100644 index 0000000000..8112763f5b --- /dev/null +++ b/tests/socket-helpers.c @@ -0,0 +1,149 @@ +/* + * Helper functions for tests using sockets + * + * Copyright 2015-2018 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + * + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "qemu/sockets.h" +#include "socket-helpers.h" + +#ifndef AI_ADDRCONFIG +# define AI_ADDRCONFIG 0 +#endif +#ifndef EAI_ADDRFAMILY +# define EAI_ADDRFAMILY 0 +#endif + +int socket_can_bind_connect(const char *hostname) +{ + int lfd = -1, cfd = -1, afd = -1; + struct addrinfo ai, *res = NULL; + struct sockaddr_storage ss; + socklen_t sslen = sizeof(ss); + int soerr; + socklen_t soerrlen = sizeof(soerr); + bool check_soerr = false; + int rc; + int ret = -1; + + memset(&ai, 0, sizeof(ai)); + ai.ai_flags = AI_CANONNAME | AI_ADDRCONFIG; + ai.ai_family = AF_UNSPEC; + ai.ai_socktype = SOCK_STREAM; + + /* lookup */ + rc = getaddrinfo(hostname, NULL, &ai, &res); + if (rc != 0) { + if (rc == EAI_ADDRFAMILY || + rc == EAI_FAMILY) { + errno = EADDRNOTAVAIL; + } else { + errno = EINVAL; + } + goto cleanup; + } + + lfd = qemu_socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (lfd < 0) { + goto cleanup; + } + + cfd = qemu_socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (cfd < 0) { + goto cleanup; + } + + if (bind(lfd, res->ai_addr, res->ai_addrlen) < 0) { + goto cleanup; + } + + if (listen(lfd, 1) < 0) { + goto cleanup; + } + + if (getsockname(lfd, (struct sockaddr *)&ss, &sslen) < 0) { + goto cleanup; + } + + qemu_set_nonblock(cfd); + if (connect(cfd, (struct sockaddr *)&ss, sslen) < 0) { + if (errno == EINPROGRESS) { + check_soerr = true; + } else { + goto cleanup; + } + } + + sslen = sizeof(ss); + afd = accept(lfd, (struct sockaddr *)&ss, &sslen); + if (afd < 0) { + goto cleanup; + } + + if (check_soerr) { + if (qemu_getsockopt(cfd, SOL_SOCKET, SO_ERROR, &soerr, &soerrlen) < 0) { + goto cleanup; + } + if (soerr) { + errno = soerr; + goto cleanup; + } + } + + ret = 0; + + cleanup: + if (afd != -1) { + close(afd); + } + if (cfd != -1) { + close(cfd); + } + if (lfd != -1) { + close(lfd); + } + if (res) { + freeaddrinfo(res); + } + return ret; +} + + +int socket_check_protocol_support(bool *has_ipv4, bool *has_ipv6) +{ + *has_ipv4 = *has_ipv6 = false; + + if (socket_can_bind_connect("127.0.0.1") < 0) { + if (errno != EADDRNOTAVAIL) { + return -1; + } + } else { + *has_ipv4 = true; + } + + if (socket_can_bind_connect("::1") < 0) { + if (errno != EADDRNOTAVAIL) { + return -1; + } + } else { + *has_ipv6 = true; + } + + return 0; +} diff --git a/tests/socket-helpers.h b/tests/socket-helpers.h new file mode 100644 index 0000000000..1c07d6d656 --- /dev/null +++ b/tests/socket-helpers.h @@ -0,0 +1,42 @@ +/* + * Helper functions for tests using sockets + * + * Copyright 2015-2018 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + * + */ + +/* + * @hostname: a DNS name or numeric IP address + * + * Check whether it is possible to bind & connect to ports + * on the DNS name or IP address @hostname. If an IP address + * is used, it must not be a wildcard address. + * + * Returns 0 on success, -1 on error with errno set + */ +int socket_can_bind_connect(const char *hostname); + +/* + * @has_ipv4: set to true on return if IPv4 is available + * @has_ipv6: set to true on return if IPv6 is available + * + * Check whether IPv4 and/or IPv6 are available for use. + * On success, @has_ipv4 and @has_ipv6 will be set to + * indicate whether the respective protocols are available. + * + * Returns 0 on success, -1 on fatal error + */ +int socket_check_protocol_support(bool *has_ipv4, bool *has_ipv6); diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c index d760e2b243..7673de1062 100644 --- a/tests/test-bdrv-drain.c +++ b/tests/test-bdrv-drain.c @@ -505,6 +505,7 @@ static void coroutine_fn test_job_start(void *opaque) { TestBlockJob *s = opaque; + block_job_event_ready(&s->common); while (!s->should_complete) { block_job_sleep_ns(&s->common, 100000); } @@ -541,8 +542,8 @@ static void test_blockjob_common(enum drain_type drain_type) blk_target = blk_new(BLK_PERM_ALL, BLK_PERM_ALL); blk_insert_bs(blk_target, target, &error_abort); - job = block_job_create("job0", &test_job_driver, src, 0, BLK_PERM_ALL, 0, - 0, NULL, NULL, &error_abort); + job = block_job_create("job0", &test_job_driver, NULL, src, 0, BLK_PERM_ALL, + 0, 0, NULL, NULL, &error_abort); block_job_add_bdrv(job, "target", target, 0, BLK_PERM_ALL, &error_abort); block_job_start(job); diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c index 3591c9617f..5789893dda 100644 --- a/tests/test-blockjob-txn.c +++ b/tests/test-blockjob-txn.c @@ -87,7 +87,7 @@ static const BlockJobDriver test_block_job_driver = { */ static BlockJob *test_block_job_start(unsigned int iterations, bool use_timer, - int rc, int *result) + int rc, int *result, BlockJobTxn *txn) { BlockDriverState *bs; TestBlockJob *s; @@ -101,7 +101,7 @@ static BlockJob *test_block_job_start(unsigned int iterations, g_assert_nonnull(bs); snprintf(job_id, sizeof(job_id), "job%u", counter++); - s = block_job_create(job_id, &test_block_job_driver, bs, + s = block_job_create(job_id, &test_block_job_driver, txn, bs, 0, BLK_PERM_ALL, 0, BLOCK_JOB_DEFAULT, test_block_job_cb, data, &error_abort); s->iterations = iterations; @@ -120,12 +120,11 @@ static void test_single_job(int expected) int result = -EINPROGRESS; txn = block_job_txn_new(); - job = test_block_job_start(1, true, expected, &result); - block_job_txn_add_job(txn, job); + job = test_block_job_start(1, true, expected, &result, txn); block_job_start(job); if (expected == -ECANCELED) { - block_job_cancel(job); + block_job_cancel(job, false); } while (result == -EINPROGRESS) { @@ -160,10 +159,8 @@ static void test_pair_jobs(int expected1, int expected2) 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); + job1 = test_block_job_start(1, true, expected1, &result1, txn); + job2 = test_block_job_start(2, true, expected2, &result2, txn); block_job_start(job1); block_job_start(job2); @@ -173,10 +170,10 @@ static void test_pair_jobs(int expected1, int expected2) block_job_txn_unref(txn); if (expected1 == -ECANCELED) { - block_job_cancel(job1); + block_job_cancel(job1, false); } if (expected2 == -ECANCELED) { - block_job_cancel(job2); + block_job_cancel(job2, false); } while (result1 == -EINPROGRESS || result2 == -EINPROGRESS) { @@ -224,14 +221,12 @@ static void test_pair_jobs_fail_cancel_race(void) 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); + job1 = test_block_job_start(1, true, -ECANCELED, &result1, txn); + job2 = test_block_job_start(2, false, 0, &result2, txn); block_job_start(job1); block_job_start(job2); - block_job_cancel(job1); + block_job_cancel(job1, false); /* Now make job2 finish before the main loop kicks jobs. This simulates * the race between a pending kick and another job completing. diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c index 23bdf1a932..8946bfd37b 100644 --- a/tests/test-blockjob.c +++ b/tests/test-blockjob.c @@ -24,14 +24,15 @@ static void block_job_cb(void *opaque, int ret) { } -static BlockJob *do_test_id(BlockBackend *blk, const char *id, - bool should_succeed) +static BlockJob *mk_job(BlockBackend *blk, const char *id, + const BlockJobDriver *drv, bool should_succeed, + int flags) { BlockJob *job; Error *errp = NULL; - job = block_job_create(id, &test_block_job_driver, blk_bs(blk), - 0, BLK_PERM_ALL, 0, BLOCK_JOB_DEFAULT, block_job_cb, + job = block_job_create(id, drv, NULL, blk_bs(blk), + 0, BLK_PERM_ALL, 0, flags, block_job_cb, NULL, &errp); if (should_succeed) { g_assert_null(errp); @@ -50,6 +51,13 @@ static BlockJob *do_test_id(BlockBackend *blk, const char *id, return job; } +static BlockJob *do_test_id(BlockBackend *blk, const char *id, + bool should_succeed) +{ + return mk_job(blk, id, &test_block_job_driver, + should_succeed, BLOCK_JOB_DEFAULT); +} + /* This creates a BlockBackend (optionally with a name) with a * BlockDriverState inserted. */ static BlockBackend *create_blk(const char *name) @@ -142,6 +150,216 @@ static void test_job_ids(void) destroy_blk(blk[2]); } +typedef struct CancelJob { + BlockJob common; + BlockBackend *blk; + bool should_converge; + bool should_complete; + bool completed; +} CancelJob; + +static void cancel_job_completed(BlockJob *job, void *opaque) +{ + CancelJob *s = opaque; + s->completed = true; + block_job_completed(job, 0); +} + +static void cancel_job_complete(BlockJob *job, Error **errp) +{ + CancelJob *s = container_of(job, CancelJob, common); + s->should_complete = true; +} + +static void coroutine_fn cancel_job_start(void *opaque) +{ + CancelJob *s = opaque; + + while (!s->should_complete) { + if (block_job_is_cancelled(&s->common)) { + goto defer; + } + + if (!s->common.ready && s->should_converge) { + block_job_event_ready(&s->common); + } + + block_job_sleep_ns(&s->common, 100000); + } + + defer: + block_job_defer_to_main_loop(&s->common, cancel_job_completed, s); +} + +static const BlockJobDriver test_cancel_driver = { + .instance_size = sizeof(CancelJob), + .start = cancel_job_start, + .complete = cancel_job_complete, +}; + +static CancelJob *create_common(BlockJob **pjob) +{ + BlockBackend *blk; + BlockJob *job; + CancelJob *s; + + blk = create_blk(NULL); + job = mk_job(blk, "Steve", &test_cancel_driver, true, + BLOCK_JOB_MANUAL_FINALIZE | BLOCK_JOB_MANUAL_DISMISS); + block_job_ref(job); + assert(job->status == BLOCK_JOB_STATUS_CREATED); + s = container_of(job, CancelJob, common); + s->blk = blk; + + *pjob = job; + return s; +} + +static void cancel_common(CancelJob *s) +{ + BlockJob *job = &s->common; + BlockBackend *blk = s->blk; + BlockJobStatus sts = job->status; + + block_job_cancel_sync(job); + if ((sts != BLOCK_JOB_STATUS_CREATED) && + (sts != BLOCK_JOB_STATUS_CONCLUDED)) { + BlockJob *dummy = job; + block_job_dismiss(&dummy, &error_abort); + } + assert(job->status == BLOCK_JOB_STATUS_NULL); + block_job_unref(job); + destroy_blk(blk); +} + +static void test_cancel_created(void) +{ + BlockJob *job; + CancelJob *s; + + s = create_common(&job); + cancel_common(s); +} + +static void test_cancel_running(void) +{ + BlockJob *job; + CancelJob *s; + + s = create_common(&job); + + block_job_start(job); + assert(job->status == BLOCK_JOB_STATUS_RUNNING); + + cancel_common(s); +} + +static void test_cancel_paused(void) +{ + BlockJob *job; + CancelJob *s; + + s = create_common(&job); + + block_job_start(job); + assert(job->status == BLOCK_JOB_STATUS_RUNNING); + + block_job_user_pause(job, &error_abort); + block_job_enter(job); + assert(job->status == BLOCK_JOB_STATUS_PAUSED); + + cancel_common(s); +} + +static void test_cancel_ready(void) +{ + BlockJob *job; + CancelJob *s; + + s = create_common(&job); + + block_job_start(job); + assert(job->status == BLOCK_JOB_STATUS_RUNNING); + + s->should_converge = true; + block_job_enter(job); + assert(job->status == BLOCK_JOB_STATUS_READY); + + cancel_common(s); +} + +static void test_cancel_standby(void) +{ + BlockJob *job; + CancelJob *s; + + s = create_common(&job); + + block_job_start(job); + assert(job->status == BLOCK_JOB_STATUS_RUNNING); + + s->should_converge = true; + block_job_enter(job); + assert(job->status == BLOCK_JOB_STATUS_READY); + + block_job_user_pause(job, &error_abort); + block_job_enter(job); + assert(job->status == BLOCK_JOB_STATUS_STANDBY); + + cancel_common(s); +} + +static void test_cancel_pending(void) +{ + BlockJob *job; + CancelJob *s; + + s = create_common(&job); + + block_job_start(job); + assert(job->status == BLOCK_JOB_STATUS_RUNNING); + + s->should_converge = true; + block_job_enter(job); + assert(job->status == BLOCK_JOB_STATUS_READY); + + block_job_complete(job, &error_abort); + block_job_enter(job); + while (!s->completed) { + aio_poll(qemu_get_aio_context(), true); + } + assert(job->status == BLOCK_JOB_STATUS_PENDING); + + cancel_common(s); +} + +static void test_cancel_concluded(void) +{ + BlockJob *job; + CancelJob *s; + + s = create_common(&job); + + block_job_start(job); + assert(job->status == BLOCK_JOB_STATUS_RUNNING); + + s->should_converge = true; + block_job_enter(job); + assert(job->status == BLOCK_JOB_STATUS_READY); + + block_job_complete(job, &error_abort); + block_job_enter(job); + while (!s->completed) { + aio_poll(qemu_get_aio_context(), true); + } + assert(job->status == BLOCK_JOB_STATUS_PENDING); + + block_job_finalize(job, &error_abort); + assert(job->status == BLOCK_JOB_STATUS_CONCLUDED); + + cancel_common(s); +} + int main(int argc, char **argv) { qemu_init_main_loop(&error_abort); @@ -149,5 +367,12 @@ int main(int argc, char **argv) g_test_init(&argc, &argv, NULL); g_test_add_func("/blockjob/ids", test_job_ids); + g_test_add_func("/blockjob/cancel/created", test_cancel_created); + g_test_add_func("/blockjob/cancel/running", test_cancel_running); + g_test_add_func("/blockjob/cancel/paused", test_cancel_paused); + g_test_add_func("/blockjob/cancel/ready", test_cancel_ready); + g_test_add_func("/blockjob/cancel/standby", test_cancel_standby); + g_test_add_func("/blockjob/cancel/pending", test_cancel_pending); + g_test_add_func("/blockjob/cancel/concluded", test_cancel_concluded); return g_test_run(); } diff --git a/tests/test-char.c b/tests/test-char.c index b3a77af085..306c728335 100644 --- a/tests/test-char.c +++ b/tests/test-char.c @@ -166,7 +166,6 @@ static void char_mux_test(void) FeHandler h1 = { 0, }, h2 = { 0, }; CharBackend chr_be1, chr_be2; - muxes_realized = true; /* done after machine init */ opts = qemu_opts_create(qemu_find_opts("chardev"), "mux-label", 1, &error_abort); qemu_opt_set(opts, "backend", "ringbuf", &error_abort); @@ -302,9 +301,8 @@ static int socket_can_read_hello(void *opaque) return 10; } -static void char_socket_test(void) +static void char_socket_test_common(Chardev *chr) { - Chardev *chr = qemu_chr_new("server", "tcp:127.0.0.1:0,server,nowait"); Chardev *chr_client; QObject *addr; QDict *qdict; @@ -321,7 +319,7 @@ static void char_socket_test(void) g_assert(!object_property_get_bool(OBJECT(chr), "connected", &error_abort)); addr = object_property_get_qobject(OBJECT(chr), "addr", &error_abort); - qdict = qobject_to_qdict(addr); + qdict = qobject_to(QDict, addr); port = qdict_get_str(qdict, "port"); tmp = g_strdup_printf("tcp:127.0.0.1:%s", port); QDECREF(qdict); @@ -359,6 +357,47 @@ static void char_socket_test(void) object_unparent(OBJECT(chr)); } + +static void char_socket_basic_test(void) +{ + Chardev *chr = qemu_chr_new("server", "tcp:127.0.0.1:0,server,nowait"); + + char_socket_test_common(chr); +} + + +static void char_socket_fdpass_test(void) +{ + Chardev *chr; + char *optstr; + QemuOpts *opts; + int fd; + SocketAddress *addr = g_new0(SocketAddress, 1); + + addr->type = SOCKET_ADDRESS_TYPE_INET; + addr->u.inet.host = g_strdup("127.0.0.1"); + addr->u.inet.port = g_strdup("0"); + + fd = socket_listen(addr, &error_abort); + g_assert(fd >= 0); + + qapi_free_SocketAddress(addr); + + optstr = g_strdup_printf("socket,id=cdev,fd=%d,server,nowait", fd); + + opts = qemu_opts_parse_noisily(qemu_find_opts("chardev"), + optstr, true); + g_free(optstr); + g_assert_nonnull(opts); + + chr = qemu_chr_new_from_opts(opts, &error_abort); + + qemu_opts_del(opts); + + char_socket_test_common(chr); +} + + #ifndef _WIN32 static void char_pipe_test(void) { @@ -775,7 +814,8 @@ int main(int argc, char **argv) #ifndef _WIN32 g_test_add_func("/char/file-fifo", char_file_fifo_test); #endif - g_test_add_func("/char/socket", char_socket_test); + g_test_add_func("/char/socket/basic", char_socket_basic_test); + g_test_add_func("/char/socket/fdpass", char_socket_fdpass_test); g_test_add_func("/char/udp", char_udp_test); #ifdef HAVE_CHARDEV_SERIAL g_test_add_func("/char/serial", char_serial_test); diff --git a/tests/test-cutils.c b/tests/test-cutils.c index f64a49b7fb..64a489c2e9 100644 --- a/tests/test-cutils.c +++ b/tests/test-cutils.c @@ -223,6 +223,583 @@ static void test_parse_uint_full_correct(void) g_assert_cmpint(i, ==, 123); } +static void test_qemu_strtoi_correct(void) +{ + const char *str = "12345 foo"; + char f = 'X'; + const char *endptr = &f; + int res = 999; + int err; + + err = qemu_strtoi(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 12345); + g_assert(endptr == str + 5); +} + +static void test_qemu_strtoi_null(void) +{ + char f = 'X'; + const char *endptr = &f; + int res = 999; + int err; + + err = qemu_strtoi(NULL, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == NULL); +} + +static void test_qemu_strtoi_empty(void) +{ + const char *str = ""; + char f = 'X'; + const char *endptr = &f; + int res = 999; + int err; + + err = qemu_strtoi(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); +} + +static void test_qemu_strtoi_whitespace(void) +{ + const char *str = " \t "; + char f = 'X'; + const char *endptr = &f; + int res = 999; + int err; + + err = qemu_strtoi(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); +} + +static void test_qemu_strtoi_invalid(void) +{ + const char *str = " xxxx \t abc"; + char f = 'X'; + const char *endptr = &f; + int res = 999; + int err; + + err = qemu_strtoi(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); +} + +static void test_qemu_strtoi_trailing(void) +{ + const char *str = "123xxx"; + char f = 'X'; + const char *endptr = &f; + int res = 999; + int err; + + err = qemu_strtoi(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 123); + g_assert(endptr == str + 3); +} + +static void test_qemu_strtoi_octal(void) +{ + const char *str = "0123"; + char f = 'X'; + const char *endptr = &f; + int res = 999; + int err; + + err = qemu_strtoi(str, &endptr, 8, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0123); + g_assert(endptr == str + strlen(str)); + + res = 999; + endptr = &f; + err = qemu_strtoi(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0123); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtoi_decimal(void) +{ + const char *str = "0123"; + char f = 'X'; + const char *endptr = &f; + int res = 999; + int err; + + err = qemu_strtoi(str, &endptr, 10, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 123); + g_assert(endptr == str + strlen(str)); + + str = "123"; + res = 999; + endptr = &f; + err = qemu_strtoi(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 123); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtoi_hex(void) +{ + const char *str = "0123"; + char f = 'X'; + const char *endptr = &f; + int res = 999; + int err; + + err = qemu_strtoi(str, &endptr, 16, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0x123); + g_assert(endptr == str + strlen(str)); + + str = "0x123"; + res = 999; + endptr = &f; + err = qemu_strtoi(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0x123); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtoi_max(void) +{ + char *str = g_strdup_printf("%d", INT_MAX); + char f = 'X'; + const char *endptr = &f; + int res = 999; + int err; + + err = qemu_strtoi(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, INT_MAX); + g_assert(endptr == str + strlen(str)); + g_free(str); +} + +static void test_qemu_strtoi_overflow(void) +{ + char *str = g_strdup_printf("%lld", (long long)INT_MAX + 1ll); + char f = 'X'; + const char *endptr = &f; + int res = 999; + int err; + + err = qemu_strtoi(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, INT_MAX); + g_assert(endptr == str + strlen(str)); + g_free(str); +} + +static void test_qemu_strtoi_underflow(void) +{ + char *str = g_strdup_printf("%lld", (long long)INT_MIN - 1ll); + char f = 'X'; + const char *endptr = &f; + int res = 999; + int err; + + err = qemu_strtoi(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, INT_MIN); + g_assert(endptr == str + strlen(str)); + g_free(str); +} + +static void test_qemu_strtoi_negative(void) +{ + const char *str = " \t -321"; + char f = 'X'; + const char *endptr = &f; + int res = 999; + int err; + + err = qemu_strtoi(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, -321); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtoi_full_correct(void) +{ + const char *str = "123"; + int res = 999; + int err; + + err = qemu_strtoi(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 123); +} + +static void test_qemu_strtoi_full_null(void) +{ + char f = 'X'; + const char *endptr = &f; + int res = 999; + int err; + + err = qemu_strtoi(NULL, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == NULL); +} + +static void test_qemu_strtoi_full_empty(void) +{ + const char *str = ""; + int res = 999L; + int err; + + err = qemu_strtoi(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); +} + +static void test_qemu_strtoi_full_negative(void) +{ + const char *str = " \t -321"; + int res = 999; + int err; + + err = qemu_strtoi(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, -321); +} + +static void test_qemu_strtoi_full_trailing(void) +{ + const char *str = "123xxx"; + int res; + int err; + + err = qemu_strtoi(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); +} + +static void test_qemu_strtoi_full_max(void) +{ + char *str = g_strdup_printf("%d", INT_MAX); + int res; + int err; + + err = qemu_strtoi(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, INT_MAX); + g_free(str); +} + +static void test_qemu_strtoui_correct(void) +{ + const char *str = "12345 foo"; + char f = 'X'; + const char *endptr = &f; + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, 12345); + g_assert(endptr == str + 5); +} + +static void test_qemu_strtoui_null(void) +{ + char f = 'X'; + const char *endptr = &f; + unsigned int res = 999; + int err; + + err = qemu_strtoui(NULL, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == NULL); +} + +static void test_qemu_strtoui_empty(void) +{ + const char *str = ""; + char f = 'X'; + const char *endptr = &f; + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); +} + +static void test_qemu_strtoui_whitespace(void) +{ + const char *str = " \t "; + char f = 'X'; + const char *endptr = &f; + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); +} + +static void test_qemu_strtoui_invalid(void) +{ + const char *str = " xxxx \t abc"; + char f = 'X'; + const char *endptr = &f; + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); +} + +static void test_qemu_strtoui_trailing(void) +{ + const char *str = "123xxx"; + char f = 'X'; + const char *endptr = &f; + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, 123); + g_assert(endptr == str + 3); +} + +static void test_qemu_strtoui_octal(void) +{ + const char *str = "0123"; + char f = 'X'; + const char *endptr = &f; + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, &endptr, 8, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, 0123); + g_assert(endptr == str + strlen(str)); + + res = 999; + endptr = &f; + err = qemu_strtoui(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, 0123); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtoui_decimal(void) +{ + const char *str = "0123"; + char f = 'X'; + const char *endptr = &f; + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, &endptr, 10, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, 123); + g_assert(endptr == str + strlen(str)); + + str = "123"; + res = 999; + endptr = &f; + err = qemu_strtoui(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, 123); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtoui_hex(void) +{ + const char *str = "0123"; + char f = 'X'; + const char *endptr = &f; + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, &endptr, 16, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmphex(res, ==, 0x123); + g_assert(endptr == str + strlen(str)); + + str = "0x123"; + res = 999; + endptr = &f; + err = qemu_strtoui(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmphex(res, ==, 0x123); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtoui_max(void) +{ + char *str = g_strdup_printf("%u", UINT_MAX); + char f = 'X'; + const char *endptr = &f; + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmphex(res, ==, UINT_MAX); + g_assert(endptr == str + strlen(str)); + g_free(str); +} + +static void test_qemu_strtoui_overflow(void) +{ + char *str = g_strdup_printf("%lld", (long long)UINT_MAX + 1ll); + char f = 'X'; + const char *endptr = &f; + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmphex(res, ==, UINT_MAX); + g_assert(endptr == str + strlen(str)); + g_free(str); +} + +static void test_qemu_strtoui_underflow(void) +{ + char *str = g_strdup_printf("%lld", (long long)INT_MIN - 1ll); + char f = 'X'; + const char *endptr = &f; + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, (unsigned int)-1); + g_assert(endptr == str + strlen(str)); + g_free(str); +} + +static void test_qemu_strtoui_negative(void) +{ + const char *str = " \t -321"; + char f = 'X'; + const char *endptr = &f; + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, (unsigned int)-321); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtoui_full_correct(void) +{ + const char *str = "123"; + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, 123); +} + +static void test_qemu_strtoui_full_null(void) +{ + unsigned int res = 999; + int err; + + err = qemu_strtoui(NULL, NULL, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); +} + +static void test_qemu_strtoui_full_empty(void) +{ + const char *str = ""; + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); +} +static void test_qemu_strtoui_full_negative(void) +{ + const char *str = " \t -321"; + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, NULL, 0, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, (unsigned int)-321); +} + +static void test_qemu_strtoui_full_trailing(void) +{ + const char *str = "123xxx"; + unsigned int res; + int err; + + err = qemu_strtoui(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); +} + +static void test_qemu_strtoui_full_max(void) +{ + char *str = g_strdup_printf("%u", UINT_MAX); + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmphex(res, ==, UINT_MAX); + g_free(str); +} + static void test_qemu_strtol_correct(void) { const char *str = "12345 foo"; @@ -1612,6 +2189,86 @@ int main(int argc, char **argv) g_test_add_func("/cutils/parse_uint_full/correct", test_parse_uint_full_correct); + /* qemu_strtoi() tests */ + g_test_add_func("/cutils/qemu_strtoi/correct", + test_qemu_strtoi_correct); + g_test_add_func("/cutils/qemu_strtoi/null", + test_qemu_strtoi_null); + g_test_add_func("/cutils/qemu_strtoi/empty", + test_qemu_strtoi_empty); + g_test_add_func("/cutils/qemu_strtoi/whitespace", + test_qemu_strtoi_whitespace); + g_test_add_func("/cutils/qemu_strtoi/invalid", + test_qemu_strtoi_invalid); + g_test_add_func("/cutils/qemu_strtoi/trailing", + test_qemu_strtoi_trailing); + g_test_add_func("/cutils/qemu_strtoi/octal", + test_qemu_strtoi_octal); + g_test_add_func("/cutils/qemu_strtoi/decimal", + test_qemu_strtoi_decimal); + g_test_add_func("/cutils/qemu_strtoi/hex", + test_qemu_strtoi_hex); + g_test_add_func("/cutils/qemu_strtoi/max", + test_qemu_strtoi_max); + g_test_add_func("/cutils/qemu_strtoi/overflow", + test_qemu_strtoi_overflow); + g_test_add_func("/cutils/qemu_strtoi/underflow", + test_qemu_strtoi_underflow); + g_test_add_func("/cutils/qemu_strtoi/negative", + test_qemu_strtoi_negative); + g_test_add_func("/cutils/qemu_strtoi_full/correct", + test_qemu_strtoi_full_correct); + g_test_add_func("/cutils/qemu_strtoi_full/null", + test_qemu_strtoi_full_null); + g_test_add_func("/cutils/qemu_strtoi_full/empty", + test_qemu_strtoi_full_empty); + g_test_add_func("/cutils/qemu_strtoi_full/negative", + test_qemu_strtoi_full_negative); + g_test_add_func("/cutils/qemu_strtoi_full/trailing", + test_qemu_strtoi_full_trailing); + g_test_add_func("/cutils/qemu_strtoi_full/max", + test_qemu_strtoi_full_max); + + /* qemu_strtoui() tests */ + g_test_add_func("/cutils/qemu_strtoui/correct", + test_qemu_strtoui_correct); + g_test_add_func("/cutils/qemu_strtoui/null", + test_qemu_strtoui_null); + g_test_add_func("/cutils/qemu_strtoui/empty", + test_qemu_strtoui_empty); + g_test_add_func("/cutils/qemu_strtoui/whitespace", + test_qemu_strtoui_whitespace); + g_test_add_func("/cutils/qemu_strtoui/invalid", + test_qemu_strtoui_invalid); + g_test_add_func("/cutils/qemu_strtoui/trailing", + test_qemu_strtoui_trailing); + g_test_add_func("/cutils/qemu_strtoui/octal", + test_qemu_strtoui_octal); + g_test_add_func("/cutils/qemu_strtoui/decimal", + test_qemu_strtoui_decimal); + g_test_add_func("/cutils/qemu_strtoui/hex", + test_qemu_strtoui_hex); + g_test_add_func("/cutils/qemu_strtoui/max", + test_qemu_strtoui_max); + g_test_add_func("/cutils/qemu_strtoui/overflow", + test_qemu_strtoui_overflow); + g_test_add_func("/cutils/qemu_strtoui/underflow", + test_qemu_strtoui_underflow); + g_test_add_func("/cutils/qemu_strtoui/negative", + test_qemu_strtoui_negative); + g_test_add_func("/cutils/qemu_strtoui_full/correct", + test_qemu_strtoui_full_correct); + g_test_add_func("/cutils/qemu_strtoui_full/null", + test_qemu_strtoui_full_null); + g_test_add_func("/cutils/qemu_strtoui_full/empty", + test_qemu_strtoui_full_empty); + g_test_add_func("/cutils/qemu_strtoui_full/negative", + test_qemu_strtoui_full_negative); + g_test_add_func("/cutils/qemu_strtoui_full/trailing", + test_qemu_strtoui_full_trailing); + g_test_add_func("/cutils/qemu_strtoui_full/max", + test_qemu_strtoui_full_max); + /* qemu_strtol() tests */ g_test_add_func("/cutils/qemu_strtol/correct", test_qemu_strtol_correct); diff --git a/tests/test-io-channel-socket.c b/tests/test-io-channel-socket.c index b273fd3ba2..0597213f93 100644 --- a/tests/test-io-channel-socket.c +++ b/tests/test-io-channel-socket.c @@ -22,77 +22,9 @@ #include "io/channel-socket.h" #include "io/channel-util.h" #include "io-channel-helpers.h" +#include "socket-helpers.h" #include "qapi/error.h" -#ifndef AI_ADDRCONFIG -# define AI_ADDRCONFIG 0 -#endif -#ifndef EAI_ADDRFAMILY -# define EAI_ADDRFAMILY 0 -#endif - -static int check_bind(const char *hostname, bool *has_proto) -{ - int fd = -1; - struct addrinfo ai, *res = NULL; - int rc; - int ret = -1; - - memset(&ai, 0, sizeof(ai)); - ai.ai_flags = AI_CANONNAME | AI_ADDRCONFIG; - ai.ai_family = AF_UNSPEC; - ai.ai_socktype = SOCK_STREAM; - - /* lookup */ - rc = getaddrinfo(hostname, NULL, &ai, &res); - if (rc != 0) { - if (rc == EAI_ADDRFAMILY || - rc == EAI_FAMILY) { - *has_proto = false; - goto done; - } - goto cleanup; - } - - fd = qemu_socket(res->ai_family, res->ai_socktype, res->ai_protocol); - if (fd < 0) { - goto cleanup; - } - - if (bind(fd, res->ai_addr, res->ai_addrlen) < 0) { - if (errno == EADDRNOTAVAIL) { - *has_proto = false; - goto done; - } - goto cleanup; - } - - *has_proto = true; - done: - ret = 0; - - cleanup: - if (fd != -1) { - close(fd); - } - if (res) { - freeaddrinfo(res); - } - return ret; -} - -static int check_protocol_support(bool *has_ipv4, bool *has_ipv6) -{ - if (check_bind("127.0.0.1", has_ipv4) < 0) { - return -1; - } - if (check_bind("::1", has_ipv6) < 0) { - return -1; - } - - return 0; -} - static void test_io_channel_set_socket_bufs(QIOChannel *src, QIOChannel *dst) @@ -566,7 +498,7 @@ int main(int argc, char **argv) * each protocol to avoid breaking tests on machines * with either IPv4 or IPv6 disabled. */ - if (check_protocol_support(&has_ipv4, &has_ipv6) < 0) { + if (socket_check_protocol_support(&has_ipv4, &has_ipv6) < 0) { return 1; } diff --git a/tests/test-keyval.c b/tests/test-keyval.c index 94eb4df28d..029f05202a 100644 --- a/tests/test-keyval.c +++ b/tests/test-keyval.c @@ -195,7 +195,7 @@ static void check_list012(QList *qlist) g_assert(qlist); for (i = 0; i < ARRAY_SIZE(expected); i++) { - qstr = qobject_to_qstring(qlist_pop(qlist)); + qstr = qobject_to(QString, qlist_pop(qlist)); g_assert(qstr); g_assert_cmpstr(qstring_get_str(qstr), ==, expected[i]); QDECREF(qstr); @@ -654,12 +654,12 @@ static void test_keyval_visit_any(void) QDECREF(qdict); visit_start_struct(v, NULL, NULL, 0, &error_abort); visit_type_any(v, "a", &any, &error_abort); - qlist = qobject_to_qlist(any); + qlist = qobject_to(QList, any); g_assert(qlist); - qstr = qobject_to_qstring(qlist_pop(qlist)); + qstr = qobject_to(QString, qlist_pop(qlist)); g_assert_cmpstr(qstring_get_str(qstr), ==, "null"); QDECREF(qstr); - qstr = qobject_to_qstring(qlist_pop(qlist)); + qstr = qobject_to(QString, qlist_pop(qlist)); g_assert_cmpstr(qstring_get_str(qstr), ==, "1"); g_assert(qlist_empty(qlist)); QDECREF(qstr); diff --git a/tests/test-qga.c b/tests/test-qga.c index 5c5b661f8a..e6ab788f31 100644 --- a/tests/test-qga.c +++ b/tests/test-qga.c @@ -297,8 +297,8 @@ static void test_qga_get_vcpus(gconstpointer fix) /* check there is at least a cpu */ list = qdict_get_qlist(ret, "return"); entry = qlist_first(list); - g_assert(qdict_haskey(qobject_to_qdict(entry->value), "online")); - g_assert(qdict_haskey(qobject_to_qdict(entry->value), "logical-id")); + g_assert(qdict_haskey(qobject_to(QDict, entry->value), "online")); + g_assert(qdict_haskey(qobject_to(QDict, entry->value), "logical-id")); QDECREF(ret); } @@ -318,10 +318,10 @@ static void test_qga_get_fsinfo(gconstpointer fix) list = qdict_get_qlist(ret, "return"); entry = qlist_first(list); if (entry) { - g_assert(qdict_haskey(qobject_to_qdict(entry->value), "name")); - g_assert(qdict_haskey(qobject_to_qdict(entry->value), "mountpoint")); - g_assert(qdict_haskey(qobject_to_qdict(entry->value), "type")); - g_assert(qdict_haskey(qobject_to_qdict(entry->value), "disk")); + g_assert(qdict_haskey(qobject_to(QDict, entry->value), "name")); + g_assert(qdict_haskey(qobject_to(QDict, entry->value), "mountpoint")); + g_assert(qdict_haskey(qobject_to(QDict, entry->value), "type")); + g_assert(qdict_haskey(qobject_to(QDict, entry->value), "disk")); } QDECREF(ret); @@ -363,8 +363,9 @@ static void test_qga_get_memory_blocks(gconstpointer fix) entry = qlist_first(list); /* newer versions of qga may return empty list without error */ if (entry) { - g_assert(qdict_haskey(qobject_to_qdict(entry->value), "phys-index")); - g_assert(qdict_haskey(qobject_to_qdict(entry->value), "online")); + g_assert(qdict_haskey(qobject_to(QDict, entry->value), + "phys-index")); + g_assert(qdict_haskey(qobject_to(QDict, entry->value), "online")); } } @@ -385,7 +386,7 @@ static void test_qga_network_get_interfaces(gconstpointer fix) /* check there is at least an interface */ list = qdict_get_qlist(ret, "return"); entry = qlist_first(list); - g_assert(qdict_haskey(qobject_to_qdict(entry->value), "name")); + g_assert(qdict_haskey(qobject_to(QDict, entry->value), "name")); QDECREF(ret); } diff --git a/tests/test-qmp-cmds.c b/tests/test-qmp-cmds.c index 5b1cee6912..93fbbb1b73 100644 --- a/tests/test-qmp-cmds.c +++ b/tests/test-qmp-cmds.c @@ -100,7 +100,7 @@ static void test_dispatch_cmd(void) resp = qmp_dispatch(&qmp_commands, QOBJECT(req)); assert(resp != NULL); - assert(!qdict_haskey(qobject_to_qdict(resp), "error")); + assert(!qdict_haskey(qobject_to(QDict, resp), "error")); qobject_decref(resp); QDECREF(req); @@ -117,7 +117,7 @@ static void test_dispatch_cmd_failure(void) resp = qmp_dispatch(&qmp_commands, QOBJECT(req)); assert(resp != NULL); - assert(qdict_haskey(qobject_to_qdict(resp), "error")); + assert(qdict_haskey(qobject_to(QDict, resp), "error")); qobject_decref(resp); QDECREF(req); @@ -131,7 +131,7 @@ static void test_dispatch_cmd_failure(void) resp = qmp_dispatch(&qmp_commands, QOBJECT(req)); assert(resp != NULL); - assert(qdict_haskey(qobject_to_qdict(resp), "error")); + assert(qdict_haskey(qobject_to(QDict, resp), "error")); qobject_decref(resp); QDECREF(req); @@ -145,7 +145,7 @@ static QObject *test_qmp_dispatch(QDict *req) resp_obj = qmp_dispatch(&qmp_commands, QOBJECT(req)); assert(resp_obj); - resp = qobject_to_qdict(resp_obj); + resp = qobject_to(QDict, resp_obj); assert(resp && !qdict_haskey(resp, "error")); ret = qdict_get(resp, "return"); assert(ret); @@ -176,7 +176,7 @@ static void test_dispatch_cmd_io(void) qdict_put(req, "arguments", args); qdict_put_str(req, "execute", "user_def_cmd2"); - ret = qobject_to_qdict(test_qmp_dispatch(req)); + ret = qobject_to(QDict, test_qmp_dispatch(req)); assert(!strcmp(qdict_get_str(ret, "string0"), "blah1")); ret_dict = qdict_get_qdict(ret, "dict1"); @@ -197,7 +197,7 @@ static void test_dispatch_cmd_io(void) qdict_put(req, "arguments", args3); qdict_put_str(req, "execute", "guest-get-time"); - ret3 = qobject_to_qnum(test_qmp_dispatch(req)); + ret3 = qobject_to(QNum, test_qmp_dispatch(req)); g_assert(qnum_get_try_int(ret3, &val)); g_assert_cmpint(val, ==, 66); QDECREF(ret3); diff --git a/tests/test-qmp-event.c b/tests/test-qmp-event.c index 31f35b3e66..bb1036615f 100644 --- a/tests/test-qmp-event.c +++ b/tests/test-qmp-event.c @@ -60,22 +60,22 @@ void qdict_cmp_do_simple(const char *key, QObject *obj1, void *opaque) switch (qobject_type(obj1)) { case QTYPE_QBOOL: - d->result = (qbool_get_bool(qobject_to_qbool(obj1)) == - qbool_get_bool(qobject_to_qbool(obj2))); + d->result = (qbool_get_bool(qobject_to(QBool, obj1)) == + qbool_get_bool(qobject_to(QBool, obj2))); return; case QTYPE_QNUM: - g_assert(qnum_get_try_int(qobject_to_qnum(obj1), &val1)); - g_assert(qnum_get_try_int(qobject_to_qnum(obj2), &val2)); + g_assert(qnum_get_try_int(qobject_to(QNum, obj1), &val1)); + g_assert(qnum_get_try_int(qobject_to(QNum, obj2), &val2)); d->result = val1 == val2; return; case QTYPE_QSTRING: - d->result = g_strcmp0(qstring_get_str(qobject_to_qstring(obj1)), - qstring_get_str(qobject_to_qstring(obj2))) == 0; + d->result = g_strcmp0(qstring_get_str(qobject_to(QString, obj1)), + qstring_get_str(qobject_to(QString, obj2))) == 0; return; case QTYPE_QDICT: - d_new.expect = qobject_to_qdict(obj2); + d_new.expect = qobject_to(QDict, obj2); d_new.result = true; - qdict_iter(qobject_to_qdict(obj1), qdict_cmp_do_simple, &d_new); + qdict_iter(qobject_to(QDict, obj1), qdict_cmp_do_simple, &d_new); d->result = d_new.result; return; default: diff --git a/tests/test-qobject-input-visitor.c b/tests/test-qobject-input-visitor.c index 79b1a8cb17..6dc59c6211 100644 --- a/tests/test-qobject-input-visitor.c +++ b/tests/test-qobject-input-visitor.c @@ -479,7 +479,7 @@ static void test_visitor_in_any(TestInputVisitorData *data, v = visitor_input_test_init(data, "-42"); visit_type_any(v, NULL, &res, &error_abort); - qnum = qobject_to_qnum(res); + qnum = qobject_to(QNum, res); g_assert(qnum); g_assert(qnum_get_try_int(qnum, &val)); g_assert_cmpint(val, ==, -42); @@ -487,22 +487,22 @@ static void test_visitor_in_any(TestInputVisitorData *data, v = visitor_input_test_init(data, "{ 'integer': -42, 'boolean': true, 'string': 'foo' }"); visit_type_any(v, NULL, &res, &error_abort); - qdict = qobject_to_qdict(res); + qdict = qobject_to(QDict, res); g_assert(qdict && qdict_size(qdict) == 3); qobj = qdict_get(qdict, "integer"); g_assert(qobj); - qnum = qobject_to_qnum(qobj); + qnum = qobject_to(QNum, qobj); g_assert(qnum); g_assert(qnum_get_try_int(qnum, &val)); g_assert_cmpint(val, ==, -42); qobj = qdict_get(qdict, "boolean"); g_assert(qobj); - qbool = qobject_to_qbool(qobj); + qbool = qobject_to(QBool, qobj); g_assert(qbool); g_assert(qbool_get_bool(qbool) == true); qobj = qdict_get(qdict, "string"); g_assert(qobj); - qstring = qobject_to_qstring(qobj); + qstring = qobject_to(QString, qobj); g_assert(qstring); g_assert_cmpstr(qstring_get_str(qstring), ==, "foo"); qobject_decref(res); @@ -1250,24 +1250,27 @@ static void test_visitor_in_fail_alternate(TestInputVisitorData *data, } static void do_test_visitor_in_qmp_introspect(TestInputVisitorData *data, - const char *schema_json) + const QLitObject *qlit) { SchemaInfoList *schema = NULL; + QObject *obj = qobject_from_qlit(qlit); Visitor *v; - v = visitor_input_test_init_raw(data, schema_json); + v = qobject_input_visitor_new(obj); visit_type_SchemaInfoList(v, NULL, &schema, &error_abort); g_assert(schema); qapi_free_SchemaInfoList(schema); + qobject_decref(obj); + visit_free(v); } static void test_visitor_in_qmp_introspect(TestInputVisitorData *data, const void *unused) { - do_test_visitor_in_qmp_introspect(data, test_qmp_schema_json); - do_test_visitor_in_qmp_introspect(data, qmp_schema_json); + do_test_visitor_in_qmp_introspect(data, &test_qmp_schema_qlit); + do_test_visitor_in_qmp_introspect(data, &qmp_schema_qlit); } int main(int argc, char **argv) diff --git a/tests/test-qobject-output-visitor.c b/tests/test-qobject-output-visitor.c index 11e8c5aa40..ecf21c0f31 100644 --- a/tests/test-qobject-output-visitor.c +++ b/tests/test-qobject-output-visitor.c @@ -66,7 +66,7 @@ static void test_visitor_out_int(TestOutputVisitorData *data, visit_type_int(data->ov, NULL, &value, &error_abort); - qnum = qobject_to_qnum(visitor_get(data)); + qnum = qobject_to(QNum, visitor_get(data)); g_assert(qnum); g_assert(qnum_get_try_int(qnum, &val)); g_assert_cmpint(val, ==, value); @@ -80,7 +80,7 @@ static void test_visitor_out_bool(TestOutputVisitorData *data, visit_type_bool(data->ov, NULL, &value, &error_abort); - qbool = qobject_to_qbool(visitor_get(data)); + qbool = qobject_to(QBool, visitor_get(data)); g_assert(qbool); g_assert(qbool_get_bool(qbool) == value); } @@ -93,7 +93,7 @@ static void test_visitor_out_number(TestOutputVisitorData *data, visit_type_number(data->ov, NULL, &value, &error_abort); - qnum = qobject_to_qnum(visitor_get(data)); + qnum = qobject_to(QNum, visitor_get(data)); g_assert(qnum); g_assert(qnum_get_double(qnum) == value); } @@ -106,7 +106,7 @@ static void test_visitor_out_string(TestOutputVisitorData *data, visit_type_str(data->ov, NULL, &string, &error_abort); - qstr = qobject_to_qstring(visitor_get(data)); + qstr = qobject_to(QString, visitor_get(data)); g_assert(qstr); g_assert_cmpstr(qstring_get_str(qstr), ==, string); } @@ -120,7 +120,7 @@ static void test_visitor_out_no_string(TestOutputVisitorData *data, /* A null string should return "" */ visit_type_str(data->ov, NULL, &string, &error_abort); - qstr = qobject_to_qstring(visitor_get(data)); + qstr = qobject_to(QString, visitor_get(data)); g_assert(qstr); g_assert_cmpstr(qstring_get_str(qstr), ==, ""); } @@ -134,7 +134,7 @@ static void test_visitor_out_enum(TestOutputVisitorData *data, for (i = 0; i < ENUM_ONE__MAX; i++) { visit_type_EnumOne(data->ov, "unused", &i, &error_abort); - qstr = qobject_to_qstring(visitor_get(data)); + qstr = qobject_to(QString, visitor_get(data)); g_assert(qstr); g_assert_cmpstr(qstring_get_str(qstr), ==, EnumOne_str(i)); visitor_reset(data); @@ -167,7 +167,7 @@ static void test_visitor_out_struct(TestOutputVisitorData *data, visit_type_TestStruct(data->ov, NULL, &p, &error_abort); - qdict = qobject_to_qdict(visitor_get(data)); + qdict = qobject_to(QDict, visitor_get(data)); g_assert(qdict); g_assert_cmpint(qdict_size(qdict), ==, 3); g_assert_cmpint(qdict_get_int(qdict, "integer"), ==, 42); @@ -206,7 +206,7 @@ static void test_visitor_out_struct_nested(TestOutputVisitorData *data, visit_type_UserDefTwo(data->ov, "unused", &ud2, &error_abort); - qdict = qobject_to_qdict(visitor_get(data)); + qdict = qobject_to(QDict, visitor_get(data)); g_assert(qdict); g_assert_cmpint(qdict_size(qdict), ==, 2); g_assert_cmpstr(qdict_get_str(qdict, "string0"), ==, strings[0]); @@ -280,7 +280,7 @@ static void test_visitor_out_list(TestOutputVisitorData *data, visit_type_TestStructList(data->ov, NULL, &head, &error_abort); - qlist = qobject_to_qlist(visitor_get(data)); + qlist = qobject_to(QList, visitor_get(data)); g_assert(qlist); g_assert(!qlist_empty(qlist)); @@ -289,7 +289,7 @@ static void test_visitor_out_list(TestOutputVisitorData *data, QLIST_FOREACH_ENTRY(qlist, entry) { QDict *qdict; - qdict = qobject_to_qdict(entry->value); + qdict = qobject_to(QDict, entry->value); g_assert(qdict); g_assert_cmpint(qdict_size(qdict), ==, 3); g_assert_cmpint(qdict_get_int(qdict, "integer"), ==, value_int + i); @@ -342,7 +342,7 @@ static void test_visitor_out_any(TestOutputVisitorData *data, qobj = QOBJECT(qnum_from_int(-42)); visit_type_any(data->ov, NULL, &qobj, &error_abort); - qnum = qobject_to_qnum(visitor_get(data)); + qnum = qobject_to(QNum, visitor_get(data)); g_assert(qnum); g_assert(qnum_get_try_int(qnum, &val)); g_assert_cmpint(val, ==, -42); @@ -356,16 +356,16 @@ static void test_visitor_out_any(TestOutputVisitorData *data, qobj = QOBJECT(qdict); visit_type_any(data->ov, NULL, &qobj, &error_abort); qobject_decref(qobj); - qdict = qobject_to_qdict(visitor_get(data)); + qdict = qobject_to(QDict, visitor_get(data)); g_assert(qdict); - qnum = qobject_to_qnum(qdict_get(qdict, "integer")); + qnum = qobject_to(QNum, qdict_get(qdict, "integer")); g_assert(qnum); g_assert(qnum_get_try_int(qnum, &val)); g_assert_cmpint(val, ==, -42); - qbool = qobject_to_qbool(qdict_get(qdict, "boolean")); + qbool = qobject_to(QBool, qdict_get(qdict, "boolean")); g_assert(qbool); g_assert(qbool_get_bool(qbool) == true); - qstring = qobject_to_qstring(qdict_get(qdict, "string")); + qstring = qobject_to(QString, qdict_get(qdict, "string")); g_assert(qstring); g_assert_cmpstr(qstring_get_str(qstring), ==, "foo"); } @@ -382,7 +382,7 @@ static void test_visitor_out_union_flat(TestOutputVisitorData *data, tmp->u.value1.boolean = true; visit_type_UserDefFlatUnion(data->ov, NULL, &tmp, &error_abort); - qdict = qobject_to_qdict(visitor_get(data)); + qdict = qobject_to(QDict, visitor_get(data)); g_assert(qdict); g_assert_cmpstr(qdict_get_str(qdict, "enum1"), ==, "value1"); g_assert_cmpstr(qdict_get_str(qdict, "string"), ==, "str"); @@ -406,7 +406,7 @@ static void test_visitor_out_alternate(TestOutputVisitorData *data, tmp->u.i = 42; visit_type_UserDefAlternate(data->ov, NULL, &tmp, &error_abort); - qnum = qobject_to_qnum(visitor_get(data)); + qnum = qobject_to(QNum, visitor_get(data)); g_assert(qnum); g_assert(qnum_get_try_int(qnum, &val)); g_assert_cmpint(val, ==, 42); @@ -419,7 +419,7 @@ static void test_visitor_out_alternate(TestOutputVisitorData *data, tmp->u.e = ENUM_ONE_VALUE1; visit_type_UserDefAlternate(data->ov, NULL, &tmp, &error_abort); - qstr = qobject_to_qstring(visitor_get(data)); + qstr = qobject_to(QString, visitor_get(data)); g_assert(qstr); g_assert_cmpstr(qstring_get_str(qstr), ==, "value1"); @@ -444,7 +444,7 @@ static void test_visitor_out_alternate(TestOutputVisitorData *data, tmp->u.udfu.u.value1.boolean = true; visit_type_UserDefAlternate(data->ov, NULL, &tmp, &error_abort); - qdict = qobject_to_qdict(visitor_get(data)); + qdict = qobject_to(QDict, visitor_get(data)); g_assert(qdict); g_assert_cmpint(qdict_size(qdict), ==, 4); g_assert_cmpint(qdict_get_int(qdict, "integer"), ==, 1); @@ -466,7 +466,7 @@ static void test_visitor_out_null(TestOutputVisitorData *data, visit_type_null(data->ov, "a", &null, &error_abort); visit_check_struct(data->ov, &error_abort); visit_end_struct(data->ov, NULL); - qdict = qobject_to_qdict(visitor_get(data)); + qdict = qobject_to(QDict, visitor_get(data)); g_assert(qdict); g_assert_cmpint(qdict_size(qdict), ==, 1); nil = qdict_get(qdict, "a"); @@ -610,10 +610,10 @@ static void check_native_list(QObject *qobj, QList *qlist; int i; - qdict = qobject_to_qdict(qobj); + qdict = qobject_to(QDict, qobj); g_assert(qdict); g_assert(qdict_haskey(qdict, "data")); - qlist = qlist_copy(qobject_to_qlist(qdict_get(qdict, "data"))); + qlist = qlist_copy(qobject_to(QList, qdict_get(qdict, "data"))); switch (kind) { case USER_DEF_NATIVE_LIST_UNION_KIND_U8: @@ -627,7 +627,7 @@ static void check_native_list(QObject *qobj, tmp = qlist_peek(qlist); g_assert(tmp); - qvalue = qobject_to_qnum(tmp); + qvalue = qobject_to(QNum, tmp); g_assert(qnum_get_try_uint(qvalue, &val)); g_assert_cmpint(val, ==, i); qobject_decref(qlist_pop(qlist)); @@ -651,7 +651,7 @@ static void check_native_list(QObject *qobj, tmp = qlist_peek(qlist); g_assert(tmp); - qvalue = qobject_to_qnum(tmp); + qvalue = qobject_to(QNum, tmp); g_assert(qnum_get_try_int(qvalue, &val)); g_assert_cmpint(val, ==, i); qobject_decref(qlist_pop(qlist)); @@ -663,7 +663,7 @@ static void check_native_list(QObject *qobj, QBool *qvalue; tmp = qlist_peek(qlist); g_assert(tmp); - qvalue = qobject_to_qbool(tmp); + qvalue = qobject_to(QBool, tmp); g_assert_cmpint(qbool_get_bool(qvalue), ==, i % 3 == 0); qobject_decref(qlist_pop(qlist)); } @@ -675,7 +675,7 @@ static void check_native_list(QObject *qobj, gchar str[8]; tmp = qlist_peek(qlist); g_assert(tmp); - qvalue = qobject_to_qstring(tmp); + qvalue = qobject_to(QString, tmp); sprintf(str, "%d", i); g_assert_cmpstr(qstring_get_str(qvalue), ==, str); qobject_decref(qlist_pop(qlist)); @@ -690,7 +690,7 @@ static void check_native_list(QObject *qobj, tmp = qlist_peek(qlist); g_assert(tmp); - qvalue = qobject_to_qnum(tmp); + qvalue = qobject_to(QNum, tmp); g_string_printf(double_expected, "%.6f", (double)i / 3); g_string_printf(double_actual, "%.6f", qnum_get_double(qvalue)); g_assert_cmpstr(double_actual->str, ==, double_expected->str); diff --git a/tests/test-util-sockets.c b/tests/test-util-sockets.c new file mode 100644 index 0000000000..acadd85e8f --- /dev/null +++ b/tests/test-util-sockets.c @@ -0,0 +1,266 @@ +/* + * Tests for util/qemu-sockets.c + * + * Copyright 2018 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this library; if not, see <http://www.gnu.org/licenses/>. + * + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "qemu/sockets.h" +#include "qapi/error.h" +#include "socket-helpers.h" +#include "monitor/monitor.h" + +static void test_fd_is_socket_bad(void) +{ + char *tmp = g_strdup("qemu-test-util-sockets-XXXXXX"); + int fd = mkstemp(tmp); + if (fd != 0) { + unlink(tmp); + } + g_free(tmp); + + g_assert(fd >= 0); + + g_assert(!fd_is_socket(fd)); + close(fd); +} + +static void test_fd_is_socket_good(void) +{ + int fd = qemu_socket(PF_INET, SOCK_STREAM, 0); + + g_assert(fd >= 0); + + g_assert(fd_is_socket(fd)); + close(fd); +} + +static int mon_fd = -1; +static const char *mon_fdname; + +int monitor_get_fd(Monitor *mon, const char *fdname, Error **errp) +{ + g_assert(cur_mon); + g_assert(mon == cur_mon); + if (mon_fd == -1 || !g_str_equal(mon_fdname, fdname)) { + error_setg(errp, "No fd named %s", fdname); + return -1; + } + return dup(mon_fd); +} + +/* Syms in libqemustub.a are discarded at .o file granularity. + * To replace monitor_get_fd() we must ensure everything in + * stubs/monitor.c is defined, to make sure monitor.o is discarded + * otherwise we get duplicate syms at link time. + */ +Monitor *cur_mon; +void monitor_init(Chardev *chr, int flags) {} + + +static void test_socket_fd_pass_name_good(void) +{ + SocketAddress addr; + int fd; + + cur_mon = g_malloc(1); /* Fake a monitor */ + mon_fdname = "myfd"; + mon_fd = qemu_socket(AF_INET, SOCK_STREAM, 0); + g_assert_cmpint(mon_fd, >, STDERR_FILENO); + + addr.type = SOCKET_ADDRESS_TYPE_FD; + addr.u.fd.str = g_strdup(mon_fdname); + + fd = socket_connect(&addr, &error_abort); + g_assert_cmpint(fd, !=, -1); + g_assert_cmpint(fd, !=, mon_fd); + close(fd); + + fd = socket_listen(&addr, &error_abort); + g_assert_cmpint(fd, !=, -1); + g_assert_cmpint(fd, !=, mon_fd); + close(fd); + + g_free(addr.u.fd.str); + mon_fdname = NULL; + close(mon_fd); + mon_fd = -1; + g_free(cur_mon); + cur_mon = NULL; +} + +static void test_socket_fd_pass_name_bad(void) +{ + SocketAddress addr; + Error *err = NULL; + int fd; + + cur_mon = g_malloc(1); /* Fake a monitor */ + mon_fdname = "myfd"; + mon_fd = dup(STDOUT_FILENO); + g_assert_cmpint(mon_fd, >, STDERR_FILENO); + + addr.type = SOCKET_ADDRESS_TYPE_FD; + addr.u.fd.str = g_strdup(mon_fdname); + + fd = socket_connect(&addr, &err); + g_assert_cmpint(fd, ==, -1); + error_free_or_abort(&err); + + fd = socket_listen(&addr, &err); + g_assert_cmpint(fd, ==, -1); + error_free_or_abort(&err); + + g_free(addr.u.fd.str); + mon_fdname = NULL; + close(mon_fd); + mon_fd = -1; + g_free(cur_mon); + cur_mon = NULL; +} + +static void test_socket_fd_pass_name_nomon(void) +{ + SocketAddress addr; + Error *err = NULL; + int fd; + + g_assert(cur_mon == NULL); + + addr.type = SOCKET_ADDRESS_TYPE_FD; + addr.u.fd.str = g_strdup("myfd"); + + fd = socket_connect(&addr, &err); + g_assert_cmpint(fd, ==, -1); + error_free_or_abort(&err); + + fd = socket_listen(&addr, &err); + g_assert_cmpint(fd, ==, -1); + error_free_or_abort(&err); + + g_free(addr.u.fd.str); +} + + +static void test_socket_fd_pass_num_good(void) +{ + SocketAddress addr; + int fd, sfd; + + g_assert(cur_mon == NULL); + sfd = qemu_socket(AF_INET, SOCK_STREAM, 0); + g_assert_cmpint(sfd, >, STDERR_FILENO); + + addr.type = SOCKET_ADDRESS_TYPE_FD; + addr.u.fd.str = g_strdup_printf("%d", sfd); + + fd = socket_connect(&addr, &error_abort); + g_assert_cmpint(fd, ==, sfd); + + fd = socket_listen(&addr, &error_abort); + g_assert_cmpint(fd, ==, sfd); + + g_free(addr.u.fd.str); + close(sfd); +} + +static void test_socket_fd_pass_num_bad(void) +{ + SocketAddress addr; + Error *err = NULL; + int fd, sfd; + + g_assert(cur_mon == NULL); + sfd = dup(STDOUT_FILENO); + + addr.type = SOCKET_ADDRESS_TYPE_FD; + addr.u.fd.str = g_strdup_printf("%d", sfd); + + fd = socket_connect(&addr, &err); + g_assert_cmpint(fd, ==, -1); + error_free_or_abort(&err); + + fd = socket_listen(&addr, &err); + g_assert_cmpint(fd, ==, -1); + error_free_or_abort(&err); + + g_free(addr.u.fd.str); + close(sfd); +} + +static void test_socket_fd_pass_num_nocli(void) +{ + SocketAddress addr; + Error *err = NULL; + int fd; + + cur_mon = g_malloc(1); /* Fake a monitor */ + + addr.type = SOCKET_ADDRESS_TYPE_FD; + addr.u.fd.str = g_strdup_printf("%d", STDOUT_FILENO); + + fd = socket_connect(&addr, &err); + g_assert_cmpint(fd, ==, -1); + error_free_or_abort(&err); + + fd = socket_listen(&addr, &err); + g_assert_cmpint(fd, ==, -1); + error_free_or_abort(&err); + + g_free(addr.u.fd.str); +} + + +int main(int argc, char **argv) +{ + bool has_ipv4, has_ipv6; + + socket_init(); + + g_test_init(&argc, &argv, NULL); + + /* We're creating actual IPv4/6 sockets, so we should + * check if the host running tests actually supports + * each protocol to avoid breaking tests on machines + * with either IPv4 or IPv6 disabled. + */ + if (socket_check_protocol_support(&has_ipv4, &has_ipv6) < 0) { + return 1; + } + + if (has_ipv4) { + g_test_add_func("/util/socket/is-socket/bad", + test_fd_is_socket_bad); + g_test_add_func("/util/socket/is-socket/good", + test_fd_is_socket_good); + g_test_add_func("/socket/fd-pass/name/good", + test_socket_fd_pass_name_good); + g_test_add_func("/socket/fd-pass/name/bad", + test_socket_fd_pass_name_bad); + g_test_add_func("/socket/fd-pass/name/nomon", + test_socket_fd_pass_name_nomon); + g_test_add_func("/socket/fd-pass/num/good", + test_socket_fd_pass_num_good); + g_test_add_func("/socket/fd-pass/num/bad", + test_socket_fd_pass_num_bad); + g_test_add_func("/socket/fd-pass/num/nocli", + test_socket_fd_pass_num_nocli); + } + + return g_test_run(); +} diff --git a/tests/test-x86-cpuid-compat.c b/tests/test-x86-cpuid-compat.c index 495dd1e7ef..02e41843fc 100644 --- a/tests/test-x86-cpuid-compat.c +++ b/tests/test-x86-cpuid-compat.c @@ -17,7 +17,7 @@ static char *get_cpu0_qom_path(void) g_assert(qdict_haskey(resp, "return")); ret = qdict_get_qlist(resp, "return"); - cpu0 = qobject_to_qdict(qlist_peek(ret)); + cpu0 = qobject_to(QDict, qlist_peek(ret)); path = g_strdup(qdict_get_str(cpu0, "qom_path")); QDECREF(resp); return path; @@ -38,7 +38,7 @@ static QObject *qom_get(const char *path, const char *prop) #ifdef CONFIG_HAS_GLIB_SUBPROCESS_TESTS static bool qom_get_bool(const char *path, const char *prop) { - QBool *value = qobject_to_qbool(qom_get(path, prop)); + QBool *value = qobject_to(QBool, qom_get(path, prop)); bool b = qbool_get_bool(value); QDECREF(value); @@ -61,7 +61,7 @@ static void test_cpuid_prop(const void *data) qtest_start(args->cmdline); path = get_cpu0_qom_path(); - value = qobject_to_qnum(qom_get(path, args->property)); + value = qobject_to(QNum, qom_get(path, args->property)); g_assert(qnum_get_try_int(value, &val)); g_assert_cmpint(val, ==, args->expected_value); qtest_end(); @@ -105,7 +105,7 @@ static uint32_t get_feature_word(QList *features, uint32_t eax, uint32_t ecx, const QListEntry *e; for (e = qlist_first(features); e; e = qlist_next(e)) { - QDict *w = qobject_to_qdict(qlist_entry_obj(e)); + QDict *w = qobject_to(QDict, qlist_entry_obj(e)); const char *rreg = qdict_get_str(w, "cpuid-register"); uint32_t reax = qdict_get_int(w, "cpuid-input-eax"); bool has_ecx = qdict_haskey(w, "cpuid-input-ecx"); @@ -116,8 +116,9 @@ static uint32_t get_feature_word(QList *features, uint32_t eax, uint32_t ecx, recx = qdict_get_int(w, "cpuid-input-ecx"); } if (eax == reax && (!has_ecx || ecx == recx) && !strcmp(rreg, reg)) { - g_assert(qnum_get_try_int(qobject_to_qnum(qdict_get(w, "features")), - &val)); + g_assert(qnum_get_try_int(qobject_to(QNum, + qdict_get(w, "features")), + &val)); return val; } } @@ -133,8 +134,8 @@ static void test_feature_flag(const void *data) qtest_start(args->cmdline); path = get_cpu0_qom_path(); - present = qobject_to_qlist(qom_get(path, "feature-words")); - filtered = qobject_to_qlist(qom_get(path, "filtered-features")); + present = qobject_to(QList, qom_get(path, "feature-words")); + filtered = qobject_to(QList, qom_get(path, "filtered-features")); value = get_feature_word(present, args->in_eax, args->in_ecx, args->reg); value |= get_feature_word(filtered, args->in_eax, args->in_ecx, args->reg); qtest_end(); diff --git a/tests/virtio-scsi-test.c b/tests/virtio-scsi-test.c index 7393d69bb2..037872bb98 100644 --- a/tests/virtio-scsi-test.c +++ b/tests/virtio-scsi-test.c @@ -216,6 +216,9 @@ static void test_unaligned_write_same(void) const uint8_t write_same_cdb_2[VIRTIO_SCSI_CDB_SIZE] = { 0x41, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x33, 0x00, 0x00 }; + const uint8_t write_same_cdb_ndob[VIRTIO_SCSI_CDB_SIZE] = { + 0x41, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x33, 0x00, 0x00 + }; vs = qvirtio_scsi_pci_init(PCI_SLOT); @@ -225,6 +228,9 @@ static void test_unaligned_write_same(void) g_assert_cmphex(0, ==, virtio_scsi_do_command(vs, write_same_cdb_2, NULL, 0, buf2, 512, NULL)); + g_assert_cmphex(0, ==, + virtio_scsi_do_command(vs, write_same_cdb_ndob, NULL, 0, NULL, 0, NULL)); + qvirtio_scsi_pci_free(vs); } |