When reference counting for json objects was introduced the
old json_clone() function became json_deep_clone(), but it
still calls shallow json_clone() while cloning objects and
arrays not really producing a deep copy.
Fixing that by making other functions to perform a deep copy
as well. There are no users for this functionality inside
OVS right now, but OVS exports this functionality externally.
'ovstest test-json' extended to test both versions of a clone
on provided inputs.
Fixes: 9854d473adea ("json: Use reference counting in JSON objects")
Signed-off-by: Ilya Maximets <[email protected]>
---
lib/json.c | 16 +++---
tests/test-json.c | 125 ++++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 129 insertions(+), 12 deletions(-)
diff --git a/lib/json.c b/lib/json.c
index 3267a6196..aded8bb01 100644
--- a/lib/json.c
+++ b/lib/json.c
@@ -420,8 +420,8 @@ json_destroy_array(struct json_array *array)
free(array->elems);
}
-static struct json *json_clone_object(const struct shash *object);
-static struct json *json_clone_array(const struct json_array *array);
+static struct json *json_deep_clone_object(const struct shash *object);
+static struct json *json_deep_clone_array(const struct json_array *array);
/* Returns a deep copy of 'json'. */
struct json *
@@ -429,10 +429,10 @@ json_deep_clone(const struct json *json)
{
switch (json->type) {
case JSON_OBJECT:
- return json_clone_object(json->object);
+ return json_deep_clone_object(json->object);
case JSON_ARRAY:
- return json_clone_array(&json->array);
+ return json_deep_clone_array(&json->array);
case JSON_STRING:
return json_string_create(json->string);
@@ -464,7 +464,7 @@ json_nullable_clone(const struct json *json)
}
static struct json *
-json_clone_object(const struct shash *object)
+json_deep_clone_object(const struct shash *object)
{
struct shash_node *node;
struct json *json;
@@ -472,20 +472,20 @@ json_clone_object(const struct shash *object)
json = json_object_create();
SHASH_FOR_EACH (node, object) {
struct json *value = node->data;
- json_object_put(json, node->name, json_clone(value));
+ json_object_put(json, node->name, json_deep_clone(value));
}
return json;
}
static struct json *
-json_clone_array(const struct json_array *array)
+json_deep_clone_array(const struct json_array *array)
{
struct json **elems;
size_t i;
elems = xmalloc(array->n * sizeof *elems);
for (i = 0; i < array->n; i++) {
- elems[i] = json_clone(array->elems[i]);
+ elems[i] = json_deep_clone(array->elems[i]);
}
return json_array_create(elems, array->n);
}
diff --git a/tests/test-json.c b/tests/test-json.c
index a2f4332e7..225badf70 100644
--- a/tests/test-json.c
+++ b/tests/test-json.c
@@ -34,8 +34,124 @@ static int pretty = 0;
* instead of exactly one object or array. */
static int multiple = 0;
+
+static void test_json_equal(const struct json *a, const struct json *b,
+ bool allow_the_same);
+
+static void
+test_json_equal_object(const struct shash *a, const struct shash *b,
+ bool allow_the_same)
+{
+ struct shash_node *a_node;
+
+ ovs_assert(allow_the_same || a != b);
+
+ if (a == b) {
+ return;
+ }
+
+ ovs_assert(shash_count(a) == shash_count(b));
+
+ SHASH_FOR_EACH (a_node, a) {
+ struct shash_node *b_node = shash_find(b, a_node->name);
+
+ ovs_assert(b_node);
+ test_json_equal(a_node->data, b_node->data, allow_the_same);
+ }
+}
+
+static void
+test_json_equal_array(const struct json_array *a, const struct json_array *b,
+ bool allow_the_same)
+{
+ ovs_assert(allow_the_same || a != b);
+
+ if (a == b) {
+ return;
+ }
+
+ ovs_assert(a->n == b->n);
+
+ for (size_t i = 0; i < a->n; i++) {
+ test_json_equal(a->elems[i], b->elems[i], allow_the_same);
+ }
+}
+
+static void
+test_json_equal(const struct json *a, const struct json *b,
+ bool allow_the_same)
+{
+ ovs_assert(allow_the_same || a != b);
+ ovs_assert(a && b);
+
+ if (a == b) {
+ ovs_assert(a->count > 1);
+ return;
+ }
+
+ ovs_assert(a->type == b->type);
+
+ switch (a->type) {
+ case JSON_OBJECT:
+ test_json_equal_object(a->object, b->object, allow_the_same);
+ return;
+
+ case JSON_ARRAY:
+ test_json_equal_array(&a->array, &b->array, allow_the_same);
+ return;
+
+ case JSON_STRING:
+ case JSON_SERIALIZED_OBJECT:
+ ovs_assert(a->string != b->string);
+ ovs_assert(!strcmp(a->string, b->string));
+ return;
+
+ case JSON_NULL:
+ case JSON_FALSE:
+ case JSON_TRUE:
+ return;
+
+ case JSON_INTEGER:
+ ovs_assert(a->integer == b->integer);
+ return;
+
+ case JSON_REAL:
+ ovs_assert(a->real == b->real);
+ return;
+
+ case JSON_N_TYPES:
+ default:
+ OVS_NOT_REACHED();
+ }
+}
+
+static void
+test_json_clone(struct json *json)
+{
+ struct json *copy, *deep_copy;
+
+ copy = json_clone(json);
+
+ ovs_assert(json_equal(json, copy));
+ test_json_equal(json, copy, true);
+ ovs_assert(json->count == 2);
+
+ json_destroy(copy);
+ ovs_assert(json->count == 1);
+
+ deep_copy = json_deep_clone(json);
+
+ ovs_assert(json_equal(json, deep_copy));
+ test_json_equal(json, deep_copy, false);
+ ovs_assert(json->count == 1);
+ ovs_assert(deep_copy->count == 1);
+
+ json_destroy(deep_copy);
+ ovs_assert(json->count == 1);
+}
+
static bool
-print_and_free_json(struct json *json)
+print_test_and_free_json(struct json *json)
{
bool ok;
if (json->type == JSON_STRING) {
@@ -47,6 +163,7 @@ print_and_free_json(struct json *json)
free(s);
ok = true;
}
+ test_json_clone(json);
json_destroy(json);
return ok;
}
@@ -89,7 +206,7 @@ parse_multiple(FILE *stream)
used += json_parser_feed(parser, &buffer[used], n - used);
if (used < n) {
- if (!print_and_free_json(json_parser_finish(parser))) {
+ if (!print_test_and_free_json(json_parser_finish(parser))) {
ok = false;
}
parser = NULL;
@@ -97,7 +214,7 @@ parse_multiple(FILE *stream)
}
}
if (parser) {
- if (!print_and_free_json(json_parser_finish(parser))) {
+ if (!print_test_and_free_json(json_parser_finish(parser))) {
ok = false;
}
}
@@ -150,7 +267,7 @@ test_json_main(int argc, char *argv[])
if (multiple) {
ok = parse_multiple(stream);
} else {
- ok = print_and_free_json(json_from_stream(stream));
+ ok = print_test_and_free_json(json_from_stream(stream));
}
fclose(stream);
--
2.34.3
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev