diff BUGFIX single subtree for nested changes in move
diff --git a/src/diff.c b/src/diff.c
index fd5a176..556ac58 100644
--- a/src/diff.c
+++ b/src/diff.c
@@ -161,6 +161,127 @@
return rc;
}
+/**
+ * @brief Find metadata/an attribute of a node.
+ *
+ * @param[in] node Node to search.
+ * @param[in] name Metadata/attribute name.
+ * @param[out] meta Metadata found, NULL if not found.
+ * @param[out] attr Attribute found, NULL if not found.
+ */
+static void
+lyd_diff_find_meta(const struct lyd_node *node, const char *name, struct lyd_meta **meta, struct lyd_attr **attr)
+{
+ struct lyd_meta *m;
+ struct lyd_attr *a;
+
+ if (meta) {
+ *meta = NULL;
+ }
+ if (attr) {
+ *attr = NULL;
+ }
+
+ if (node->schema) {
+ assert(meta);
+
+ LY_LIST_FOR(node->meta, m) {
+ if (!strcmp(m->name, name) && !strcmp(m->annotation->module->name, "yang")) {
+ *meta = m;
+ break;
+ }
+ }
+ } else {
+ assert(attr);
+
+ LY_LIST_FOR(((struct lyd_node_opaq *)node)->attr, a) {
+ /* name */
+ if (strcmp(a->name.name, name)) {
+ continue;
+ }
+
+ /* module */
+ switch (a->format) {
+ case LY_VALUE_JSON:
+ if (strcmp(a->name.module_name, "yang")) {
+ continue;
+ }
+ break;
+ case LY_VALUE_XML:
+ if (strcmp(a->name.module_ns, "urn:ietf:params:xml:ns:yang:1")) {
+ continue;
+ }
+ break;
+ default:
+ LOGINT(LYD_CTX(node));
+ return;
+ }
+
+ *attr = a;
+ break;
+ }
+ }
+}
+
+/**
+ * @brief Learn operation of a diff node.
+ *
+ * @param[in] diff_node Diff node.
+ * @param[out] op Operation.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyd_diff_get_op(const struct lyd_node *diff_node, enum lyd_diff_op *op)
+{
+ struct lyd_meta *meta = NULL;
+ struct lyd_attr *attr = NULL;
+ const struct lyd_node *diff_parent;
+ const char *str;
+ char *path;
+
+ for (diff_parent = diff_node; diff_parent; diff_parent = lyd_parent(diff_parent)) {
+ lyd_diff_find_meta(diff_parent, "operation", &meta, &attr);
+ if (!meta && !attr) {
+ continue;
+ }
+
+ str = meta ? lyd_get_meta_value(meta) : attr->value;
+ if ((str[0] == 'r') && (diff_parent != diff_node)) {
+ /* we do not care about this operation if it's in our parent */
+ continue;
+ }
+ *op = lyd_diff_str2op(str);
+ return LY_SUCCESS;
+ }
+
+ /* operation not found */
+ path = lyd_path(diff_node, LYD_PATH_STD, NULL, 0);
+ LOGERR(LYD_CTX(diff_node), LY_EINVAL, "Node \"%s\" without an operation.", path);
+ free(path);
+ return LY_EINT;
+}
+
+/**
+ * @brief Remove metadata/an attribute from a node.
+ *
+ * @param[in] node Node to update.
+ * @param[in] name Metadata/attribute name.
+ */
+static void
+lyd_diff_del_meta(struct lyd_node *node, const char *name)
+{
+ struct lyd_meta *meta;
+ struct lyd_attr *attr;
+
+ lyd_diff_find_meta(node, name, &meta, &attr);
+
+ if (meta) {
+ lyd_free_meta_single(meta);
+ } else if (attr) {
+ lyd_free_attr_single(LYD_CTX(node), attr);
+ }
+}
+
LY_ERR
lyd_diff_add(const struct lyd_node *node, enum lyd_diff_op op, const char *orig_default, const char *orig_value,
const char *key, const char *value, const char *position, const char *orig_key, const char *orig_position,
@@ -168,6 +289,7 @@
{
struct lyd_node *dup, *siblings, *match = NULL, *diff_parent = NULL, *elem;
const struct lyd_node *parent = NULL;
+ enum lyd_diff_op cur_op;
assert(diff);
@@ -189,14 +311,16 @@
/* find the first existing parent */
siblings = *diff;
- while (1) {
+ do {
/* find next node parent */
parent = node;
while (parent->parent && (!diff_parent || (parent->parent->schema != diff_parent->schema))) {
parent = lyd_parent(parent);
}
- if (parent == node) {
- /* no more parents to find */
+
+ if (lysc_is_dup_inst_list(parent->schema)) {
+ /* assume it never exists, we are not able to distinguish whether it does or not */
+ match = NULL;
break;
}
@@ -210,41 +334,51 @@
/* move down in the diff */
siblings = lyd_child_no_keys(match);
- }
+ } while (parent != node);
- /* duplicate the subtree (and connect to the diff if possible) */
- if (diff_parent) {
- LY_CHECK_RET(lyd_dup_single_to_ctx(node, LYD_CTX(diff_parent), (struct lyd_node_inner *)diff_parent,
- LYD_DUP_RECURSIVE | LYD_DUP_NO_META | LYD_DUP_WITH_PARENTS | LYD_DUP_WITH_FLAGS, &dup));
+ if (match && (parent == node)) {
+ /* special case when there is already an operation on our descendant */
+ assert(!lyd_diff_get_op(diff_parent, &cur_op) && (cur_op == LYD_DIFF_OP_NONE));
+ (void)cur_op;
+
+ /* will be replaced by the new operation */
+ lyd_diff_del_meta(diff_parent, "operation");
+ dup = diff_parent;
} else {
- LY_CHECK_RET(lyd_dup_single(node, NULL,
- LYD_DUP_RECURSIVE | LYD_DUP_NO_META | LYD_DUP_WITH_PARENTS | LYD_DUP_WITH_FLAGS, &dup));
- }
-
- /* find the first duplicated parent */
- if (!diff_parent) {
- diff_parent = lyd_parent(dup);
- while (diff_parent && diff_parent->parent) {
- diff_parent = lyd_parent(diff_parent);
+ /* duplicate the subtree (and connect to the diff if possible) */
+ if (diff_parent) {
+ LY_CHECK_RET(lyd_dup_single_to_ctx(node, LYD_CTX(diff_parent), (struct lyd_node_inner *)diff_parent,
+ LYD_DUP_RECURSIVE | LYD_DUP_NO_META | LYD_DUP_WITH_PARENTS | LYD_DUP_WITH_FLAGS, &dup));
+ } else {
+ LY_CHECK_RET(lyd_dup_single(node, NULL,
+ LYD_DUP_RECURSIVE | LYD_DUP_NO_META | LYD_DUP_WITH_PARENTS | LYD_DUP_WITH_FLAGS, &dup));
}
- } else {
- diff_parent = dup;
- while (diff_parent->parent && (diff_parent->parent->schema == parent->schema)) {
- diff_parent = lyd_parent(diff_parent);
+
+ /* find the first duplicated parent */
+ if (!diff_parent) {
+ diff_parent = lyd_parent(dup);
+ while (diff_parent && diff_parent->parent) {
+ diff_parent = lyd_parent(diff_parent);
+ }
+ } else {
+ diff_parent = dup;
+ while (diff_parent->parent && (diff_parent->parent->schema == parent->schema)) {
+ diff_parent = lyd_parent(diff_parent);
+ }
}
- }
- /* no parent existed, must be manually connected */
- if (!diff_parent) {
- /* there actually was no parent to duplicate */
- lyd_insert_sibling(*diff, dup, diff);
- } else if (!diff_parent->parent) {
- lyd_insert_sibling(*diff, diff_parent, diff);
- }
+ /* no parent existed, must be manually connected */
+ if (!diff_parent) {
+ /* there actually was no parent to duplicate */
+ lyd_insert_sibling(*diff, dup, diff);
+ } else if (!diff_parent->parent) {
+ lyd_insert_sibling(*diff, diff_parent, diff);
+ }
- /* add parent operation, if any */
- if (diff_parent && (diff_parent != dup)) {
- LY_CHECK_RET(lyd_new_meta(NULL, diff_parent, NULL, "yang:operation", "none", 0, NULL));
+ /* add parent operation, if any */
+ if (diff_parent && (diff_parent != dup)) {
+ LY_CHECK_RET(lyd_new_meta(NULL, diff_parent, NULL, "yang:operation", "none", 0, NULL));
+ }
}
/* add subtree operation */
@@ -929,98 +1063,6 @@
}
/**
- * @brief Find metadata/an attribute of a node.
- *
- * @param[in] node Node to search.
- * @param[in] name Metadata/attribute name.
- * @param[out] meta Metadata found, NULL if not found.
- * @param[out] attr Attribute found, NULL if not found.
- */
-static void
-lyd_diff_find_meta(const struct lyd_node *node, const char *name, struct lyd_meta **meta, struct lyd_attr **attr)
-{
- struct lyd_meta *m;
- struct lyd_attr *a;
-
- *meta = NULL;
- *attr = NULL;
-
- if (node->schema) {
- LY_LIST_FOR(node->meta, m) {
- if (!strcmp(m->name, name) && !strcmp(m->annotation->module->name, "yang")) {
- *meta = m;
- break;
- }
- }
- } else {
- LY_LIST_FOR(((struct lyd_node_opaq *)node)->attr, a) {
- /* name */
- if (strcmp(a->name.name, name)) {
- continue;
- }
-
- /* module */
- switch (a->format) {
- case LY_VALUE_JSON:
- if (strcmp(a->name.module_name, "yang")) {
- continue;
- }
- break;
- case LY_VALUE_XML:
- if (strcmp(a->name.module_ns, "urn:ietf:params:xml:ns:yang:1")) {
- continue;
- }
- break;
- default:
- LOGINT(LYD_CTX(node));
- return;
- }
-
- *attr = a;
- break;
- }
- }
-}
-
-/**
- * @brief Learn operation of a diff node.
- *
- * @param[in] diff_node Diff node.
- * @param[out] op Operation.
- * @return LY_ERR value.
- */
-static LY_ERR
-lyd_diff_get_op(const struct lyd_node *diff_node, enum lyd_diff_op *op)
-{
- struct lyd_meta *meta = NULL;
- struct lyd_attr *attr = NULL;
- const struct lyd_node *diff_parent;
- const char *str;
- char *path;
-
- for (diff_parent = diff_node; diff_parent; diff_parent = lyd_parent(diff_parent)) {
- lyd_diff_find_meta(diff_parent, "operation", &meta, &attr);
- if (!meta && !attr) {
- continue;
- }
-
- str = meta ? lyd_get_meta_value(meta) : attr->value;
- if ((str[0] == 'r') && (diff_parent != diff_node)) {
- /* we do not care about this operation if it's in our parent */
- continue;
- }
- *op = lyd_diff_str2op(str);
- return LY_SUCCESS;
- }
-
- /* operation not found */
- path = lyd_path(diff_node, LYD_PATH_STD, NULL, 0);
- LOGERR(LYD_CTX(diff_node), LY_EINVAL, "Node \"%s\" without an operation.", path);
- free(path);
- return LY_EINT;
-}
-
-/**
* @brief Insert a diff node into a data tree.
*
* @param[in,out] first_node First sibling of the data tree.
@@ -1358,27 +1400,6 @@
}
/**
- * @brief Remove metadata/an attribute from a node.
- *
- * @param[in] node Node to update.
- * @param[in] name Metadata/attribute name.
- */
-static void
-lyd_diff_del_meta(struct lyd_node *node, const char *name)
-{
- struct lyd_meta *meta;
- struct lyd_attr *attr;
-
- lyd_diff_find_meta(node, name, &meta, &attr);
-
- if (meta) {
- lyd_free_meta_single(meta);
- } else if (attr) {
- lyd_free_attr_single(LYD_CTX(node), attr);
- }
-}
-
-/**
* @brief Set a specific operation of a node. Delete the previous operation, if any.
* Does not change the default flag.
*
diff --git a/tests/utests/data/test_diff.c b/tests/utests/data/test_diff.c
index 818a782..748c064 100644
--- a/tests/utests/data/test_diff.c
+++ b/tests/utests/data/test_diff.c
@@ -1062,6 +1062,103 @@
}
static void
+test_userord_list3(void **state)
+{
+ (void) state;
+ const char *xml1 =
+ "<df xmlns=\"urn:libyang:tests:defaults\">\n"
+ " <ul>\n"
+ " <l1>a</l1>\n"
+ " <l2>1</l2>\n"
+ " </ul>\n"
+ " <ul>\n"
+ " <l1>b</l1>\n"
+ " <l2>2</l2>\n"
+ " </ul>\n"
+ " <ul>\n"
+ " <l1>c</l1>\n"
+ " </ul>\n"
+ " <ul>\n"
+ " <l1>d</l1>\n"
+ " <l2>4</l2>\n"
+ " </ul>\n"
+ "</df>\n";
+ const char *xml2 =
+ "<df xmlns=\"urn:libyang:tests:defaults\">\n"
+ " <ul>\n"
+ " <l1>c</l1>\n"
+ " <l2>3</l2>\n"
+ " </ul>\n"
+ " <ul>\n"
+ " <l1>a</l1>\n"
+ " <l2>1</l2>\n"
+ " </ul>\n"
+ " <ul>\n"
+ " <l1>d</l1>\n"
+ " <l2>44</l2>\n"
+ " </ul>\n"
+ " <ul>\n"
+ " <l1>b</l1>\n"
+ " <l2>2</l2>\n"
+ " </ul>\n"
+ "</df>\n";
+ const char *xml3 =
+ "<df xmlns=\"urn:libyang:tests:defaults\">\n"
+ " <ul>\n"
+ " <l1>a</l1>\n"
+ " </ul>\n"
+ " <ul>\n"
+ " <l1>c</l1>\n"
+ " <l2>3</l2>\n"
+ " </ul>\n"
+ " <ul>\n"
+ " <l1>d</l1>\n"
+ " <l2>44</l2>\n"
+ " </ul>\n"
+ " <ul>\n"
+ " <l1>b</l1>\n"
+ " <l2>2</l2>\n"
+ " </ul>\n"
+ "</df>\n";
+
+ const char *out_diff_1 =
+ "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n"
+ " <ul yang:operation=\"replace\" yang:key=\"\" yang:orig-key=\"[l1='b']\">\n"
+ " <l1>c</l1>\n"
+ " <l2 yang:operation=\"create\">3</l2>\n"
+ " </ul>\n"
+ " <ul yang:operation=\"replace\" yang:key=\"[l1='a']\" yang:orig-key=\"[l1='b']\">\n"
+ " <l1>d</l1>\n"
+ " <l2 yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"4\">44</l2>\n"
+ " </ul>\n"
+ "</df>\n";
+ const char *out_diff_2 =
+ "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n"
+ " <ul yang:operation=\"replace\" yang:key=\"\" yang:orig-key=\"[l1='c']\">\n"
+ " <l1>a</l1>\n"
+ " <l2 yang:operation=\"delete\">1</l2>\n"
+ " </ul>\n"
+ "</df>\n";
+ const char *out_merge =
+ "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n"
+ " <ul yang:operation=\"replace\" yang:key=\"\" yang:orig-key=\"[l1='b']\">\n"
+ " <l1>c</l1>\n"
+ " <l2 yang:operation=\"create\">3</l2>\n"
+ " </ul>\n"
+ " <ul yang:operation=\"replace\" yang:key=\"[l1='a']\" yang:orig-key=\"[l1='b']\">\n"
+ " <l1>d</l1>\n"
+ " <l2 yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"4\">44</l2>\n"
+ " </ul>\n"
+ " <ul yang:key=\"\" yang:orig-key=\"[l1='c']\" yang:operation=\"replace\">\n"
+ " <l1>a</l1>\n"
+ " <l2 yang:operation=\"delete\">1</l2>\n"
+ " </ul>\n"
+ "</df>\n";
+
+ TEST_DIFF_3(xml1, xml2, xml3, out_diff_1, out_diff_2, out_merge);
+}
+
+static void
test_keyless_list(void **state)
{
(void) state;
@@ -1335,6 +1432,7 @@
UTEST(test_userord_mix, setup),
UTEST(test_userord_list, setup),
UTEST(test_userord_list2, setup),
+ UTEST(test_userord_list3, setup),
UTEST(test_keyless_list, setup),
UTEST(test_state_llist, setup),
UTEST(test_wd, setup),