aboutsummaryrefslogtreecommitdiff
path: root/tests/check-qdict.c
diff options
context:
space:
mode:
authorDaniel P. Berrange <berrange@redhat.com>2016-09-30 15:45:25 +0100
committerMarkus Armbruster <armbru@redhat.com>2016-10-25 17:56:14 +0200
commit603476c25c4d588a557ec2139352028f3157cba4 (patch)
tree84400c4830c048177e388504b271aa8408c4ebb6 /tests/check-qdict.c
parentb1d2e5f1b0612178cdc2752fb0f60407dd87960e (diff)
downloadqemu-603476c25c4d588a557ec2139352028f3157cba4.zip
qemu-603476c25c4d588a557ec2139352028f3157cba4.tar.gz
qemu-603476c25c4d588a557ec2139352028f3157cba4.tar.bz2
qdict: implement a qdict_crumple method for un-flattening a dict
The qdict_flatten() method will take a dict whose elements are further nested dicts/lists and flatten them by concatenating keys. The qdict_crumple() method aims to do the reverse, taking a flat qdict, and turning it into a set of nested dicts/lists. It will apply nesting based on the key name, with a '.' indicating a new level in the hierarchy. If the keys in the nested structure are all numeric, it will create a list, otherwise it will create a dict. If the keys are a mixture of numeric and non-numeric, or the numeric keys are not in strictly ascending order, an error will be reported. As an example, a flat dict containing { 'foo.0.bar': 'one', 'foo.0.wizz': '1', 'foo.1.bar': 'two', 'foo.1.wizz': '2' } will get turned into a dict with one element 'foo' whose value is a list. The list elements will each in turn be dicts. { 'foo': [ { 'bar': 'one', 'wizz': '1' }, { 'bar': 'two', 'wizz': '2' } ], } If the key is intended to contain a literal '.', then it must be escaped as '..'. ie a flat dict { 'foo..bar': 'wizz', 'bar.foo..bar': 'eek', 'bar.hello': 'world' } Will end up as { 'foo.bar': 'wizz', 'bar': { 'foo.bar': 'eek', 'hello': 'world' } } The intent of this function is that it allows a set of QemuOpts to be turned into a nested data structure that mirrors the nesting used when the same object is defined over QMP. Reviewed-by: Eric Blake <eblake@redhat.com> Reviewed-by: Kevin Wolf <kwolf@redhat.com> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Signed-off-by: Daniel P. Berrange <berrange@redhat.com> Message-Id: <1475246744-29302-3-git-send-email-berrange@redhat.com> Reviewed-by: Markus Armbruster <armbru@redhat.com> [Parameter recursive dropped along with its tests; whitespace style touched up] Signed-off-by: Markus Armbruster <armbru@redhat.com>
Diffstat (limited to 'tests/check-qdict.c')
-rw-r--r--tests/check-qdict.c158
1 files changed, 150 insertions, 8 deletions
diff --git a/tests/check-qdict.c b/tests/check-qdict.c
index 42da1e6..07b1c79 100644
--- a/tests/check-qdict.c
+++ b/tests/check-qdict.c
@@ -14,6 +14,7 @@
#include "qapi/qmp/qint.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qstring.h"
+#include "qapi/error.h"
#include "qemu-common.h"
/*
@@ -412,7 +413,6 @@ static void qdict_array_split_test(void)
QDECREF(test_dict);
-
/*
* Test the split of
*
@@ -518,7 +518,6 @@ static void qdict_join_test(void)
dict1 = qdict_new();
dict2 = qdict_new();
-
/* Test everything once without overwrite and once with */
do
{
@@ -528,7 +527,6 @@ static void qdict_join_test(void)
g_assert(qdict_size(dict1) == 0);
g_assert(qdict_size(dict2) == 0);
-
/* First iteration: Test movement */
/* Second iteration: Test empty source and non-empty destination */
qdict_put(dict2, "foo", qint_from_int(42));
@@ -542,7 +540,6 @@ static void qdict_join_test(void)
g_assert(qdict_get_int(dict1, "foo") == 42);
}
-
/* Test non-empty source and destination without conflict */
qdict_put(dict2, "bar", qint_from_int(23));
@@ -554,7 +551,6 @@ static void qdict_join_test(void)
g_assert(qdict_get_int(dict1, "foo") == 42);
g_assert(qdict_get_int(dict1, "bar") == 23);
-
/* Test conflict */
qdict_put(dict2, "foo", qint_from_int(84));
@@ -570,7 +566,6 @@ static void qdict_join_test(void)
g_assert(qdict_get_int(dict2, "foo") == 84);
}
-
/* Check the references */
g_assert(qdict_get(dict1, "foo")->refcnt == 1);
g_assert(qdict_get(dict1, "bar")->refcnt == 1);
@@ -579,7 +574,6 @@ static void qdict_join_test(void)
g_assert(qdict_get(dict2, "foo")->refcnt == 1);
}
-
/* Clean up */
qdict_del(dict1, "foo");
qdict_del(dict1, "bar");
@@ -590,11 +584,152 @@ static void qdict_join_test(void)
}
while (overwrite ^= true);
-
QDECREF(dict1);
QDECREF(dict2);
}
+static void qdict_crumple_test_recursive(void)
+{
+ QDict *src, *dst, *rule, *vnc, *acl, *listen;
+ QObject *child, *res;
+ QList *rules;
+
+ src = qdict_new();
+ qdict_put(src, "vnc.listen.addr", qstring_from_str("127.0.0.1"));
+ qdict_put(src, "vnc.listen.port", qstring_from_str("5901"));
+ qdict_put(src, "vnc.acl.rules.0.match", qstring_from_str("fred"));
+ qdict_put(src, "vnc.acl.rules.0.policy", qstring_from_str("allow"));
+ qdict_put(src, "vnc.acl.rules.1.match", qstring_from_str("bob"));
+ qdict_put(src, "vnc.acl.rules.1.policy", qstring_from_str("deny"));
+ qdict_put(src, "vnc.acl.default", qstring_from_str("deny"));
+ qdict_put(src, "vnc.acl..name", qstring_from_str("acl0"));
+ qdict_put(src, "vnc.acl.rule..name", qstring_from_str("acl0"));
+
+ res = qdict_crumple(src, &error_abort);
+
+ g_assert_cmpint(qobject_type(res), ==, QTYPE_QDICT);
+
+ dst = qobject_to_qdict(res);
+
+ g_assert_cmpint(qdict_size(dst), ==, 1);
+
+ child = qdict_get(dst, "vnc");
+ g_assert_cmpint(qobject_type(child), ==, QTYPE_QDICT);
+ vnc = qobject_to_qdict(child);
+
+ child = qdict_get(vnc, "listen");
+ g_assert_cmpint(qobject_type(child), ==, QTYPE_QDICT);
+ listen = qobject_to_qdict(child);
+ g_assert_cmpstr("127.0.0.1", ==, qdict_get_str(listen, "addr"));
+ g_assert_cmpstr("5901", ==, qdict_get_str(listen, "port"));
+
+ child = qdict_get(vnc, "acl");
+ g_assert_cmpint(qobject_type(child), ==, QTYPE_QDICT);
+ acl = qobject_to_qdict(child);
+
+ child = qdict_get(acl, "rules");
+ g_assert_cmpint(qobject_type(child), ==, QTYPE_QLIST);
+ rules = qobject_to_qlist(child);
+ g_assert_cmpint(qlist_size(rules), ==, 2);
+
+ rule = qobject_to_qdict(qlist_pop(rules));
+ g_assert_cmpint(qdict_size(rule), ==, 2);
+ g_assert_cmpstr("fred", ==, qdict_get_str(rule, "match"));
+ g_assert_cmpstr("allow", ==, qdict_get_str(rule, "policy"));
+ QDECREF(rule);
+
+ rule = qobject_to_qdict(qlist_pop(rules));
+ g_assert_cmpint(qdict_size(rule), ==, 2);
+ g_assert_cmpstr("bob", ==, qdict_get_str(rule, "match"));
+ g_assert_cmpstr("deny", ==, qdict_get_str(rule, "policy"));
+ QDECREF(rule);
+
+ /* With recursive crumpling, we should see all names unescaped */
+ g_assert_cmpstr("acl0", ==, qdict_get_str(vnc, "acl.name"));
+ child = qdict_get(vnc, "acl");
+ g_assert_cmpint(qobject_type(child), ==, QTYPE_QDICT);
+ acl = qdict_get_qdict(vnc, "acl");
+ g_assert_cmpstr("acl0", ==, qdict_get_str(acl, "rule.name"));
+
+ QDECREF(src);
+ QDECREF(dst);
+}
+
+static void qdict_crumple_test_empty(void)
+{
+ QDict *src, *dst;
+
+ src = qdict_new();
+
+ dst = (QDict *)qdict_crumple(src, &error_abort);
+
+ g_assert_cmpint(qdict_size(dst), ==, 0);
+
+ QDECREF(src);
+ QDECREF(dst);
+}
+
+static void qdict_crumple_test_bad_inputs(void)
+{
+ QDict *src;
+ Error *error = NULL;
+
+ src = qdict_new();
+ /* rule.0 can't be both a string and a dict */
+ qdict_put(src, "rule.0", qstring_from_str("fred"));
+ qdict_put(src, "rule.0.policy", qstring_from_str("allow"));
+
+ g_assert(qdict_crumple(src, &error) == NULL);
+ g_assert(error != NULL);
+ error_free(error);
+ error = NULL;
+ QDECREF(src);
+
+ src = qdict_new();
+ /* rule can't be both a list and a dict */
+ qdict_put(src, "rule.0", qstring_from_str("fred"));
+ qdict_put(src, "rule.a", qstring_from_str("allow"));
+
+ g_assert(qdict_crumple(src, &error) == NULL);
+ g_assert(error != NULL);
+ error_free(error);
+ error = NULL;
+ QDECREF(src);
+
+ src = qdict_new();
+ /* The input should be flat, ie no dicts or lists */
+ qdict_put(src, "rule.a", qdict_new());
+ qdict_put(src, "rule.b", qstring_from_str("allow"));
+
+ g_assert(qdict_crumple(src, &error) == NULL);
+ g_assert(error != NULL);
+ error_free(error);
+ error = NULL;
+ QDECREF(src);
+
+ src = qdict_new();
+ /* List indexes must not have gaps */
+ qdict_put(src, "rule.0", qstring_from_str("deny"));
+ qdict_put(src, "rule.3", qstring_from_str("allow"));
+
+ g_assert(qdict_crumple(src, &error) == NULL);
+ g_assert(error != NULL);
+ error_free(error);
+ error = NULL;
+ QDECREF(src);
+
+ src = qdict_new();
+ /* List indexes must be in %zu format */
+ qdict_put(src, "rule.0", qstring_from_str("deny"));
+ qdict_put(src, "rule.+1", qstring_from_str("allow"));
+
+ g_assert(qdict_crumple(src, &error) == NULL);
+ g_assert(error != NULL);
+ error_free(error);
+ error = NULL;
+ QDECREF(src);
+}
+
/*
* Errors test-cases
*/
@@ -742,6 +877,13 @@ int main(int argc, char **argv)
g_test_add_func("/errors/put_exists", qdict_put_exists_test);
g_test_add_func("/errors/get_not_exists", qdict_get_not_exists_test);
+ g_test_add_func("/public/crumple/recursive",
+ qdict_crumple_test_recursive);
+ g_test_add_func("/public/crumple/empty",
+ qdict_crumple_test_empty);
+ g_test_add_func("/public/crumple/bad_inputs",
+ qdict_crumple_test_bad_inputs);
+
/* The Big one */
if (g_test_slow()) {
g_test_add_func("/stress/test", qdict_stress_test);