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

Reply via email to