+static void qdict_array_entries_test(void)
+{
+ QDict *dict = qdict_new();
+
+ g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 0);
+
+ qdict_put_int(dict, "bar", 0);
+ qdict_put_int(dict, "baz.0", 0);
+ g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 0);
+
+ qdict_put_int(dict, "foo.1", 0);
+ g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, -EINVAL);
+ qdict_put_int(dict, "foo.0", 0);
+ g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 2);
+ qdict_put_int(dict, "foo.bar", 0);
+ g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, -EINVAL);
+ qdict_del(dict, "foo.bar");
+
+ qdict_put_int(dict, "foo.2.a", 0);
+ qdict_put_int(dict, "foo.2.b", 0);
+ qdict_put_int(dict, "foo.2.c", 0);
+ g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 3);
+ g_assert_cmpint(qdict_array_entries(dict, ""), ==, -EINVAL);
+
+ QDECREF(dict);
+
+ dict = qdict_new();
+ qdict_put_int(dict, "1", 0);
+ g_assert_cmpint(qdict_array_entries(dict, ""), ==, -EINVAL);
+ qdict_put_int(dict, "0", 0);
+ g_assert_cmpint(qdict_array_entries(dict, ""), ==, 2);
+ qdict_put_int(dict, "bar", 0);
+ g_assert_cmpint(qdict_array_entries(dict, ""), ==, -EINVAL);
+ qdict_del(dict, "bar");
+
+ qdict_put_int(dict, "2.a", 0);
+ qdict_put_int(dict, "2.b", 0);
+ qdict_put_int(dict, "2.c", 0);
+ g_assert_cmpint(qdict_array_entries(dict, ""), ==, 3);
+
+ QDECREF(dict);
+}
+
+static void qdict_join_test(void)
+{
+ QDict *dict1, *dict2;
+ bool overwrite = false;
+ int i;
+
+ dict1 = qdict_new();
+ dict2 = qdict_new();
+
+ /* Test everything once without overwrite and once with */
+ do
+ {
+ /* Test empty dicts */
+ qdict_join(dict1, dict2, overwrite);
+
+ 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_int(dict2, "foo", 42);
+
+ for (i = 0; i < 2; i++) {
+ qdict_join(dict1, dict2, overwrite);
+
+ g_assert(qdict_size(dict1) == 1);
+ g_assert(qdict_size(dict2) == 0);
+
+ g_assert(qdict_get_int(dict1, "foo") == 42);
+ }
+
+ /* Test non-empty source and destination without conflict */
+ qdict_put_int(dict2, "bar", 23);
+
+ qdict_join(dict1, dict2, overwrite);
+
+ g_assert(qdict_size(dict1) == 2);
+ g_assert(qdict_size(dict2) == 0);
+
+ g_assert(qdict_get_int(dict1, "foo") == 42);
+ g_assert(qdict_get_int(dict1, "bar") == 23);
+
+ /* Test conflict */
+ qdict_put_int(dict2, "foo", 84);
+
+ qdict_join(dict1, dict2, overwrite);
+
+ g_assert(qdict_size(dict1) == 2);
+ g_assert(qdict_size(dict2) == !overwrite);
+
+ g_assert(qdict_get_int(dict1, "foo") == (overwrite ? 84 : 42));
+ g_assert(qdict_get_int(dict1, "bar") == 23);
+
+ if (!overwrite) {
+ 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);
+
+ if (!overwrite) {
+ g_assert(qdict_get(dict2, "foo")->refcnt == 1);
+ }
+
+ /* Clean up */
+ qdict_del(dict1, "foo");
+ qdict_del(dict1, "bar");
+
+ if (!overwrite) {
+ qdict_del(dict2, "foo");
+ }
+ }
+ while (overwrite ^= true);
+
+ QDECREF(dict1);
+ QDECREF(dict2);
+}
+
+static void qdict_crumple_test_recursive(void)
+{
+ QDict *src, *dst, *rule, *vnc, *acl, *listen;
+ QList *rules;
+
+ src = qdict_new();
+ qdict_put_str(src, "vnc.listen.addr", "127.0.0.1");
+ qdict_put_str(src, "vnc.listen.port", "5901");
+ qdict_put_str(src, "vnc.acl.rules.0.match", "fred");
+ qdict_put_str(src, "vnc.acl.rules.0.policy", "allow");
+ qdict_put_str(src, "vnc.acl.rules.1.match", "bob");
+ qdict_put_str(src, "vnc.acl.rules.1.policy", "deny");
+ qdict_put_str(src, "vnc.acl.default", "deny");
+ qdict_put_str(src, "vnc.acl..name", "acl0");
+ qdict_put_str(src, "vnc.acl.rule..name", "acl0");
+
+ dst = qobject_to_qdict(qdict_crumple(src, &error_abort));
+ g_assert(dst);
+ g_assert_cmpint(qdict_size(dst), ==, 1);
+
+ vnc = qdict_get_qdict(dst, "vnc");
+ g_assert(vnc);
+ g_assert_cmpint(qdict_size(vnc), ==, 3);
+
+ listen = qdict_get_qdict(vnc, "listen");
+ g_assert(listen);
+ g_assert_cmpint(qdict_size(listen), ==, 2);
+ g_assert_cmpstr("127.0.0.1", ==, qdict_get_str(listen, "addr"));
+ g_assert_cmpstr("5901", ==, qdict_get_str(listen, "port"));
+
+ acl = qdict_get_qdict(vnc, "acl");
+ g_assert(acl);
+ g_assert_cmpint(qdict_size(acl), ==, 3);
+
+ rules = qdict_get_qlist(acl, "rules");
+ g_assert(rules);
+ g_assert_cmpint(qlist_size(rules), ==, 2);
+
+ rule = qobject_to_qdict(qlist_pop(rules));
+ g_assert(rule);
+ g_assert_cmpint(qdict_size(rule), ==, 2);
+ g_assert_cmpstr("fred", ==, qdict_get_str(rule, "match"));
+ g_assert_cmpstr("allow", ==, qdict_get_str(rule, "policy"));
+ QDECREF(rule);
+
+ rule = qobject_to_qdict(qlist_pop(rules));
+ g_assert(rule);
+ 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"));
+ 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_str(src, "rule.0", "fred");
+ qdict_put_str(src, "rule.0.policy", "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_str(src, "rule.0", "fred");
+ qdict_put_str(src, "rule.a", "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_str(src, "rule.b", "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_str(src, "rule.0", "deny");
+ qdict_put_str(src, "rule.3", "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_str(src, "rule.0", "deny");
+ qdict_put_str(src, "rule.+1", "allow");
+
+ g_assert(qdict_crumple(src, &error) == NULL);
+ g_assert(error != NULL);
+ error_free(error);
+ error = NULL;
+ QDECREF(src);
+}
+