| /* |
| * QemuOpts unit-tests. |
| * |
| * Copyright (C) 2014 Leandro Dorileo <l@dorileo.org> |
| * |
| * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. |
| * See the COPYING.LIB file in the top-level directory. |
| */ |
| |
| #include "qemu/osdep.h" |
| #include "qemu/units.h" |
| #include "qemu/option.h" |
| #include "qemu/option_int.h" |
| #include "qapi/error.h" |
| #include "qapi/qmp/qdict.h" |
| #include "qapi/qmp/qstring.h" |
| #include "qemu/config-file.h" |
| |
| |
| static QemuOptsList opts_list_01 = { |
| .name = "opts_list_01", |
| .head = QTAILQ_HEAD_INITIALIZER(opts_list_01.head), |
| .desc = { |
| { |
| .name = "str1", |
| .type = QEMU_OPT_STRING, |
| .help = "Help texts are preserved in qemu_opts_append", |
| .def_value_str = "default", |
| },{ |
| .name = "str2", |
| .type = QEMU_OPT_STRING, |
| },{ |
| .name = "str3", |
| .type = QEMU_OPT_STRING, |
| },{ |
| .name = "number1", |
| .type = QEMU_OPT_NUMBER, |
| .help = "Having help texts only for some options is okay", |
| },{ |
| .name = "number2", |
| .type = QEMU_OPT_NUMBER, |
| }, |
| { /* end of list */ } |
| }, |
| }; |
| |
| static QemuOptsList opts_list_02 = { |
| .name = "opts_list_02", |
| .head = QTAILQ_HEAD_INITIALIZER(opts_list_02.head), |
| .desc = { |
| { |
| .name = "str1", |
| .type = QEMU_OPT_STRING, |
| },{ |
| .name = "str2", |
| .type = QEMU_OPT_STRING, |
| },{ |
| .name = "bool1", |
| .type = QEMU_OPT_BOOL, |
| },{ |
| .name = "bool2", |
| .type = QEMU_OPT_BOOL, |
| },{ |
| .name = "size1", |
| .type = QEMU_OPT_SIZE, |
| },{ |
| .name = "size2", |
| .type = QEMU_OPT_SIZE, |
| },{ |
| .name = "size3", |
| .type = QEMU_OPT_SIZE, |
| }, |
| { /* end of list */ } |
| }, |
| }; |
| |
| static QemuOptsList opts_list_03 = { |
| .name = "opts_list_03", |
| .implied_opt_name = "implied", |
| .head = QTAILQ_HEAD_INITIALIZER(opts_list_03.head), |
| .desc = { |
| /* no elements => accept any params */ |
| { /* end of list */ } |
| }, |
| }; |
| |
| static QemuOptsList opts_list_04 = { |
| .name = "opts_list_04", |
| .head = QTAILQ_HEAD_INITIALIZER(opts_list_04.head), |
| .merge_lists = true, |
| .desc = { |
| { |
| .name = "str3", |
| .type = QEMU_OPT_STRING, |
| }, |
| { /* end of list */ } |
| }, |
| }; |
| |
| static void register_opts(void) |
| { |
| qemu_add_opts(&opts_list_01); |
| qemu_add_opts(&opts_list_02); |
| qemu_add_opts(&opts_list_03); |
| qemu_add_opts(&opts_list_04); |
| } |
| |
| static void test_find_unknown_opts(void) |
| { |
| QemuOptsList *list; |
| Error *err = NULL; |
| |
| /* should not return anything, we don't have an "unknown" option */ |
| list = qemu_find_opts_err("unknown", &err); |
| g_assert(list == NULL); |
| error_free_or_abort(&err); |
| } |
| |
| static void test_qemu_find_opts(void) |
| { |
| QemuOptsList *list; |
| |
| /* we have an "opts_list_01" option, should return it */ |
| list = qemu_find_opts("opts_list_01"); |
| g_assert(list != NULL); |
| g_assert_cmpstr(list->name, ==, "opts_list_01"); |
| } |
| |
| static void test_qemu_opts_create(void) |
| { |
| QemuOptsList *list; |
| QemuOpts *opts; |
| |
| list = qemu_find_opts("opts_list_01"); |
| g_assert(list != NULL); |
| g_assert(QTAILQ_EMPTY(&list->head)); |
| g_assert_cmpstr(list->name, ==, "opts_list_01"); |
| |
| /* should not find anything at this point */ |
| opts = qemu_opts_find(list, NULL); |
| g_assert(opts == NULL); |
| |
| /* create the opts */ |
| opts = qemu_opts_create(list, NULL, 0, &error_abort); |
| g_assert(opts != NULL); |
| g_assert(!QTAILQ_EMPTY(&list->head)); |
| |
| /* now we've create the opts, must find it */ |
| opts = qemu_opts_find(list, NULL); |
| g_assert(opts != NULL); |
| |
| qemu_opts_del(opts); |
| |
| /* should not find anything at this point */ |
| opts = qemu_opts_find(list, NULL); |
| g_assert(opts == NULL); |
| } |
| |
| static void test_qemu_opt_get(void) |
| { |
| QemuOptsList *list; |
| QemuOpts *opts; |
| const char *opt = NULL; |
| |
| list = qemu_find_opts("opts_list_01"); |
| g_assert(list != NULL); |
| g_assert(QTAILQ_EMPTY(&list->head)); |
| g_assert_cmpstr(list->name, ==, "opts_list_01"); |
| |
| /* should not find anything at this point */ |
| opts = qemu_opts_find(list, NULL); |
| g_assert(opts == NULL); |
| |
| /* create the opts */ |
| opts = qemu_opts_create(list, NULL, 0, &error_abort); |
| g_assert(opts != NULL); |
| g_assert(!QTAILQ_EMPTY(&list->head)); |
| |
| /* haven't set anything to str2 yet */ |
| opt = qemu_opt_get(opts, "str2"); |
| g_assert(opt == NULL); |
| |
| qemu_opt_set(opts, "str2", "value", &error_abort); |
| |
| /* now we have set str2, should know about it */ |
| opt = qemu_opt_get(opts, "str2"); |
| g_assert_cmpstr(opt, ==, "value"); |
| |
| qemu_opt_set(opts, "str2", "value2", &error_abort); |
| |
| /* having reset the value, the returned should be the reset one */ |
| opt = qemu_opt_get(opts, "str2"); |
| g_assert_cmpstr(opt, ==, "value2"); |
| |
| qemu_opts_del(opts); |
| |
| /* should not find anything at this point */ |
| opts = qemu_opts_find(list, NULL); |
| g_assert(opts == NULL); |
| } |
| |
| static void test_qemu_opt_get_bool(void) |
| { |
| QemuOptsList *list; |
| QemuOpts *opts; |
| bool opt; |
| |
| list = qemu_find_opts("opts_list_02"); |
| g_assert(list != NULL); |
| g_assert(QTAILQ_EMPTY(&list->head)); |
| g_assert_cmpstr(list->name, ==, "opts_list_02"); |
| |
| /* should not find anything at this point */ |
| opts = qemu_opts_find(list, NULL); |
| g_assert(opts == NULL); |
| |
| /* create the opts */ |
| opts = qemu_opts_create(list, NULL, 0, &error_abort); |
| g_assert(opts != NULL); |
| g_assert(!QTAILQ_EMPTY(&list->head)); |
| |
| /* haven't set anything to bool1 yet, so defval should be returned */ |
| opt = qemu_opt_get_bool(opts, "bool1", false); |
| g_assert(opt == false); |
| |
| qemu_opt_set_bool(opts, "bool1", true, &error_abort); |
| |
| /* now we have set bool1, should know about it */ |
| opt = qemu_opt_get_bool(opts, "bool1", false); |
| g_assert(opt == true); |
| |
| /* having reset the value, opt should be the reset one not defval */ |
| qemu_opt_set_bool(opts, "bool1", false, &error_abort); |
| |
| opt = qemu_opt_get_bool(opts, "bool1", true); |
| g_assert(opt == false); |
| |
| qemu_opts_del(opts); |
| |
| /* should not find anything at this point */ |
| opts = qemu_opts_find(list, NULL); |
| g_assert(opts == NULL); |
| } |
| |
| static void test_qemu_opt_get_number(void) |
| { |
| QemuOptsList *list; |
| QemuOpts *opts; |
| uint64_t opt; |
| |
| list = qemu_find_opts("opts_list_01"); |
| g_assert(list != NULL); |
| g_assert(QTAILQ_EMPTY(&list->head)); |
| g_assert_cmpstr(list->name, ==, "opts_list_01"); |
| |
| /* should not find anything at this point */ |
| opts = qemu_opts_find(list, NULL); |
| g_assert(opts == NULL); |
| |
| /* create the opts */ |
| opts = qemu_opts_create(list, NULL, 0, &error_abort); |
| g_assert(opts != NULL); |
| g_assert(!QTAILQ_EMPTY(&list->head)); |
| |
| /* haven't set anything to number1 yet, so defval should be returned */ |
| opt = qemu_opt_get_number(opts, "number1", 5); |
| g_assert(opt == 5); |
| |
| qemu_opt_set_number(opts, "number1", 10, &error_abort); |
| |
| /* now we have set number1, should know about it */ |
| opt = qemu_opt_get_number(opts, "number1", 5); |
| g_assert(opt == 10); |
| |
| /* having reset it, the returned should be the reset one not defval */ |
| qemu_opt_set_number(opts, "number1", 15, &error_abort); |
| |
| opt = qemu_opt_get_number(opts, "number1", 5); |
| g_assert(opt == 15); |
| |
| qemu_opts_del(opts); |
| |
| /* should not find anything at this point */ |
| opts = qemu_opts_find(list, NULL); |
| g_assert(opts == NULL); |
| } |
| |
| static void test_qemu_opt_get_size(void) |
| { |
| QemuOptsList *list; |
| QemuOpts *opts; |
| uint64_t opt; |
| QDict *dict; |
| |
| list = qemu_find_opts("opts_list_02"); |
| g_assert(list != NULL); |
| g_assert(QTAILQ_EMPTY(&list->head)); |
| g_assert_cmpstr(list->name, ==, "opts_list_02"); |
| |
| /* should not find anything at this point */ |
| opts = qemu_opts_find(list, NULL); |
| g_assert(opts == NULL); |
| |
| /* create the opts */ |
| opts = qemu_opts_create(list, NULL, 0, &error_abort); |
| g_assert(opts != NULL); |
| g_assert(!QTAILQ_EMPTY(&list->head)); |
| |
| /* haven't set anything to size1 yet, so defval should be returned */ |
| opt = qemu_opt_get_size(opts, "size1", 5); |
| g_assert(opt == 5); |
| |
| dict = qdict_new(); |
| g_assert(dict != NULL); |
| |
| qdict_put_str(dict, "size1", "10"); |
| |
| qemu_opts_absorb_qdict(opts, dict, &error_abort); |
| g_assert(error_abort == NULL); |
| |
| /* now we have set size1, should know about it */ |
| opt = qemu_opt_get_size(opts, "size1", 5); |
| g_assert(opt == 10); |
| |
| /* reset value */ |
| qdict_put_str(dict, "size1", "15"); |
| |
| qemu_opts_absorb_qdict(opts, dict, &error_abort); |
| g_assert(error_abort == NULL); |
| |
| /* test the reset value */ |
| opt = qemu_opt_get_size(opts, "size1", 5); |
| g_assert(opt == 15); |
| |
| qdict_del(dict, "size1"); |
| g_free(dict); |
| |
| qemu_opts_del(opts); |
| |
| /* should not find anything at this point */ |
| opts = qemu_opts_find(list, NULL); |
| g_assert(opts == NULL); |
| } |
| |
| static void test_qemu_opt_unset(void) |
| { |
| QemuOpts *opts; |
| const char *value; |
| int ret; |
| |
| /* dynamically initialized (parsed) opts */ |
| opts = qemu_opts_parse(&opts_list_03, "key=value", false, NULL); |
| g_assert(opts != NULL); |
| |
| /* check default/parsed value */ |
| value = qemu_opt_get(opts, "key"); |
| g_assert_cmpstr(value, ==, "value"); |
| |
| /* reset it to value2 */ |
| qemu_opt_set(opts, "key", "value2", &error_abort); |
| |
| value = qemu_opt_get(opts, "key"); |
| g_assert_cmpstr(value, ==, "value2"); |
| |
| /* unset, valid only for "accept any" */ |
| ret = qemu_opt_unset(opts, "key"); |
| g_assert(ret == 0); |
| |
| /* after reset the value should be the parsed/default one */ |
| value = qemu_opt_get(opts, "key"); |
| g_assert_cmpstr(value, ==, "value"); |
| |
| qemu_opts_del(opts); |
| } |
| |
| static void test_qemu_opts_reset(void) |
| { |
| QemuOptsList *list; |
| QemuOpts *opts; |
| uint64_t opt; |
| |
| list = qemu_find_opts("opts_list_01"); |
| g_assert(list != NULL); |
| g_assert(QTAILQ_EMPTY(&list->head)); |
| g_assert_cmpstr(list->name, ==, "opts_list_01"); |
| |
| /* should not find anything at this point */ |
| opts = qemu_opts_find(list, NULL); |
| g_assert(opts == NULL); |
| |
| /* create the opts */ |
| opts = qemu_opts_create(list, NULL, 0, &error_abort); |
| g_assert(opts != NULL); |
| g_assert(!QTAILQ_EMPTY(&list->head)); |
| |
| /* haven't set anything to number1 yet, so defval should be returned */ |
| opt = qemu_opt_get_number(opts, "number1", 5); |
| g_assert(opt == 5); |
| |
| qemu_opt_set_number(opts, "number1", 10, &error_abort); |
| |
| /* now we have set number1, should know about it */ |
| opt = qemu_opt_get_number(opts, "number1", 5); |
| g_assert(opt == 10); |
| |
| qemu_opts_reset(list); |
| |
| /* should not find anything at this point */ |
| opts = qemu_opts_find(list, NULL); |
| g_assert(opts == NULL); |
| } |
| |
| static void test_qemu_opts_set(void) |
| { |
| QemuOptsList *list; |
| QemuOpts *opts; |
| const char *opt; |
| |
| list = qemu_find_opts("opts_list_04"); |
| g_assert(list != NULL); |
| g_assert(QTAILQ_EMPTY(&list->head)); |
| g_assert_cmpstr(list->name, ==, "opts_list_04"); |
| |
| /* should not find anything at this point */ |
| opts = qemu_opts_find(list, NULL); |
| g_assert(opts == NULL); |
| |
| /* implicitly create opts and set str3 value */ |
| qemu_opts_set(list, "str3", "value", &error_abort); |
| g_assert(!QTAILQ_EMPTY(&list->head)); |
| |
| /* get the just created opts */ |
| opts = qemu_opts_find(list, NULL); |
| g_assert(opts != NULL); |
| |
| /* check the str3 value */ |
| opt = qemu_opt_get(opts, "str3"); |
| g_assert_cmpstr(opt, ==, "value"); |
| |
| qemu_opts_del(opts); |
| |
| /* should not find anything at this point */ |
| opts = qemu_opts_find(list, NULL); |
| g_assert(opts == NULL); |
| } |
| |
| static int opts_count_iter(void *opaque, const char *name, const char *value, |
| Error **errp) |
| { |
| (*(size_t *)opaque)++; |
| return 0; |
| } |
| |
| static size_t opts_count(QemuOpts *opts) |
| { |
| size_t n = 0; |
| |
| qemu_opt_foreach(opts, opts_count_iter, &n, NULL); |
| return n; |
| } |
| |
| static void test_opts_parse(void) |
| { |
| Error *err = NULL; |
| QemuOpts *opts; |
| |
| /* Nothing */ |
| opts = qemu_opts_parse(&opts_list_03, "", false, &error_abort); |
| g_assert_cmpuint(opts_count(opts), ==, 0); |
| |
| /* Empty key */ |
| opts = qemu_opts_parse(&opts_list_03, "=val", false, &error_abort); |
| g_assert_cmpuint(opts_count(opts), ==, 1); |
| g_assert_cmpstr(qemu_opt_get(opts, ""), ==, "val"); |
| |
| /* Multiple keys, last one wins */ |
| opts = qemu_opts_parse(&opts_list_03, "a=1,b=2,,x,a=3", |
| false, &error_abort); |
| g_assert_cmpuint(opts_count(opts), ==, 3); |
| g_assert_cmpstr(qemu_opt_get(opts, "a"), ==, "3"); |
| g_assert_cmpstr(qemu_opt_get(opts, "b"), ==, "2,x"); |
| |
| /* Except when it doesn't */ |
| opts = qemu_opts_parse(&opts_list_03, "id=foo,id=bar", |
| false, &error_abort); |
| g_assert_cmpuint(opts_count(opts), ==, 0); |
| g_assert_cmpstr(qemu_opts_id(opts), ==, "foo"); |
| |
| /* TODO Cover low-level access to repeated keys */ |
| |
| /* Trailing comma is ignored */ |
| opts = qemu_opts_parse(&opts_list_03, "x=y,", false, &error_abort); |
| g_assert_cmpuint(opts_count(opts), ==, 1); |
| g_assert_cmpstr(qemu_opt_get(opts, "x"), ==, "y"); |
| |
| /* Except when it isn't */ |
| opts = qemu_opts_parse(&opts_list_03, ",", false, &error_abort); |
| g_assert_cmpuint(opts_count(opts), ==, 1); |
| g_assert_cmpstr(qemu_opt_get(opts, ""), ==, "on"); |
| |
| /* Duplicate ID */ |
| opts = qemu_opts_parse(&opts_list_03, "x=y,id=foo", false, &err); |
| error_free_or_abort(&err); |
| g_assert(!opts); |
| /* TODO Cover .merge_lists = true */ |
| |
| /* Buggy ID recognition (fixed) */ |
| opts = qemu_opts_parse(&opts_list_03, "x=,,id=bar", false, &error_abort); |
| g_assert_cmpuint(opts_count(opts), ==, 1); |
| g_assert(!qemu_opts_id(opts)); |
| g_assert_cmpstr(qemu_opt_get(opts, "x"), ==, ",id=bar"); |
| |
| /* Anti-social ID */ |
| opts = qemu_opts_parse(&opts_list_01, "id=666", false, &err); |
| error_free_or_abort(&err); |
| g_assert(!opts); |
| |
| /* Implied value (qemu_opts_parse warns but accepts it) */ |
| opts = qemu_opts_parse(&opts_list_03, "an,noaus,noaus=", |
| false, &error_abort); |
| g_assert_cmpuint(opts_count(opts), ==, 3); |
| g_assert_cmpstr(qemu_opt_get(opts, "an"), ==, "on"); |
| g_assert_cmpstr(qemu_opt_get(opts, "aus"), ==, "off"); |
| g_assert_cmpstr(qemu_opt_get(opts, "noaus"), ==, ""); |
| |
| /* Implied value, negated empty key */ |
| opts = qemu_opts_parse(&opts_list_03, "no", false, &error_abort); |
| g_assert_cmpuint(opts_count(opts), ==, 1); |
| g_assert_cmpstr(qemu_opt_get(opts, ""), ==, "off"); |
| |
| /* Implied key */ |
| opts = qemu_opts_parse(&opts_list_03, "an,noaus,noaus=", true, |
| &error_abort); |
| g_assert_cmpuint(opts_count(opts), ==, 3); |
| g_assert_cmpstr(qemu_opt_get(opts, "implied"), ==, "an"); |
| g_assert_cmpstr(qemu_opt_get(opts, "aus"), ==, "off"); |
| g_assert_cmpstr(qemu_opt_get(opts, "noaus"), ==, ""); |
| |
| /* Implied key with empty value */ |
| opts = qemu_opts_parse(&opts_list_03, ",", true, &error_abort); |
| g_assert_cmpuint(opts_count(opts), ==, 1); |
| g_assert_cmpstr(qemu_opt_get(opts, "implied"), ==, ""); |
| |
| /* Implied key with comma value */ |
| opts = qemu_opts_parse(&opts_list_03, ",,,a=1", true, &error_abort); |
| g_assert_cmpuint(opts_count(opts), ==, 2); |
| g_assert_cmpstr(qemu_opt_get(opts, "implied"), ==, ","); |
| g_assert_cmpstr(qemu_opt_get(opts, "a"), ==, "1"); |
| |
| /* Empty key is not an implied key */ |
| opts = qemu_opts_parse(&opts_list_03, "=val", true, &error_abort); |
| g_assert_cmpuint(opts_count(opts), ==, 1); |
| g_assert_cmpstr(qemu_opt_get(opts, ""), ==, "val"); |
| |
| /* Unknown key */ |
| opts = qemu_opts_parse(&opts_list_01, "nonexistent=", false, &err); |
| error_free_or_abort(&err); |
| g_assert(!opts); |
| |
| qemu_opts_reset(&opts_list_01); |
| qemu_opts_reset(&opts_list_03); |
| } |
| |
| static void test_opts_parse_bool(void) |
| { |
| Error *err = NULL; |
| QemuOpts *opts; |
| |
| opts = qemu_opts_parse(&opts_list_02, "bool1=on,bool2=off", |
| false, &error_abort); |
| g_assert_cmpuint(opts_count(opts), ==, 2); |
| g_assert(qemu_opt_get_bool(opts, "bool1", false)); |
| g_assert(!qemu_opt_get_bool(opts, "bool2", true)); |
| |
| opts = qemu_opts_parse(&opts_list_02, "bool1=offer", false, &err); |
| error_free_or_abort(&err); |
| g_assert(!opts); |
| |
| qemu_opts_reset(&opts_list_02); |
| } |
| |
| static void test_opts_parse_number(void) |
| { |
| Error *err = NULL; |
| QemuOpts *opts; |
| |
| /* Lower limit zero */ |
| opts = qemu_opts_parse(&opts_list_01, "number1=0", false, &error_abort); |
| g_assert_cmpuint(opts_count(opts), ==, 1); |
| g_assert_cmpuint(qemu_opt_get_number(opts, "number1", 1), ==, 0); |
| |
| /* Upper limit 2^64-1 */ |
| opts = qemu_opts_parse(&opts_list_01, |
| "number1=18446744073709551615,number2=-1", |
| false, &error_abort); |
| g_assert_cmpuint(opts_count(opts), ==, 2); |
| g_assert_cmphex(qemu_opt_get_number(opts, "number1", 1), ==, UINT64_MAX); |
| g_assert_cmphex(qemu_opt_get_number(opts, "number2", 0), ==, UINT64_MAX); |
| |
| /* Above upper limit */ |
| opts = qemu_opts_parse(&opts_list_01, "number1=18446744073709551616", |
| false, &err); |
| error_free_or_abort(&err); |
| g_assert(!opts); |
| |
| /* Below lower limit */ |
| opts = qemu_opts_parse(&opts_list_01, "number1=-18446744073709551616", |
| false, &err); |
| error_free_or_abort(&err); |
| g_assert(!opts); |
| |
| /* Hex and octal */ |
| opts = qemu_opts_parse(&opts_list_01, "number1=0x2a,number2=052", |
| false, &error_abort); |
| g_assert_cmpuint(opts_count(opts), ==, 2); |
| g_assert_cmpuint(qemu_opt_get_number(opts, "number1", 1), ==, 42); |
| g_assert_cmpuint(qemu_opt_get_number(opts, "number2", 0), ==, 42); |
| |
| /* Invalid */ |
| opts = qemu_opts_parse(&opts_list_01, "number1=", false, &err); |
| error_free_or_abort(&err); |
| g_assert(!opts); |
| opts = qemu_opts_parse(&opts_list_01, "number1=eins", false, &err); |
| error_free_or_abort(&err); |
| g_assert(!opts); |
| |
| /* Leading whitespace */ |
| opts = qemu_opts_parse(&opts_list_01, "number1= \t42", |
| false, &error_abort); |
| g_assert_cmpuint(opts_count(opts), ==, 1); |
| g_assert_cmpuint(qemu_opt_get_number(opts, "number1", 1), ==, 42); |
| |
| /* Trailing crap */ |
| opts = qemu_opts_parse(&opts_list_01, "number1=3.14", false, &err); |
| error_free_or_abort(&err); |
| g_assert(!opts); |
| opts = qemu_opts_parse(&opts_list_01, "number1=08", false, &err); |
| error_free_or_abort(&err); |
| g_assert(!opts); |
| opts = qemu_opts_parse(&opts_list_01, "number1=0 ", false, &err); |
| error_free_or_abort(&err); |
| g_assert(!opts); |
| |
| qemu_opts_reset(&opts_list_01); |
| } |
| |
| static void test_opts_parse_size(void) |
| { |
| Error *err = NULL; |
| QemuOpts *opts; |
| |
| /* Lower limit zero */ |
| opts = qemu_opts_parse(&opts_list_02, "size1=0", false, &error_abort); |
| g_assert_cmpuint(opts_count(opts), ==, 1); |
| g_assert_cmpuint(qemu_opt_get_size(opts, "size1", 1), ==, 0); |
| |
| /* Note: full 64 bits of precision */ |
| |
| /* Around double limit of precision: 2^53-1, 2^53, 2^53+1 */ |
| opts = qemu_opts_parse(&opts_list_02, |
| "size1=9007199254740991," |
| "size2=9007199254740992," |
| "size3=9007199254740993", |
| false, &error_abort); |
| g_assert_cmpuint(opts_count(opts), ==, 3); |
| g_assert_cmphex(qemu_opt_get_size(opts, "size1", 1), |
| ==, 0x1fffffffffffff); |
| g_assert_cmphex(qemu_opt_get_size(opts, "size2", 1), |
| ==, 0x20000000000000); |
| g_assert_cmphex(qemu_opt_get_size(opts, "size3", 1), |
| ==, 0x20000000000001); |
| |
| /* Close to signed int limit: 2^63-1, 2^63, 2^63+1 */ |
| opts = qemu_opts_parse(&opts_list_02, |
| "size1=9223372036854775807," /* 7fffffffffffffff */ |
| "size2=9223372036854775808," /* 8000000000000000 */ |
| "size3=9223372036854775809", /* 8000000000000001 */ |
| false, &error_abort); |
| g_assert_cmpuint(opts_count(opts), ==, 3); |
| g_assert_cmphex(qemu_opt_get_size(opts, "size1", 1), |
| ==, 0x7fffffffffffffff); |
| g_assert_cmphex(qemu_opt_get_size(opts, "size2", 1), |
| ==, 0x8000000000000000); |
| g_assert_cmphex(qemu_opt_get_size(opts, "size3", 1), |
| ==, 0x8000000000000001); |
| |
| /* Close to actual upper limit 0xfffffffffffff800 (53 msbs set) */ |
| opts = qemu_opts_parse(&opts_list_02, |
| "size1=18446744073709549568," /* fffffffffffff800 */ |
| "size2=18446744073709550591", /* fffffffffffffbff */ |
| false, &error_abort); |
| g_assert_cmpuint(opts_count(opts), ==, 2); |
| g_assert_cmphex(qemu_opt_get_size(opts, "size1", 1), |
| ==, 0xfffffffffffff800); |
| g_assert_cmphex(qemu_opt_get_size(opts, "size2", 1), |
| ==, 0xfffffffffffffbff); |
| |
| /* Actual limit, 2^64-1 */ |
| opts = qemu_opts_parse(&opts_list_02, |
| "size1=18446744073709551615", /* ffffffffffffffff */ |
| false, &error_abort); |
| g_assert_cmpuint(opts_count(opts), ==, 1); |
| g_assert_cmphex(qemu_opt_get_size(opts, "size1", 1), |
| ==, 0xffffffffffffffff); |
| |
| /* Beyond limits */ |
| opts = qemu_opts_parse(&opts_list_02, "size1=-1", false, &err); |
| error_free_or_abort(&err); |
| g_assert(!opts); |
| opts = qemu_opts_parse(&opts_list_02, |
| "size1=18446744073709551616", /* 2^64 */ |
| false, &err); |
| error_free_or_abort(&err); |
| g_assert(!opts); |
| |
| /* Suffixes */ |
| opts = qemu_opts_parse(&opts_list_02, "size1=8b,size2=1.5k,size3=2M", |
| false, &error_abort); |
| g_assert_cmpuint(opts_count(opts), ==, 3); |
| g_assert_cmphex(qemu_opt_get_size(opts, "size1", 0), ==, 8); |
| g_assert_cmphex(qemu_opt_get_size(opts, "size2", 0), ==, 1536); |
| g_assert_cmphex(qemu_opt_get_size(opts, "size3", 0), ==, 2 * MiB); |
| opts = qemu_opts_parse(&opts_list_02, "size1=0.1G,size2=16777215T", |
| false, &error_abort); |
| g_assert_cmpuint(opts_count(opts), ==, 2); |
| g_assert_cmphex(qemu_opt_get_size(opts, "size1", 0), ==, GiB / 10); |
| g_assert_cmphex(qemu_opt_get_size(opts, "size2", 0), ==, 16777215ULL * TiB); |
| |
| /* Beyond limit with suffix */ |
| opts = qemu_opts_parse(&opts_list_02, "size1=16777216T", |
| false, &err); |
| error_free_or_abort(&err); |
| g_assert(!opts); |
| |
| /* Trailing crap */ |
| opts = qemu_opts_parse(&opts_list_02, "size1=16E", false, &err); |
| error_free_or_abort(&err); |
| g_assert(!opts); |
| opts = qemu_opts_parse(&opts_list_02, "size1=16Gi", false, &err); |
| error_free_or_abort(&err); |
| g_assert(!opts); |
| |
| qemu_opts_reset(&opts_list_02); |
| } |
| |
| static void test_has_help_option(void) |
| { |
| static const struct { |
| const char *params; |
| /* expected value of qemu_opt_has_help_opt() with implied=false */ |
| bool expect; |
| /* expected value of qemu_opt_has_help_opt() with implied=true */ |
| bool expect_implied; |
| } test[] = { |
| { "help", true, false }, |
| { "?", true, false }, |
| { "helpme", false, false }, |
| { "?me", false, false }, |
| { "a,help", true, true }, |
| { "a,?", true, true }, |
| { "a=0,help,b", true, true }, |
| { "a=0,?,b", true, true }, |
| { "help,b=1", true, false }, |
| { "?,b=1", true, false }, |
| { "a,b,,help", true, true }, |
| { "a,b,,?", true, true }, |
| }; |
| int i; |
| QemuOpts *opts; |
| |
| for (i = 0; i < ARRAY_SIZE(test); i++) { |
| g_assert_cmpint(has_help_option(test[i].params), |
| ==, test[i].expect); |
| opts = qemu_opts_parse(&opts_list_03, test[i].params, false, |
| &error_abort); |
| g_assert_cmpint(qemu_opt_has_help_opt(opts), |
| ==, test[i].expect); |
| qemu_opts_del(opts); |
| opts = qemu_opts_parse(&opts_list_03, test[i].params, true, |
| &error_abort); |
| g_assert_cmpint(qemu_opt_has_help_opt(opts), |
| ==, test[i].expect_implied); |
| qemu_opts_del(opts); |
| } |
| } |
| |
| static void append_verify_list_01(QemuOptDesc *desc, bool with_overlapping) |
| { |
| int i = 0; |
| |
| if (with_overlapping) { |
| g_assert_cmpstr(desc[i].name, ==, "str1"); |
| g_assert_cmpint(desc[i].type, ==, QEMU_OPT_STRING); |
| g_assert_cmpstr(desc[i].help, ==, |
| "Help texts are preserved in qemu_opts_append"); |
| g_assert_cmpstr(desc[i].def_value_str, ==, "default"); |
| i++; |
| |
| g_assert_cmpstr(desc[i].name, ==, "str2"); |
| g_assert_cmpint(desc[i].type, ==, QEMU_OPT_STRING); |
| g_assert_cmpstr(desc[i].help, ==, NULL); |
| g_assert_cmpstr(desc[i].def_value_str, ==, NULL); |
| i++; |
| } |
| |
| g_assert_cmpstr(desc[i].name, ==, "str3"); |
| g_assert_cmpint(desc[i].type, ==, QEMU_OPT_STRING); |
| g_assert_cmpstr(desc[i].help, ==, NULL); |
| g_assert_cmpstr(desc[i].def_value_str, ==, NULL); |
| i++; |
| |
| g_assert_cmpstr(desc[i].name, ==, "number1"); |
| g_assert_cmpint(desc[i].type, ==, QEMU_OPT_NUMBER); |
| g_assert_cmpstr(desc[i].help, ==, |
| "Having help texts only for some options is okay"); |
| g_assert_cmpstr(desc[i].def_value_str, ==, NULL); |
| i++; |
| |
| g_assert_cmpstr(desc[i].name, ==, "number2"); |
| g_assert_cmpint(desc[i].type, ==, QEMU_OPT_NUMBER); |
| g_assert_cmpstr(desc[i].help, ==, NULL); |
| g_assert_cmpstr(desc[i].def_value_str, ==, NULL); |
| i++; |
| |
| g_assert_cmpstr(desc[i].name, ==, NULL); |
| } |
| |
| static void append_verify_list_02(QemuOptDesc *desc) |
| { |
| int i = 0; |
| |
| g_assert_cmpstr(desc[i].name, ==, "str1"); |
| g_assert_cmpint(desc[i].type, ==, QEMU_OPT_STRING); |
| g_assert_cmpstr(desc[i].help, ==, NULL); |
| g_assert_cmpstr(desc[i].def_value_str, ==, NULL); |
| i++; |
| |
| g_assert_cmpstr(desc[i].name, ==, "str2"); |
| g_assert_cmpint(desc[i].type, ==, QEMU_OPT_STRING); |
| g_assert_cmpstr(desc[i].help, ==, NULL); |
| g_assert_cmpstr(desc[i].def_value_str, ==, NULL); |
| i++; |
| |
| g_assert_cmpstr(desc[i].name, ==, "bool1"); |
| g_assert_cmpint(desc[i].type, ==, QEMU_OPT_BOOL); |
| g_assert_cmpstr(desc[i].help, ==, NULL); |
| g_assert_cmpstr(desc[i].def_value_str, ==, NULL); |
| i++; |
| |
| g_assert_cmpstr(desc[i].name, ==, "bool2"); |
| g_assert_cmpint(desc[i].type, ==, QEMU_OPT_BOOL); |
| g_assert_cmpstr(desc[i].help, ==, NULL); |
| g_assert_cmpstr(desc[i].def_value_str, ==, NULL); |
| i++; |
| |
| g_assert_cmpstr(desc[i].name, ==, "size1"); |
| g_assert_cmpint(desc[i].type, ==, QEMU_OPT_SIZE); |
| g_assert_cmpstr(desc[i].help, ==, NULL); |
| g_assert_cmpstr(desc[i].def_value_str, ==, NULL); |
| i++; |
| |
| g_assert_cmpstr(desc[i].name, ==, "size2"); |
| g_assert_cmpint(desc[i].type, ==, QEMU_OPT_SIZE); |
| g_assert_cmpstr(desc[i].help, ==, NULL); |
| g_assert_cmpstr(desc[i].def_value_str, ==, NULL); |
| i++; |
| |
| g_assert_cmpstr(desc[i].name, ==, "size3"); |
| g_assert_cmpint(desc[i].type, ==, QEMU_OPT_SIZE); |
| g_assert_cmpstr(desc[i].help, ==, NULL); |
| g_assert_cmpstr(desc[i].def_value_str, ==, NULL); |
| } |
| |
| static void test_opts_append_to_null(void) |
| { |
| QemuOptsList *merged; |
| |
| merged = qemu_opts_append(NULL, &opts_list_01); |
| g_assert(merged != &opts_list_01); |
| |
| g_assert_cmpstr(merged->name, ==, NULL); |
| g_assert_cmpstr(merged->implied_opt_name, ==, NULL); |
| g_assert_false(merged->merge_lists); |
| |
| append_verify_list_01(merged->desc, true); |
| |
| qemu_opts_free(merged); |
| } |
| |
| static void test_opts_append(void) |
| { |
| QemuOptsList *first, *merged; |
| |
| first = qemu_opts_append(NULL, &opts_list_02); |
| merged = qemu_opts_append(first, &opts_list_01); |
| g_assert(first != &opts_list_02); |
| g_assert(merged != &opts_list_01); |
| |
| g_assert_cmpstr(merged->name, ==, NULL); |
| g_assert_cmpstr(merged->implied_opt_name, ==, NULL); |
| g_assert_false(merged->merge_lists); |
| |
| append_verify_list_02(&merged->desc[0]); |
| append_verify_list_01(&merged->desc[7], false); |
| |
| qemu_opts_free(merged); |
| } |
| |
| static void test_opts_to_qdict_basic(void) |
| { |
| QemuOpts *opts; |
| QDict *dict; |
| |
| opts = qemu_opts_parse(&opts_list_01, "str1=foo,str2=,str3=bar,number1=42", |
| false, &error_abort); |
| g_assert(opts != NULL); |
| |
| dict = qemu_opts_to_qdict(opts, NULL); |
| g_assert(dict != NULL); |
| |
| g_assert_cmpstr(qdict_get_str(dict, "str1"), ==, "foo"); |
| g_assert_cmpstr(qdict_get_str(dict, "str2"), ==, ""); |
| g_assert_cmpstr(qdict_get_str(dict, "str3"), ==, "bar"); |
| g_assert_cmpstr(qdict_get_str(dict, "number1"), ==, "42"); |
| g_assert_false(qdict_haskey(dict, "number2")); |
| |
| qobject_unref(dict); |
| qemu_opts_del(opts); |
| } |
| |
| static void test_opts_to_qdict_filtered(void) |
| { |
| QemuOptsList *first, *merged; |
| QemuOpts *opts; |
| QDict *dict; |
| |
| first = qemu_opts_append(NULL, &opts_list_02); |
| merged = qemu_opts_append(first, &opts_list_01); |
| |
| opts = qemu_opts_parse(merged, |
| "str1=foo,str2=,str3=bar,bool1=off,number1=42", |
| false, &error_abort); |
| g_assert(opts != NULL); |
| |
| /* Convert to QDict without deleting from opts */ |
| dict = qemu_opts_to_qdict_filtered(opts, NULL, &opts_list_01, false); |
| g_assert(dict != NULL); |
| g_assert_cmpstr(qdict_get_str(dict, "str1"), ==, "foo"); |
| g_assert_cmpstr(qdict_get_str(dict, "str2"), ==, ""); |
| g_assert_cmpstr(qdict_get_str(dict, "str3"), ==, "bar"); |
| g_assert_cmpstr(qdict_get_str(dict, "number1"), ==, "42"); |
| g_assert_false(qdict_haskey(dict, "number2")); |
| g_assert_false(qdict_haskey(dict, "bool1")); |
| qobject_unref(dict); |
| |
| dict = qemu_opts_to_qdict_filtered(opts, NULL, &opts_list_02, false); |
| g_assert(dict != NULL); |
| g_assert_cmpstr(qdict_get_str(dict, "str1"), ==, "foo"); |
| g_assert_cmpstr(qdict_get_str(dict, "str2"), ==, ""); |
| g_assert_cmpstr(qdict_get_str(dict, "bool1"), ==, "off"); |
| g_assert_false(qdict_haskey(dict, "str3")); |
| g_assert_false(qdict_haskey(dict, "number1")); |
| g_assert_false(qdict_haskey(dict, "number2")); |
| qobject_unref(dict); |
| |
| /* Now delete converted options from opts */ |
| dict = qemu_opts_to_qdict_filtered(opts, NULL, &opts_list_01, true); |
| g_assert(dict != NULL); |
| g_assert_cmpstr(qdict_get_str(dict, "str1"), ==, "foo"); |
| g_assert_cmpstr(qdict_get_str(dict, "str2"), ==, ""); |
| g_assert_cmpstr(qdict_get_str(dict, "str3"), ==, "bar"); |
| g_assert_cmpstr(qdict_get_str(dict, "number1"), ==, "42"); |
| g_assert_false(qdict_haskey(dict, "number2")); |
| g_assert_false(qdict_haskey(dict, "bool1")); |
| qobject_unref(dict); |
| |
| dict = qemu_opts_to_qdict_filtered(opts, NULL, &opts_list_02, true); |
| g_assert(dict != NULL); |
| g_assert_cmpstr(qdict_get_str(dict, "bool1"), ==, "off"); |
| g_assert_false(qdict_haskey(dict, "str1")); |
| g_assert_false(qdict_haskey(dict, "str2")); |
| g_assert_false(qdict_haskey(dict, "str3")); |
| g_assert_false(qdict_haskey(dict, "number1")); |
| g_assert_false(qdict_haskey(dict, "number2")); |
| qobject_unref(dict); |
| |
| g_assert_true(QTAILQ_EMPTY(&opts->head)); |
| |
| qemu_opts_del(opts); |
| qemu_opts_free(merged); |
| } |
| |
| static void test_opts_to_qdict_duplicates(void) |
| { |
| QemuOpts *opts; |
| QemuOpt *opt; |
| QDict *dict; |
| |
| opts = qemu_opts_parse(&opts_list_03, "foo=a,foo=b", false, &error_abort); |
| g_assert(opts != NULL); |
| |
| /* Verify that opts has two options with the same name */ |
| opt = QTAILQ_FIRST(&opts->head); |
| g_assert_cmpstr(opt->name, ==, "foo"); |
| g_assert_cmpstr(opt->str , ==, "a"); |
| |
| opt = QTAILQ_NEXT(opt, next); |
| g_assert_cmpstr(opt->name, ==, "foo"); |
| g_assert_cmpstr(opt->str , ==, "b"); |
| |
| opt = QTAILQ_NEXT(opt, next); |
| g_assert(opt == NULL); |
| |
| /* In the conversion to QDict, the last one wins */ |
| dict = qemu_opts_to_qdict(opts, NULL); |
| g_assert(dict != NULL); |
| g_assert_cmpstr(qdict_get_str(dict, "foo"), ==, "b"); |
| qobject_unref(dict); |
| |
| /* The last one still wins if entries are deleted, and both are deleted */ |
| dict = qemu_opts_to_qdict_filtered(opts, NULL, NULL, true); |
| g_assert(dict != NULL); |
| g_assert_cmpstr(qdict_get_str(dict, "foo"), ==, "b"); |
| qobject_unref(dict); |
| |
| g_assert_true(QTAILQ_EMPTY(&opts->head)); |
| |
| qemu_opts_del(opts); |
| } |
| |
| int main(int argc, char *argv[]) |
| { |
| register_opts(); |
| g_test_init(&argc, &argv, NULL); |
| g_test_add_func("/qemu-opts/find_unknown_opts", test_find_unknown_opts); |
| g_test_add_func("/qemu-opts/find_opts", test_qemu_find_opts); |
| g_test_add_func("/qemu-opts/opts_create", test_qemu_opts_create); |
| g_test_add_func("/qemu-opts/opt_get", test_qemu_opt_get); |
| g_test_add_func("/qemu-opts/opt_get_bool", test_qemu_opt_get_bool); |
| g_test_add_func("/qemu-opts/opt_get_number", test_qemu_opt_get_number); |
| g_test_add_func("/qemu-opts/opt_get_size", test_qemu_opt_get_size); |
| g_test_add_func("/qemu-opts/opt_unset", test_qemu_opt_unset); |
| g_test_add_func("/qemu-opts/opts_reset", test_qemu_opts_reset); |
| g_test_add_func("/qemu-opts/opts_set", test_qemu_opts_set); |
| g_test_add_func("/qemu-opts/opts_parse/general", test_opts_parse); |
| g_test_add_func("/qemu-opts/opts_parse/bool", test_opts_parse_bool); |
| g_test_add_func("/qemu-opts/opts_parse/number", test_opts_parse_number); |
| g_test_add_func("/qemu-opts/opts_parse/size", test_opts_parse_size); |
| g_test_add_func("/qemu-opts/has_help_option", test_has_help_option); |
| g_test_add_func("/qemu-opts/append_to_null", test_opts_append_to_null); |
| g_test_add_func("/qemu-opts/append", test_opts_append); |
| g_test_add_func("/qemu-opts/to_qdict/basic", test_opts_to_qdict_basic); |
| g_test_add_func("/qemu-opts/to_qdict/filtered", test_opts_to_qdict_filtered); |
| g_test_add_func("/qemu-opts/to_qdict/duplicates", test_opts_to_qdict_duplicates); |
| g_test_run(); |
| return 0; |
| } |