2014-05-20 01:53:55 +04:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2016-02-08 21:08:51 +03:00
|
|
|
#include "qemu/osdep.h"
|
2018-06-25 15:41:57 +03:00
|
|
|
#include "qemu/units.h"
|
2018-02-01 14:18:46 +03:00
|
|
|
#include "qemu/option.h"
|
2018-02-19 21:05:30 +03:00
|
|
|
#include "qemu/option_int.h"
|
include/qemu/osdep.h: Don't include qapi/error.h
Commit 57cb38b included qapi/error.h into qemu/osdep.h to get the
Error typedef. Since then, we've moved to include qemu/osdep.h
everywhere. Its file comment explains: "To avoid getting into
possible circular include dependencies, this file should not include
any other QEMU headers, with the exceptions of config-host.h,
compiler.h, os-posix.h and os-win32.h, all of which are doing a
similar job to this file and are under similar constraints."
qapi/error.h doesn't do a similar job, and it doesn't adhere to
similar constraints: it includes qapi-types.h. That's in excess of
100KiB of crap most .c files don't actually need.
Add the typedef to qemu/typedefs.h, and include that instead of
qapi/error.h. Include qapi/error.h in .c files that need it and don't
get it now. Include qapi-types.h in qom/object.h for uint16List.
Update scripts/clean-includes accordingly. Update it further to match
reality: replace config.h by config-target.h, add sysemu/os-posix.h,
sysemu/os-win32.h. Update the list of includes in the qemu/osdep.h
comment quoted above similarly.
This reduces the number of objects depending on qapi/error.h from "all
of them" to less than a third. Unfortunately, the number depending on
qapi-types.h shrinks only a little. More work is needed for that one.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
[Fix compilation without the spice devel packages. - Paolo]
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2016-03-14 11:01:28 +03:00
|
|
|
#include "qapi/error.h"
|
2018-02-01 14:18:39 +03:00
|
|
|
#include "qapi/qmp/qdict.h"
|
2014-05-20 01:53:55 +04:00
|
|
|
#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,
|
2018-02-19 19:28:08 +03:00
|
|
|
.help = "Help texts are preserved in qemu_opts_append",
|
|
|
|
.def_value_str = "default",
|
2014-05-20 01:53:55 +04:00
|
|
|
},{
|
|
|
|
.name = "str2",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
},{
|
|
|
|
.name = "str3",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
},{
|
|
|
|
.name = "number1",
|
|
|
|
.type = QEMU_OPT_NUMBER,
|
2018-02-19 19:28:08 +03:00
|
|
|
.help = "Having help texts only for some options is okay",
|
2017-02-21 23:13:45 +03:00
|
|
|
},{
|
|
|
|
.name = "number2",
|
|
|
|
.type = QEMU_OPT_NUMBER,
|
2014-05-20 01:53:55 +04:00
|
|
|
},
|
|
|
|
{ /* 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,
|
2017-02-21 23:13:45 +03:00
|
|
|
},{
|
|
|
|
.name = "str2",
|
|
|
|
.type = QEMU_OPT_STRING,
|
2014-05-20 01:53:55 +04:00
|
|
|
},{
|
|
|
|
.name = "bool1",
|
|
|
|
.type = QEMU_OPT_BOOL,
|
|
|
|
},{
|
2017-02-21 23:13:45 +03:00
|
|
|
.name = "bool2",
|
|
|
|
.type = QEMU_OPT_BOOL,
|
2014-05-20 01:53:55 +04:00
|
|
|
},{
|
|
|
|
.name = "size1",
|
|
|
|
.type = QEMU_OPT_SIZE,
|
2017-02-21 23:13:45 +03:00
|
|
|
},{
|
|
|
|
.name = "size2",
|
|
|
|
.type = QEMU_OPT_SIZE,
|
|
|
|
},{
|
|
|
|
.name = "size3",
|
|
|
|
.type = QEMU_OPT_SIZE,
|
2014-05-20 01:53:55 +04:00
|
|
|
},
|
|
|
|
{ /* end of list */ }
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2014-07-07 23:03:38 +04:00
|
|
|
static QemuOptsList opts_list_03 = {
|
2014-05-20 01:53:55 +04:00
|
|
|
.name = "opts_list_03",
|
2017-02-21 23:13:45 +03:00
|
|
|
.implied_opt_name = "implied",
|
2014-05-20 01:53:55 +04:00
|
|
|
.head = QTAILQ_HEAD_INITIALIZER(opts_list_03.head),
|
|
|
|
.desc = {
|
|
|
|
/* no elements => accept any params */
|
|
|
|
{ /* end of list */ }
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
qemu-option: restrict qemu_opts_set to merge-lists QemuOpts
qemu_opts_set is used to create default network backends and to
parse sugar options -kernel, -initrd, -append, -bios and -dtb.
These are very different uses:
I would *expect* a function named qemu_opts_set to set an option in a
merge-lists QemuOptsList, such as -kernel, and possibly to set an option
in a non-merge-lists QemuOptsList with non-NULL id, similar to -set.
However, it wouldn't *work* to use qemu_opts_set for the latter
because qemu_opts_set uses fail_if_exists==1. So, for non-merge-lists
QemuOptsList and non-NULL id, the semantics of qemu_opts_set (fail if the
(QemuOptsList, id) pair already exists) are debatable.
On the other hand, I would not expect qemu_opts_set to create a
non-merge-lists QemuOpts with a single option; which it does, though.
For this case of non-merge-lists QemuOptsList and NULL id, qemu_opts_set
hardly adds value over qemu_opts_parse. It does skip some parsing and
unescaping, but that's not needed when creating default network
backends.
So qemu_opts_set has warty behavior for non-merge-lists QemuOptsList
if id is non-NULL, and it's mostly pointless if id is NULL. My
solution to keeping the API as simple as possible is to limit
qemu_opts_set to merge-lists QemuOptsList. For them, it's useful (we
don't want comma-unescaping for -kernel) *and* has sane semantics.
Network backend creation is switched to qemu_opts_parse.
qemu_opts_set is now only used on merge-lists QemuOptsList... except
in the testcase, which is changed to use a merge-list QemuOptsList.
With this change we can also remove the id parameter. With the
parameter always NULL, we know that qemu_opts_create cannot fail
and can pass &error_abort to it.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2020-11-09 12:46:30 +03:00
|
|
|
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 */ }
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2014-05-20 01:53:55 +04:00
|
|
|
static void register_opts(void)
|
|
|
|
{
|
|
|
|
qemu_add_opts(&opts_list_01);
|
|
|
|
qemu_add_opts(&opts_list_02);
|
|
|
|
qemu_add_opts(&opts_list_03);
|
qemu-option: restrict qemu_opts_set to merge-lists QemuOpts
qemu_opts_set is used to create default network backends and to
parse sugar options -kernel, -initrd, -append, -bios and -dtb.
These are very different uses:
I would *expect* a function named qemu_opts_set to set an option in a
merge-lists QemuOptsList, such as -kernel, and possibly to set an option
in a non-merge-lists QemuOptsList with non-NULL id, similar to -set.
However, it wouldn't *work* to use qemu_opts_set for the latter
because qemu_opts_set uses fail_if_exists==1. So, for non-merge-lists
QemuOptsList and non-NULL id, the semantics of qemu_opts_set (fail if the
(QemuOptsList, id) pair already exists) are debatable.
On the other hand, I would not expect qemu_opts_set to create a
non-merge-lists QemuOpts with a single option; which it does, though.
For this case of non-merge-lists QemuOptsList and NULL id, qemu_opts_set
hardly adds value over qemu_opts_parse. It does skip some parsing and
unescaping, but that's not needed when creating default network
backends.
So qemu_opts_set has warty behavior for non-merge-lists QemuOptsList
if id is non-NULL, and it's mostly pointless if id is NULL. My
solution to keeping the API as simple as possible is to limit
qemu_opts_set to merge-lists QemuOptsList. For them, it's useful (we
don't want comma-unescaping for -kernel) *and* has sane semantics.
Network backend creation is switched to qemu_opts_parse.
qemu_opts_set is now only used on merge-lists QemuOptsList... except
in the testcase, which is changed to use a merge-list QemuOptsList.
With this change we can also remove the id parameter. With the
parameter always NULL, we know that qemu_opts_create cannot fail
and can pass &error_abort to it.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2020-11-09 12:46:30 +03:00
|
|
|
qemu_add_opts(&opts_list_04);
|
2014-05-20 01:53:55 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2017-02-17 23:15:54 +03:00
|
|
|
error_free_or_abort(&err);
|
2014-05-20 01:53:55 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
2015-02-12 19:52:20 +03:00
|
|
|
qemu_opt_set(opts, "str2", "value", &error_abort);
|
2014-05-20 01:53:55 +04:00
|
|
|
|
|
|
|
/* now we have set str2, should know about it */
|
|
|
|
opt = qemu_opt_get(opts, "str2");
|
|
|
|
g_assert_cmpstr(opt, ==, "value");
|
|
|
|
|
2015-02-12 19:52:20 +03:00
|
|
|
qemu_opt_set(opts, "str2", "value2", &error_abort);
|
2014-05-20 01:53:55 +04:00
|
|
|
|
|
|
|
/* 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);
|
|
|
|
|
2020-06-30 12:03:29 +03:00
|
|
|
qemu_opt_set_bool(opts, "bool1", true, &error_abort);
|
2014-05-20 01:53:55 +04:00
|
|
|
|
|
|
|
/* 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 */
|
2020-06-30 12:03:29 +03:00
|
|
|
qemu_opt_set_bool(opts, "bool1", false, &error_abort);
|
2014-05-20 01:53:55 +04:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
2020-06-30 12:03:29 +03:00
|
|
|
qemu_opt_set_number(opts, "number1", 10, &error_abort);
|
2014-05-20 01:53:55 +04:00
|
|
|
|
|
|
|
/* 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 */
|
2020-06-30 12:03:29 +03:00
|
|
|
qemu_opt_set_number(opts, "number1", 15, &error_abort);
|
2014-05-20 01:53:55 +04:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
2017-04-28 00:58:17 +03:00
|
|
|
qdict_put_str(dict, "size1", "10");
|
2014-05-20 01:53:55 +04:00
|
|
|
|
|
|
|
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 */
|
2017-04-28 00:58:17 +03:00
|
|
|
qdict_put_str(dict, "size1", "15");
|
2014-05-20 01:53:55 +04:00
|
|
|
|
|
|
|
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 */
|
QemuOpts: Wean off qerror_report_err()
qerror_report_err() is a transitional interface to help with
converting existing monitor commands to QMP. It should not be used
elsewhere.
The only remaining user in qemu-option.c is qemu_opts_parse(). Is it
used in QMP context? If not, we can simply replace
qerror_report_err() by error_report_err().
The uses in qemu-img.c, qemu-io.c, qemu-nbd.c and under tests/ are
clearly not in QMP context.
The uses in vl.c aren't either, because the only QMP command handlers
there are qmp_query_status() and qmp_query_machines(), and they don't
call it.
Remaining uses:
* drive_def(): Command line -drive and such, HMP drive_add and pci_add
* hmp_chardev_add(): HMP chardev-add
* monitor_parse_command(): HMP core
* tmp_config_parse(): Command line -tpmdev
* net_host_device_add(): HMP host_net_add
* net_client_parse(): Command line -net and -netdev
* qemu_global_option(): Command line -global
* vnc_parse_func(): Command line -display, -vnc, default display, HMP
change, QMP change. Bummer.
* qemu_pci_hot_add_nic(): HMP pci_add
* usb_net_init(): Command line -usbdevice, HMP usb_add
Propagate errors through qemu_opts_parse(). Create a convenience
function qemu_opts_parse_noisily() that passes errors to
error_report_err(). Switch all non-QMP users outside tests to it.
That leaves vnc_parse_func(). Propagate errors through it. Since I'm
touching it anyway, rename it to vnc_parse().
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Luiz Capitulino <lcapitulino@redhat.com>
2015-02-13 14:50:26 +03:00
|
|
|
opts = qemu_opts_parse(&opts_list_03, "key=value", false, NULL);
|
2014-05-20 01:53:55 +04:00
|
|
|
g_assert(opts != NULL);
|
|
|
|
|
|
|
|
/* check default/parsed value */
|
|
|
|
value = qemu_opt_get(opts, "key");
|
|
|
|
g_assert_cmpstr(value, ==, "value");
|
|
|
|
|
|
|
|
/* reset it to value2 */
|
2015-02-12 19:52:20 +03:00
|
|
|
qemu_opt_set(opts, "key", "value2", &error_abort);
|
2014-05-20 01:53:55 +04:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
2020-06-30 12:03:29 +03:00
|
|
|
qemu_opt_set_number(opts, "number1", 10, &error_abort);
|
2014-05-20 01:53:55 +04:00
|
|
|
|
|
|
|
/* 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);
|
|
|
|
}
|
|
|
|
|
2017-02-21 23:13:45 +03:00
|
|
|
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 */
|
|
|
|
|
2020-04-15 10:49:21 +03:00
|
|
|
/* Buggy ID recognition (fixed) */
|
2017-02-21 23:13:45 +03:00
|
|
|
opts = qemu_opts_parse(&opts_list_03, "x=,,id=bar", false, &error_abort);
|
|
|
|
g_assert_cmpuint(opts_count(opts), ==, 1);
|
2020-04-15 10:49:21 +03:00
|
|
|
g_assert(!qemu_opts_id(opts));
|
2017-02-21 23:13:45 +03:00
|
|
|
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);
|
|
|
|
|
2020-11-09 12:13:39 +03:00
|
|
|
/* Implied value (qemu_opts_parse warns but accepts it) */
|
2017-02-21 23:13:45 +03:00
|
|
|
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"), ==, "");
|
|
|
|
|
2017-03-01 00:26:47 +03:00
|
|
|
/* 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");
|
|
|
|
|
2017-02-21 23:13:45 +03:00
|
|
|
/* 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",
|
2017-02-21 23:13:53 +03:00
|
|
|
false, &err);
|
|
|
|
error_free_or_abort(&err);
|
|
|
|
g_assert(!opts);
|
2017-02-21 23:13:45 +03:00
|
|
|
|
|
|
|
/* Below lower limit */
|
|
|
|
opts = qemu_opts_parse(&opts_list_01, "number1=-18446744073709551616",
|
2017-02-21 23:13:53 +03:00
|
|
|
false, &err);
|
|
|
|
error_free_or_abort(&err);
|
|
|
|
g_assert(!opts);
|
2017-02-21 23:13:45 +03:00
|
|
|
|
|
|
|
/* 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);
|
2017-02-21 23:13:53 +03:00
|
|
|
error_free_or_abort(&err);
|
|
|
|
g_assert(!opts);
|
2017-02-21 23:13:45 +03:00
|
|
|
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);
|
|
|
|
|
utils: Improve qemu_strtosz() to have 64 bits of precision
We have multiple clients of qemu_strtosz (qemu-io, the opts visitor,
the keyval visitor), and it gets annoying that edge-case testing is
impacted by implicit rounding to 53 bits of precision due to parsing
with strtod(). As an example posted by Rich Jones:
$ nbdkit memory $(( 2**63 - 2**30 )) --run \
'build/qemu-io -f raw "$uri" -c "w -P 3 $(( 2**63 - 2**30 - 512 )) 512" '
write failed: Input/output error
because 9223372035781033472 got rounded to 0x7fffffffc0000000 which is
out of bounds.
It is also worth noting that our existing parser, by virtue of using
strtod(), accepts decimal AND hex numbers, even though test-cutils
previously lacked any coverage of the latter until the previous patch.
We do have existing clients that expect a hex parse to work (for
example, iotest 33 using qemu-io -c "write -P 0xa 0x200 0x400"), but
strtod() parses "08" as 8 rather than as an invalid octal number, so
we know there are no clients that depend on octal. Our use of
strtod() also means that "0x1.8k" would actually parse as 1536 (the
fraction is 8/16), rather than 1843 (if the fraction were 8/10); but
as this was not covered in the testsuite, I have no qualms forbidding
hex fractions as invalid, so this patch declares that the use of
fractions is only supported with decimal input, and enhances the
testsuite to document that.
Our previous use of strtod() meant that -1 parsed as a negative; now
that we parse with strtoull(), negative values can wrap around modulo
2^64, so we have to explicitly check whether the user passed in a '-';
and make it consistent to also reject '-0'. This has the minor effect
of treating negative values as EINVAL (with no change to endptr)
rather than ERANGE (with endptr advanced to what was parsed), visible
in the updated iotest output.
We also had no testsuite coverage of "1.1e0k", which happened to parse
under strtod() but is unlikely to occur in practice; as long as we are
making things more robust, it is easy enough to reject the use of
exponents in a strtod parse.
The fix is done by breaking the parse into an integer prefix (no loss
in precision), rejecting negative values (since we can no longer rely
on strtod() to do that), determining if a decimal or hexadecimal parse
was intended (with the new restriction that a fractional hex parse is
not allowed), and where appropriate, using a floating point fractional
parse (where we also scan to reject use of exponents in the fraction).
The bulk of the patch is then updates to the testsuite to match our
new precision, as well as adding new cases we reject (whether they
were rejected or inadvertently accepted before).
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <20210211204438.1184395-3-eblake@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
2021-02-11 23:44:36 +03:00
|
|
|
/* Note: full 64 bits of precision */
|
2017-02-21 23:13:45 +03:00
|
|
|
|
utils: Improve qemu_strtosz() to have 64 bits of precision
We have multiple clients of qemu_strtosz (qemu-io, the opts visitor,
the keyval visitor), and it gets annoying that edge-case testing is
impacted by implicit rounding to 53 bits of precision due to parsing
with strtod(). As an example posted by Rich Jones:
$ nbdkit memory $(( 2**63 - 2**30 )) --run \
'build/qemu-io -f raw "$uri" -c "w -P 3 $(( 2**63 - 2**30 - 512 )) 512" '
write failed: Input/output error
because 9223372035781033472 got rounded to 0x7fffffffc0000000 which is
out of bounds.
It is also worth noting that our existing parser, by virtue of using
strtod(), accepts decimal AND hex numbers, even though test-cutils
previously lacked any coverage of the latter until the previous patch.
We do have existing clients that expect a hex parse to work (for
example, iotest 33 using qemu-io -c "write -P 0xa 0x200 0x400"), but
strtod() parses "08" as 8 rather than as an invalid octal number, so
we know there are no clients that depend on octal. Our use of
strtod() also means that "0x1.8k" would actually parse as 1536 (the
fraction is 8/16), rather than 1843 (if the fraction were 8/10); but
as this was not covered in the testsuite, I have no qualms forbidding
hex fractions as invalid, so this patch declares that the use of
fractions is only supported with decimal input, and enhances the
testsuite to document that.
Our previous use of strtod() meant that -1 parsed as a negative; now
that we parse with strtoull(), negative values can wrap around modulo
2^64, so we have to explicitly check whether the user passed in a '-';
and make it consistent to also reject '-0'. This has the minor effect
of treating negative values as EINVAL (with no change to endptr)
rather than ERANGE (with endptr advanced to what was parsed), visible
in the updated iotest output.
We also had no testsuite coverage of "1.1e0k", which happened to parse
under strtod() but is unlikely to occur in practice; as long as we are
making things more robust, it is easy enough to reject the use of
exponents in a strtod parse.
The fix is done by breaking the parse into an integer prefix (no loss
in precision), rejecting negative values (since we can no longer rely
on strtod() to do that), determining if a decimal or hexadecimal parse
was intended (with the new restriction that a fractional hex parse is
not allowed), and where appropriate, using a floating point fractional
parse (where we also scan to reject use of exponents in the fraction).
The bulk of the patch is then updates to the testsuite to match our
new precision, as well as adding new cases we reject (whether they
were rejected or inadvertently accepted before).
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <20210211204438.1184395-3-eblake@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
2021-02-11 23:44:36 +03:00
|
|
|
/* Around double limit of precision: 2^53-1, 2^53, 2^53+1 */
|
2017-02-21 23:13:45 +03:00
|
|
|
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),
|
utils: Improve qemu_strtosz() to have 64 bits of precision
We have multiple clients of qemu_strtosz (qemu-io, the opts visitor,
the keyval visitor), and it gets annoying that edge-case testing is
impacted by implicit rounding to 53 bits of precision due to parsing
with strtod(). As an example posted by Rich Jones:
$ nbdkit memory $(( 2**63 - 2**30 )) --run \
'build/qemu-io -f raw "$uri" -c "w -P 3 $(( 2**63 - 2**30 - 512 )) 512" '
write failed: Input/output error
because 9223372035781033472 got rounded to 0x7fffffffc0000000 which is
out of bounds.
It is also worth noting that our existing parser, by virtue of using
strtod(), accepts decimal AND hex numbers, even though test-cutils
previously lacked any coverage of the latter until the previous patch.
We do have existing clients that expect a hex parse to work (for
example, iotest 33 using qemu-io -c "write -P 0xa 0x200 0x400"), but
strtod() parses "08" as 8 rather than as an invalid octal number, so
we know there are no clients that depend on octal. Our use of
strtod() also means that "0x1.8k" would actually parse as 1536 (the
fraction is 8/16), rather than 1843 (if the fraction were 8/10); but
as this was not covered in the testsuite, I have no qualms forbidding
hex fractions as invalid, so this patch declares that the use of
fractions is only supported with decimal input, and enhances the
testsuite to document that.
Our previous use of strtod() meant that -1 parsed as a negative; now
that we parse with strtoull(), negative values can wrap around modulo
2^64, so we have to explicitly check whether the user passed in a '-';
and make it consistent to also reject '-0'. This has the minor effect
of treating negative values as EINVAL (with no change to endptr)
rather than ERANGE (with endptr advanced to what was parsed), visible
in the updated iotest output.
We also had no testsuite coverage of "1.1e0k", which happened to parse
under strtod() but is unlikely to occur in practice; as long as we are
making things more robust, it is easy enough to reject the use of
exponents in a strtod parse.
The fix is done by breaking the parse into an integer prefix (no loss
in precision), rejecting negative values (since we can no longer rely
on strtod() to do that), determining if a decimal or hexadecimal parse
was intended (with the new restriction that a fractional hex parse is
not allowed), and where appropriate, using a floating point fractional
parse (where we also scan to reject use of exponents in the fraction).
The bulk of the patch is then updates to the testsuite to match our
new precision, as well as adding new cases we reject (whether they
were rejected or inadvertently accepted before).
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <20210211204438.1184395-3-eblake@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
2021-02-11 23:44:36 +03:00
|
|
|
==, 0x20000000000001);
|
2017-02-21 23:13:45 +03:00
|
|
|
|
utils: Improve qemu_strtosz() to have 64 bits of precision
We have multiple clients of qemu_strtosz (qemu-io, the opts visitor,
the keyval visitor), and it gets annoying that edge-case testing is
impacted by implicit rounding to 53 bits of precision due to parsing
with strtod(). As an example posted by Rich Jones:
$ nbdkit memory $(( 2**63 - 2**30 )) --run \
'build/qemu-io -f raw "$uri" -c "w -P 3 $(( 2**63 - 2**30 - 512 )) 512" '
write failed: Input/output error
because 9223372035781033472 got rounded to 0x7fffffffc0000000 which is
out of bounds.
It is also worth noting that our existing parser, by virtue of using
strtod(), accepts decimal AND hex numbers, even though test-cutils
previously lacked any coverage of the latter until the previous patch.
We do have existing clients that expect a hex parse to work (for
example, iotest 33 using qemu-io -c "write -P 0xa 0x200 0x400"), but
strtod() parses "08" as 8 rather than as an invalid octal number, so
we know there are no clients that depend on octal. Our use of
strtod() also means that "0x1.8k" would actually parse as 1536 (the
fraction is 8/16), rather than 1843 (if the fraction were 8/10); but
as this was not covered in the testsuite, I have no qualms forbidding
hex fractions as invalid, so this patch declares that the use of
fractions is only supported with decimal input, and enhances the
testsuite to document that.
Our previous use of strtod() meant that -1 parsed as a negative; now
that we parse with strtoull(), negative values can wrap around modulo
2^64, so we have to explicitly check whether the user passed in a '-';
and make it consistent to also reject '-0'. This has the minor effect
of treating negative values as EINVAL (with no change to endptr)
rather than ERANGE (with endptr advanced to what was parsed), visible
in the updated iotest output.
We also had no testsuite coverage of "1.1e0k", which happened to parse
under strtod() but is unlikely to occur in practice; as long as we are
making things more robust, it is easy enough to reject the use of
exponents in a strtod parse.
The fix is done by breaking the parse into an integer prefix (no loss
in precision), rejecting negative values (since we can no longer rely
on strtod() to do that), determining if a decimal or hexadecimal parse
was intended (with the new restriction that a fractional hex parse is
not allowed), and where appropriate, using a floating point fractional
parse (where we also scan to reject use of exponents in the fraction).
The bulk of the patch is then updates to the testsuite to match our
new precision, as well as adding new cases we reject (whether they
were rejected or inadvertently accepted before).
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <20210211204438.1184395-3-eblake@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
2021-02-11 23:44:36 +03:00
|
|
|
/* Close to signed int limit: 2^63-1, 2^63, 2^63+1 */
|
2017-02-21 23:13:45 +03:00
|
|
|
opts = qemu_opts_parse(&opts_list_02,
|
utils: Improve qemu_strtosz() to have 64 bits of precision
We have multiple clients of qemu_strtosz (qemu-io, the opts visitor,
the keyval visitor), and it gets annoying that edge-case testing is
impacted by implicit rounding to 53 bits of precision due to parsing
with strtod(). As an example posted by Rich Jones:
$ nbdkit memory $(( 2**63 - 2**30 )) --run \
'build/qemu-io -f raw "$uri" -c "w -P 3 $(( 2**63 - 2**30 - 512 )) 512" '
write failed: Input/output error
because 9223372035781033472 got rounded to 0x7fffffffc0000000 which is
out of bounds.
It is also worth noting that our existing parser, by virtue of using
strtod(), accepts decimal AND hex numbers, even though test-cutils
previously lacked any coverage of the latter until the previous patch.
We do have existing clients that expect a hex parse to work (for
example, iotest 33 using qemu-io -c "write -P 0xa 0x200 0x400"), but
strtod() parses "08" as 8 rather than as an invalid octal number, so
we know there are no clients that depend on octal. Our use of
strtod() also means that "0x1.8k" would actually parse as 1536 (the
fraction is 8/16), rather than 1843 (if the fraction were 8/10); but
as this was not covered in the testsuite, I have no qualms forbidding
hex fractions as invalid, so this patch declares that the use of
fractions is only supported with decimal input, and enhances the
testsuite to document that.
Our previous use of strtod() meant that -1 parsed as a negative; now
that we parse with strtoull(), negative values can wrap around modulo
2^64, so we have to explicitly check whether the user passed in a '-';
and make it consistent to also reject '-0'. This has the minor effect
of treating negative values as EINVAL (with no change to endptr)
rather than ERANGE (with endptr advanced to what was parsed), visible
in the updated iotest output.
We also had no testsuite coverage of "1.1e0k", which happened to parse
under strtod() but is unlikely to occur in practice; as long as we are
making things more robust, it is easy enough to reject the use of
exponents in a strtod parse.
The fix is done by breaking the parse into an integer prefix (no loss
in precision), rejecting negative values (since we can no longer rely
on strtod() to do that), determining if a decimal or hexadecimal parse
was intended (with the new restriction that a fractional hex parse is
not allowed), and where appropriate, using a floating point fractional
parse (where we also scan to reject use of exponents in the fraction).
The bulk of the patch is then updates to the testsuite to match our
new precision, as well as adding new cases we reject (whether they
were rejected or inadvertently accepted before).
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <20210211204438.1184395-3-eblake@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
2021-02-11 23:44:36 +03:00
|
|
|
"size1=9223372036854775807," /* 7fffffffffffffff */
|
|
|
|
"size2=9223372036854775808," /* 8000000000000000 */
|
|
|
|
"size3=9223372036854775809", /* 8000000000000001 */
|
2017-02-21 23:13:45 +03:00
|
|
|
false, &error_abort);
|
utils: Improve qemu_strtosz() to have 64 bits of precision
We have multiple clients of qemu_strtosz (qemu-io, the opts visitor,
the keyval visitor), and it gets annoying that edge-case testing is
impacted by implicit rounding to 53 bits of precision due to parsing
with strtod(). As an example posted by Rich Jones:
$ nbdkit memory $(( 2**63 - 2**30 )) --run \
'build/qemu-io -f raw "$uri" -c "w -P 3 $(( 2**63 - 2**30 - 512 )) 512" '
write failed: Input/output error
because 9223372035781033472 got rounded to 0x7fffffffc0000000 which is
out of bounds.
It is also worth noting that our existing parser, by virtue of using
strtod(), accepts decimal AND hex numbers, even though test-cutils
previously lacked any coverage of the latter until the previous patch.
We do have existing clients that expect a hex parse to work (for
example, iotest 33 using qemu-io -c "write -P 0xa 0x200 0x400"), but
strtod() parses "08" as 8 rather than as an invalid octal number, so
we know there are no clients that depend on octal. Our use of
strtod() also means that "0x1.8k" would actually parse as 1536 (the
fraction is 8/16), rather than 1843 (if the fraction were 8/10); but
as this was not covered in the testsuite, I have no qualms forbidding
hex fractions as invalid, so this patch declares that the use of
fractions is only supported with decimal input, and enhances the
testsuite to document that.
Our previous use of strtod() meant that -1 parsed as a negative; now
that we parse with strtoull(), negative values can wrap around modulo
2^64, so we have to explicitly check whether the user passed in a '-';
and make it consistent to also reject '-0'. This has the minor effect
of treating negative values as EINVAL (with no change to endptr)
rather than ERANGE (with endptr advanced to what was parsed), visible
in the updated iotest output.
We also had no testsuite coverage of "1.1e0k", which happened to parse
under strtod() but is unlikely to occur in practice; as long as we are
making things more robust, it is easy enough to reject the use of
exponents in a strtod parse.
The fix is done by breaking the parse into an integer prefix (no loss
in precision), rejecting negative values (since we can no longer rely
on strtod() to do that), determining if a decimal or hexadecimal parse
was intended (with the new restriction that a fractional hex parse is
not allowed), and where appropriate, using a floating point fractional
parse (where we also scan to reject use of exponents in the fraction).
The bulk of the patch is then updates to the testsuite to match our
new precision, as well as adding new cases we reject (whether they
were rejected or inadvertently accepted before).
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <20210211204438.1184395-3-eblake@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
2021-02-11 23:44:36 +03:00
|
|
|
g_assert_cmpuint(opts_count(opts), ==, 3);
|
2017-02-21 23:13:45 +03:00
|
|
|
g_assert_cmphex(qemu_opt_get_size(opts, "size1", 1),
|
utils: Improve qemu_strtosz() to have 64 bits of precision
We have multiple clients of qemu_strtosz (qemu-io, the opts visitor,
the keyval visitor), and it gets annoying that edge-case testing is
impacted by implicit rounding to 53 bits of precision due to parsing
with strtod(). As an example posted by Rich Jones:
$ nbdkit memory $(( 2**63 - 2**30 )) --run \
'build/qemu-io -f raw "$uri" -c "w -P 3 $(( 2**63 - 2**30 - 512 )) 512" '
write failed: Input/output error
because 9223372035781033472 got rounded to 0x7fffffffc0000000 which is
out of bounds.
It is also worth noting that our existing parser, by virtue of using
strtod(), accepts decimal AND hex numbers, even though test-cutils
previously lacked any coverage of the latter until the previous patch.
We do have existing clients that expect a hex parse to work (for
example, iotest 33 using qemu-io -c "write -P 0xa 0x200 0x400"), but
strtod() parses "08" as 8 rather than as an invalid octal number, so
we know there are no clients that depend on octal. Our use of
strtod() also means that "0x1.8k" would actually parse as 1536 (the
fraction is 8/16), rather than 1843 (if the fraction were 8/10); but
as this was not covered in the testsuite, I have no qualms forbidding
hex fractions as invalid, so this patch declares that the use of
fractions is only supported with decimal input, and enhances the
testsuite to document that.
Our previous use of strtod() meant that -1 parsed as a negative; now
that we parse with strtoull(), negative values can wrap around modulo
2^64, so we have to explicitly check whether the user passed in a '-';
and make it consistent to also reject '-0'. This has the minor effect
of treating negative values as EINVAL (with no change to endptr)
rather than ERANGE (with endptr advanced to what was parsed), visible
in the updated iotest output.
We also had no testsuite coverage of "1.1e0k", which happened to parse
under strtod() but is unlikely to occur in practice; as long as we are
making things more robust, it is easy enough to reject the use of
exponents in a strtod parse.
The fix is done by breaking the parse into an integer prefix (no loss
in precision), rejecting negative values (since we can no longer rely
on strtod() to do that), determining if a decimal or hexadecimal parse
was intended (with the new restriction that a fractional hex parse is
not allowed), and where appropriate, using a floating point fractional
parse (where we also scan to reject use of exponents in the fraction).
The bulk of the patch is then updates to the testsuite to match our
new precision, as well as adding new cases we reject (whether they
were rejected or inadvertently accepted before).
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <20210211204438.1184395-3-eblake@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
2021-02-11 23:44:36 +03:00
|
|
|
==, 0x7fffffffffffffff);
|
2017-02-21 23:13:45 +03:00
|
|
|
g_assert_cmphex(qemu_opt_get_size(opts, "size2", 1),
|
utils: Improve qemu_strtosz() to have 64 bits of precision
We have multiple clients of qemu_strtosz (qemu-io, the opts visitor,
the keyval visitor), and it gets annoying that edge-case testing is
impacted by implicit rounding to 53 bits of precision due to parsing
with strtod(). As an example posted by Rich Jones:
$ nbdkit memory $(( 2**63 - 2**30 )) --run \
'build/qemu-io -f raw "$uri" -c "w -P 3 $(( 2**63 - 2**30 - 512 )) 512" '
write failed: Input/output error
because 9223372035781033472 got rounded to 0x7fffffffc0000000 which is
out of bounds.
It is also worth noting that our existing parser, by virtue of using
strtod(), accepts decimal AND hex numbers, even though test-cutils
previously lacked any coverage of the latter until the previous patch.
We do have existing clients that expect a hex parse to work (for
example, iotest 33 using qemu-io -c "write -P 0xa 0x200 0x400"), but
strtod() parses "08" as 8 rather than as an invalid octal number, so
we know there are no clients that depend on octal. Our use of
strtod() also means that "0x1.8k" would actually parse as 1536 (the
fraction is 8/16), rather than 1843 (if the fraction were 8/10); but
as this was not covered in the testsuite, I have no qualms forbidding
hex fractions as invalid, so this patch declares that the use of
fractions is only supported with decimal input, and enhances the
testsuite to document that.
Our previous use of strtod() meant that -1 parsed as a negative; now
that we parse with strtoull(), negative values can wrap around modulo
2^64, so we have to explicitly check whether the user passed in a '-';
and make it consistent to also reject '-0'. This has the minor effect
of treating negative values as EINVAL (with no change to endptr)
rather than ERANGE (with endptr advanced to what was parsed), visible
in the updated iotest output.
We also had no testsuite coverage of "1.1e0k", which happened to parse
under strtod() but is unlikely to occur in practice; as long as we are
making things more robust, it is easy enough to reject the use of
exponents in a strtod parse.
The fix is done by breaking the parse into an integer prefix (no loss
in precision), rejecting negative values (since we can no longer rely
on strtod() to do that), determining if a decimal or hexadecimal parse
was intended (with the new restriction that a fractional hex parse is
not allowed), and where appropriate, using a floating point fractional
parse (where we also scan to reject use of exponents in the fraction).
The bulk of the patch is then updates to the testsuite to match our
new precision, as well as adding new cases we reject (whether they
were rejected or inadvertently accepted before).
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <20210211204438.1184395-3-eblake@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
2021-02-11 23:44:36 +03:00
|
|
|
==, 0x8000000000000000);
|
|
|
|
g_assert_cmphex(qemu_opt_get_size(opts, "size3", 1),
|
|
|
|
==, 0x8000000000000001);
|
2017-02-21 23:13:45 +03:00
|
|
|
|
|
|
|
/* 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),
|
utils: Improve qemu_strtosz() to have 64 bits of precision
We have multiple clients of qemu_strtosz (qemu-io, the opts visitor,
the keyval visitor), and it gets annoying that edge-case testing is
impacted by implicit rounding to 53 bits of precision due to parsing
with strtod(). As an example posted by Rich Jones:
$ nbdkit memory $(( 2**63 - 2**30 )) --run \
'build/qemu-io -f raw "$uri" -c "w -P 3 $(( 2**63 - 2**30 - 512 )) 512" '
write failed: Input/output error
because 9223372035781033472 got rounded to 0x7fffffffc0000000 which is
out of bounds.
It is also worth noting that our existing parser, by virtue of using
strtod(), accepts decimal AND hex numbers, even though test-cutils
previously lacked any coverage of the latter until the previous patch.
We do have existing clients that expect a hex parse to work (for
example, iotest 33 using qemu-io -c "write -P 0xa 0x200 0x400"), but
strtod() parses "08" as 8 rather than as an invalid octal number, so
we know there are no clients that depend on octal. Our use of
strtod() also means that "0x1.8k" would actually parse as 1536 (the
fraction is 8/16), rather than 1843 (if the fraction were 8/10); but
as this was not covered in the testsuite, I have no qualms forbidding
hex fractions as invalid, so this patch declares that the use of
fractions is only supported with decimal input, and enhances the
testsuite to document that.
Our previous use of strtod() meant that -1 parsed as a negative; now
that we parse with strtoull(), negative values can wrap around modulo
2^64, so we have to explicitly check whether the user passed in a '-';
and make it consistent to also reject '-0'. This has the minor effect
of treating negative values as EINVAL (with no change to endptr)
rather than ERANGE (with endptr advanced to what was parsed), visible
in the updated iotest output.
We also had no testsuite coverage of "1.1e0k", which happened to parse
under strtod() but is unlikely to occur in practice; as long as we are
making things more robust, it is easy enough to reject the use of
exponents in a strtod parse.
The fix is done by breaking the parse into an integer prefix (no loss
in precision), rejecting negative values (since we can no longer rely
on strtod() to do that), determining if a decimal or hexadecimal parse
was intended (with the new restriction that a fractional hex parse is
not allowed), and where appropriate, using a floating point fractional
parse (where we also scan to reject use of exponents in the fraction).
The bulk of the patch is then updates to the testsuite to match our
new precision, as well as adding new cases we reject (whether they
were rejected or inadvertently accepted before).
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <20210211204438.1184395-3-eblake@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
2021-02-11 23:44:36 +03:00
|
|
|
==, 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);
|
2017-02-21 23:13:45 +03:00
|
|
|
|
|
|
|
/* 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,
|
utils: Improve qemu_strtosz() to have 64 bits of precision
We have multiple clients of qemu_strtosz (qemu-io, the opts visitor,
the keyval visitor), and it gets annoying that edge-case testing is
impacted by implicit rounding to 53 bits of precision due to parsing
with strtod(). As an example posted by Rich Jones:
$ nbdkit memory $(( 2**63 - 2**30 )) --run \
'build/qemu-io -f raw "$uri" -c "w -P 3 $(( 2**63 - 2**30 - 512 )) 512" '
write failed: Input/output error
because 9223372035781033472 got rounded to 0x7fffffffc0000000 which is
out of bounds.
It is also worth noting that our existing parser, by virtue of using
strtod(), accepts decimal AND hex numbers, even though test-cutils
previously lacked any coverage of the latter until the previous patch.
We do have existing clients that expect a hex parse to work (for
example, iotest 33 using qemu-io -c "write -P 0xa 0x200 0x400"), but
strtod() parses "08" as 8 rather than as an invalid octal number, so
we know there are no clients that depend on octal. Our use of
strtod() also means that "0x1.8k" would actually parse as 1536 (the
fraction is 8/16), rather than 1843 (if the fraction were 8/10); but
as this was not covered in the testsuite, I have no qualms forbidding
hex fractions as invalid, so this patch declares that the use of
fractions is only supported with decimal input, and enhances the
testsuite to document that.
Our previous use of strtod() meant that -1 parsed as a negative; now
that we parse with strtoull(), negative values can wrap around modulo
2^64, so we have to explicitly check whether the user passed in a '-';
and make it consistent to also reject '-0'. This has the minor effect
of treating negative values as EINVAL (with no change to endptr)
rather than ERANGE (with endptr advanced to what was parsed), visible
in the updated iotest output.
We also had no testsuite coverage of "1.1e0k", which happened to parse
under strtod() but is unlikely to occur in practice; as long as we are
making things more robust, it is easy enough to reject the use of
exponents in a strtod parse.
The fix is done by breaking the parse into an integer prefix (no loss
in precision), rejecting negative values (since we can no longer rely
on strtod() to do that), determining if a decimal or hexadecimal parse
was intended (with the new restriction that a fractional hex parse is
not allowed), and where appropriate, using a floating point fractional
parse (where we also scan to reject use of exponents in the fraction).
The bulk of the patch is then updates to the testsuite to match our
new precision, as well as adding new cases we reject (whether they
were rejected or inadvertently accepted before).
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <20210211204438.1184395-3-eblake@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
2021-02-11 23:44:36 +03:00
|
|
|
"size1=18446744073709551616", /* 2^64 */
|
2017-02-21 23:14:08 +03:00
|
|
|
false, &err);
|
|
|
|
error_free_or_abort(&err);
|
|
|
|
g_assert(!opts);
|
2017-02-21 23:13:45 +03:00
|
|
|
|
|
|
|
/* 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);
|
2018-06-25 15:41:57 +03:00
|
|
|
g_assert_cmphex(qemu_opt_get_size(opts, "size3", 0), ==, 2 * MiB);
|
2017-02-21 23:13:45 +03:00
|
|
|
opts = qemu_opts_parse(&opts_list_02, "size1=0.1G,size2=16777215T",
|
|
|
|
false, &error_abort);
|
|
|
|
g_assert_cmpuint(opts_count(opts), ==, 2);
|
2018-06-25 15:41:57 +03:00
|
|
|
g_assert_cmphex(qemu_opt_get_size(opts, "size1", 0), ==, GiB / 10);
|
|
|
|
g_assert_cmphex(qemu_opt_get_size(opts, "size2", 0), ==, 16777215ULL * TiB);
|
2017-02-21 23:13:45 +03:00
|
|
|
|
|
|
|
/* Beyond limit with suffix */
|
|
|
|
opts = qemu_opts_parse(&opts_list_02, "size1=16777216T",
|
2017-02-21 23:14:08 +03:00
|
|
|
false, &err);
|
|
|
|
error_free_or_abort(&err);
|
|
|
|
g_assert(!opts);
|
2017-02-21 23:13:45 +03:00
|
|
|
|
|
|
|
/* Trailing crap */
|
|
|
|
opts = qemu_opts_parse(&opts_list_02, "size1=16E", false, &err);
|
|
|
|
error_free_or_abort(&err);
|
|
|
|
g_assert(!opts);
|
2017-02-21 23:14:08 +03:00
|
|
|
opts = qemu_opts_parse(&opts_list_02, "size1=16Gi", false, &err);
|
|
|
|
error_free_or_abort(&err);
|
|
|
|
g_assert(!opts);
|
2017-02-21 23:13:45 +03:00
|
|
|
|
|
|
|
qemu_opts_reset(&opts_list_02);
|
|
|
|
}
|
|
|
|
|
2020-04-15 10:49:19 +03:00
|
|
|
static void test_has_help_option(void)
|
|
|
|
{
|
|
|
|
static const struct {
|
|
|
|
const char *params;
|
|
|
|
/* expected value of qemu_opt_has_help_opt() with implied=false */
|
2020-04-15 10:49:23 +03:00
|
|
|
bool expect;
|
2020-04-15 10:49:19 +03:00
|
|
|
/* expected value of qemu_opt_has_help_opt() with implied=true */
|
2020-04-15 10:49:23 +03:00
|
|
|
bool expect_implied;
|
2020-04-15 10:49:19 +03:00
|
|
|
} test[] = {
|
2020-04-15 10:49:23 +03:00
|
|
|
{ "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 },
|
2020-04-15 10:49:19 +03:00
|
|
|
};
|
|
|
|
int i;
|
|
|
|
QemuOpts *opts;
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(test); i++) {
|
|
|
|
g_assert_cmpint(has_help_option(test[i].params),
|
2020-04-15 10:49:23 +03:00
|
|
|
==, test[i].expect);
|
2020-04-15 10:49:19 +03:00
|
|
|
opts = qemu_opts_parse(&opts_list_03, test[i].params, false,
|
|
|
|
&error_abort);
|
|
|
|
g_assert_cmpint(qemu_opt_has_help_opt(opts),
|
2020-04-15 10:49:23 +03:00
|
|
|
==, test[i].expect);
|
2020-04-15 10:49:19 +03:00
|
|
|
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),
|
2020-04-15 10:49:23 +03:00
|
|
|
==, test[i].expect_implied);
|
2020-04-15 10:49:19 +03:00
|
|
|
qemu_opts_del(opts);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-19 19:28:08 +03:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2018-02-19 21:05:30 +03:00
|
|
|
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"));
|
|
|
|
|
2018-04-19 18:01:43 +03:00
|
|
|
qobject_unref(dict);
|
2018-02-19 21:05:30 +03:00
|
|
|
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"));
|
2018-04-19 18:01:43 +03:00
|
|
|
qobject_unref(dict);
|
2018-02-19 21:05:30 +03:00
|
|
|
|
|
|
|
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"));
|
2018-04-19 18:01:43 +03:00
|
|
|
qobject_unref(dict);
|
2018-02-19 21:05:30 +03:00
|
|
|
|
|
|
|
/* 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"));
|
2018-04-19 18:01:43 +03:00
|
|
|
qobject_unref(dict);
|
2018-02-19 21:05:30 +03:00
|
|
|
|
|
|
|
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"));
|
2018-04-19 18:01:43 +03:00
|
|
|
qobject_unref(dict);
|
2018-02-19 21:05:30 +03:00
|
|
|
|
|
|
|
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");
|
2018-04-19 18:01:43 +03:00
|
|
|
qobject_unref(dict);
|
2018-02-19 21:05:30 +03:00
|
|
|
|
|
|
|
/* 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");
|
2018-04-19 18:01:43 +03:00
|
|
|
qobject_unref(dict);
|
2018-02-19 21:05:30 +03:00
|
|
|
|
|
|
|
g_assert_true(QTAILQ_EMPTY(&opts->head));
|
|
|
|
|
|
|
|
qemu_opts_del(opts);
|
|
|
|
}
|
2018-02-19 19:28:08 +03:00
|
|
|
|
2014-05-20 01:53:55 +04:00
|
|
|
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);
|
2017-02-21 23:13:45 +03:00
|
|
|
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);
|
2020-04-15 10:49:19 +03:00
|
|
|
g_test_add_func("/qemu-opts/has_help_option", test_has_help_option);
|
2018-02-19 19:28:08 +03:00
|
|
|
g_test_add_func("/qemu-opts/append_to_null", test_opts_append_to_null);
|
|
|
|
g_test_add_func("/qemu-opts/append", test_opts_append);
|
2018-02-19 21:05:30 +03:00
|
|
|
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);
|
2014-05-20 01:53:55 +04:00
|
|
|
g_test_run();
|
|
|
|
return 0;
|
|
|
|
}
|