diff --git a/src/common.h b/src/common.h index 0fedeae98..0157d26ba 100644 --- a/src/common.h +++ b/src/common.h @@ -373,6 +373,7 @@ struct ly_ctx { void *ext_clb_data; /**< optional private data for ::ly_ctx.ext_clb */ struct ly_ht *err_ht; /**< hash table of thread-specific list of errors related to the context */ pthread_mutex_t lyb_hash_lock; /**< lock for storing LYB schema hashes in schema nodes */ + struct ly_ht *leafref_links_ht; /**< hash table of leafref links between term data nodes */ }; /** diff --git a/src/context.c b/src/context.c index 7a1420322..0c410190b 100644 --- a/src/context.c +++ b/src/context.c @@ -239,6 +239,30 @@ ly_ctx_ht_err_equal_cb(void *val1_p, void *val2_p, ly_bool UNUSED(mod), void *UN return !memcmp(&err1->tid, &err2->tid, sizeof err1->tid); } +/** + * @brief Hash table value-equal callback for comparing leafref links hash table record. + */ +static ly_bool +ly_ctx_ht_leafref_links_equal_cb(void *val1_p, void *val2_p, ly_bool UNUSED(mod), void *UNUSED(cb_data)) +{ + struct lyd_leafref_links_rec *rec1 = val1_p, *rec2 = val2_p; + + return rec1->node == rec2->node; +} + +/** + * @brief Callback for freeing leafref links recorcd internal resources. + * + * @param[in] val_p Pointer to leafref links record + */ +static void +ly_ctx_ht_leafref_links_rec_free(void *val_p) +{ + struct lyd_leafref_links_rec *rec = val_p; + + lyd_free_leafref_links_rec(rec); +} + LIBYANG_API_DEF LY_ERR ly_ctx_new(const char *search_dir, uint16_t options, struct ly_ctx **new_ctx) { @@ -262,6 +286,11 @@ ly_ctx_new(const char *search_dir, uint16_t options, struct ly_ctx **new_ctx) /* plugins */ LY_CHECK_ERR_GOTO(lyplg_init(), LOGINT(NULL); rc = LY_EINT, cleanup); + if (options & LY_CTX_LEAFREF_LINKING) { + ctx->leafref_links_ht = lyht_new(1, sizeof(struct lyd_leafref_links_rec), ly_ctx_ht_leafref_links_equal_cb, NULL, 1); + LY_CHECK_ERR_GOTO(!ctx->leafref_links_ht, rc = LY_EMEM, cleanup); + } + /* initialize thread-specific error hash table */ ctx->err_ht = lyht_new(1, sizeof(struct ly_ctx_err_rec), ly_ctx_ht_err_equal_cb, NULL, 1); LY_CHECK_ERR_GOTO(!ctx->err_ht, rc = LY_EMEM, cleanup); @@ -588,6 +617,11 @@ ly_ctx_set_options(struct ly_ctx *ctx, uint16_t option) LY_CHECK_ERR_RET((option & LY_CTX_NO_YANGLIBRARY) && !(ctx->flags & LY_CTX_NO_YANGLIBRARY), LOGARG(ctx, option), LY_EINVAL); + if (!(ctx->flags & LY_CTX_LEAFREF_LINKING) && (option & LY_CTX_LEAFREF_LINKING)) { + ctx->leafref_links_ht = lyht_new(1, sizeof(struct lyd_leafref_links_rec), ly_ctx_ht_leafref_links_equal_cb, NULL, 1); + LY_CHECK_ERR_RET(!ctx->leafref_links_ht, LOGARG(ctx, option), LY_EMEM); + } + if (!(ctx->flags & LY_CTX_SET_PRIV_PARSED) && (option & LY_CTX_SET_PRIV_PARSED)) { ctx->flags |= LY_CTX_SET_PRIV_PARSED; /* recompile the whole context to set the priv pointers */ @@ -628,6 +662,11 @@ ly_ctx_unset_options(struct ly_ctx *ctx, uint16_t option) LY_CHECK_ARG_RET(ctx, ctx, LY_EINVAL); LY_CHECK_ERR_RET(option & LY_CTX_NO_YANGLIBRARY, LOGARG(ctx, option), LY_EINVAL); + if ((ctx->flags & LY_CTX_LEAFREF_LINKING) && (option & LY_CTX_LEAFREF_LINKING)) { + lyht_free(ctx->leafref_links_ht, ly_ctx_ht_leafref_links_rec_free); + ctx->leafref_links_ht = NULL; + } + if ((ctx->flags & LY_CTX_SET_PRIV_PARSED) && (option & LY_CTX_SET_PRIV_PARSED)) { struct lys_module *mod; uint32_t index; @@ -1318,6 +1357,11 @@ ly_ctx_destroy(struct ly_ctx *ctx) /* leftover unres */ lys_unres_glob_erase(&ctx->unres); + /* clean the leafref links hash table */ + if (ctx->leafref_links_ht) { + lyht_free(ctx->leafref_links_ht, ly_ctx_ht_leafref_links_rec_free); + } + /* clean the error hash table */ lyht_free(ctx->err_ht, ly_ctx_ht_err_rec_free); diff --git a/src/context.h b/src/context.h index a6367f50f..318c614c1 100644 --- a/src/context.h +++ b/src/context.h @@ -199,6 +199,10 @@ struct ly_ctx; enabled. */ #define LY_CTX_LEAFREF_EXTENDED 0x0200 /**< By default, path attribute of leafref accepts only path as defined in RFC 7950. By using this option, the path attribute will also allow using XPath functions as deref() */ +#define LY_CTX_LEAFREF_LINKING 0x0400 /**< Link valid leafref nodes with its target during validation if leafref node is not using + 'require-instance false;'. It also enables usage of + [lyd_leafref_get_links](@ref lyd_leafref_get_links) and + [lyd_leafref_link_node_tree](@ref lyd_leafref_link_node_tree) APIs. */ /** @} contextoptions */ diff --git a/src/plugins_types/leafref.c b/src/plugins_types/leafref.c index 8ab3fc5bd..8e60f986c 100644 --- a/src/plugins_types/leafref.c +++ b/src/plugins_types/leafref.c @@ -25,6 +25,7 @@ #include "common.h" #include "compat.h" #include "plugins_internal.h" /* LY_TYPE_*_STR */ +#include "tree_data_internal.h" /* lyd_link_leafref_node */ /** * @page howtoDataLYB LYB Binary Format @@ -63,12 +64,13 @@ lyplg_type_store_leafref(const struct ly_ctx *ctx, const struct lysc_type *type, } LIBYANG_API_DEF LY_ERR -lyplg_type_validate_leafref(const struct ly_ctx *UNUSED(ctx), const struct lysc_type *type, const struct lyd_node *ctx_node, +lyplg_type_validate_leafref(const struct ly_ctx *ctx, const struct lysc_type *type, const struct lyd_node *ctx_node, const struct lyd_node *tree, struct lyd_value *storage, struct ly_err_item **err) { LY_ERR ret; struct lysc_type_leafref *type_lr = (struct lysc_type_leafref *)type; char *errmsg = NULL, *path; + struct lyd_node *target = NULL; *err = NULL; @@ -78,13 +80,17 @@ lyplg_type_validate_leafref(const struct ly_ctx *UNUSED(ctx), const struct lysc_ } /* check leafref target existence */ - if (lyplg_type_resolve_leafref(type_lr, ctx_node, storage, tree, NULL, &errmsg)) { + if (lyplg_type_resolve_leafref(type_lr, ctx_node, storage, tree, &target, &errmsg)) { path = lyd_path(ctx_node, LYD_PATH_STD, NULL, 0); ret = ly_err_new(err, LY_EVALID, LYVE_DATA, path, strdup("instance-required"), "%s", errmsg); free(errmsg); return ret; } + if (ly_ctx_get_options(ctx) & LY_CTX_LEAFREF_LINKING) { + return lyd_link_leafref_node((struct lyd_node_term *)target, (struct lyd_node_term *)ctx_node); + } + return LY_SUCCESS; } diff --git a/src/tree_data.c b/src/tree_data.c index 2b007ef7a..9ccf9940c 100644 --- a/src/tree_data.c +++ b/src/tree_data.c @@ -911,6 +911,11 @@ lyd_unlink(struct lyd_node *node) /* update hashes while still linked into the tree */ lyd_unlink_hash(node); + /* unlink leafref nodes */ + if (node->schema && (node->schema->nodetype & LYD_NODE_TERM)) { + lyd_free_leafref_nodes((struct lyd_node_term *)node); + } + /* unlink from siblings */ if (node->prev->next) { node->prev->next = node->next; @@ -3268,3 +3273,148 @@ lyd_get_value(const struct lyd_node *node) return NULL; } + +LY_ERR +lyd_get_or_create_leafref_links_record(const struct lyd_node_term *node, struct lyd_leafref_links_rec **record, ly_bool create) +{ + struct ly_ht *ht; + uint32_t hash; + struct lyd_leafref_links_rec rec; + + assert(node); + assert(record); + + if (!(ly_ctx_get_options(LYD_CTX(node)) & LY_CTX_LEAFREF_LINKING)) { + *record = NULL; + return LY_EDENIED; + } + + rec.node = node; + rec.leafref_nodes = NULL; + rec.target_node = NULL; + + ht = LYD_CTX(node)->leafref_links_ht; + hash = lyht_hash((const char *)&node, sizeof & node); + if (lyht_find(ht, &rec, hash, (void **)record) == LY_ENOTFOUND) { + if (create) { + LY_CHECK_RET(lyht_insert_no_check(ht, &rec, hash, (void **)record)); + } else { + *record = NULL; + return LY_ENOTFOUND; + } + } + + return LY_SUCCESS; +} + +LIBYANG_API_DEF LY_ERR +lyd_leafref_get_links(const struct lyd_node_term *node, const struct lyd_leafref_links_rec **record) +{ + LY_CHECK_ARG_RET(NULL, node, record, LY_EINVAL); + + return lyd_get_or_create_leafref_links_record(node, (struct lyd_leafref_links_rec **)record, 0); +} + +LY_ERR +lyd_link_leafref_node(const struct lyd_node_term *node, const struct lyd_node_term *leafref_node) +{ + LY_ARRAY_COUNT_TYPE u; + const struct lyd_node_term **item = NULL; + struct lyd_leafref_links_rec *rec; + + assert(node); + assert(leafref_node); + + if (!(ly_ctx_get_options(LYD_CTX(node)) & LY_CTX_LEAFREF_LINKING)) { + return LY_EDENIED; + } + + LY_CHECK_RET(lyd_get_or_create_leafref_links_record(node, &rec, 1)); + LY_ARRAY_FOR(rec->leafref_nodes, u) { + if (rec->leafref_nodes[u] == leafref_node) { + return LY_SUCCESS; + } + } + + LY_ARRAY_NEW_RET(LYD_CTX(node), rec->leafref_nodes, item, LY_EMEM); + *item = leafref_node; + LY_CHECK_RET(lyd_get_or_create_leafref_links_record(leafref_node, &rec, 1)); + rec->target_node = node; + return LY_SUCCESS; +} + +LIBYANG_API_DEF LY_ERR +lyd_leafref_link_node_tree(const struct lyd_node *tree) +{ + const struct lyd_node *sibling, *elem; + struct lyd_node *target; + char *errmsg; + struct lyd_node_term *leafref_node; + struct lysc_node_leaf *leaf_schema; + struct lysc_type_leafref *lref; + LY_ERR ret; + + LY_CHECK_ARG_RET(NULL, tree, LY_EINVAL); + + if (!(ly_ctx_get_options(LYD_CTX(tree)) & LY_CTX_LEAFREF_LINKING)) { + return LY_EDENIED; + } + + LY_LIST_FOR(tree, sibling) { + LYD_TREE_DFS_BEGIN(sibling, elem) { + if (elem->schema->nodetype & LYD_NODE_TERM) { + leafref_node = (struct lyd_node_term *)elem; + leaf_schema = (struct lysc_node_leaf *)elem->schema; + if (leaf_schema->type->basetype == LY_TYPE_LEAFREF) { + lref = (struct lysc_type_leafref *)leaf_schema->type; + if (lyplg_type_resolve_leafref(lref, elem, &leafref_node->value, tree, &target, &errmsg)) { + free(errmsg); + } else if (target->schema->nodetype & LYD_NODE_TERM) { + ret = lyd_link_leafref_node((struct lyd_node_term *)target, leafref_node); + if (ret != LY_SUCCESS) { + return ret; + } + } + } + } + LYD_TREE_DFS_END(sibling, elem); + } + } + return LY_SUCCESS; +} + +LY_ERR +lyd_unlink_leafref_node(const struct lyd_node_term *node, const struct lyd_node_term *leafref_node) +{ + LY_ERR ret; + struct lyd_leafref_links_rec *rec; + + assert(node); + assert(leafref_node); + + if (!(ly_ctx_get_options(LYD_CTX(node)) & LY_CTX_LEAFREF_LINKING)) { + return LY_EDENIED; + } + + ret = lyd_get_or_create_leafref_links_record(node, &rec, 0); + if (ret == LY_SUCCESS) { + LY_ARRAY_REMOVE_VALUE(rec->leafref_nodes, leafref_node); + if ((LY_ARRAY_COUNT(rec->leafref_nodes) == 0) && (rec->target_node == NULL)) { + lyd_free_leafref_nodes(node); + } + } else if (ret != LY_ENOTFOUND) { + return ret; + } + + ret = lyd_get_or_create_leafref_links_record(leafref_node, &rec, 0); + if (ret == LY_SUCCESS) { + rec->target_node = NULL; + if ((LY_ARRAY_COUNT(rec->leafref_nodes) == 0) && (rec->target_node == NULL)) { + lyd_free_leafref_nodes(leafref_node); + } + } else if (ret != LY_ENOTFOUND) { + return ret; + } + + return LY_SUCCESS; +} diff --git a/src/tree_data.h b/src/tree_data.h index 4d87fbaa6..0cce9791f 100644 --- a/src/tree_data.h +++ b/src/tree_data.h @@ -997,6 +997,22 @@ struct lyd_node_opaq { const struct ly_ctx *ctx; /**< libyang context */ }; +/** + * @brief Structure of leafref links record. + */ +struct lyd_leafref_links_rec { + const struct lyd_node_term *node; /** pointer to the data node itself */ + const struct lyd_node_term **leafref_nodes; /** list of the leafref pointing to this data node [sized array](@ref sizedarrays)), + By default it is empty. It is filled automatically by validation function of + leafref nodes, which are valid and are not using 'require-instance false;'. + It can also be populated based on manual request using + [link api](@ref lyd_leafref_link_node_tree). Freeing of the resources is + automatic. */ + const struct lyd_node_term *target_node; /** pointer to leafref target data node, by default is NULL. The logic + is the same as for [leafref_nodes](@ref leafref_nodes) and is filled only + for leafrefs */ +}; + /** * @brief Get the generic parent pointer of a data node. * @@ -2702,6 +2718,29 @@ LIBYANG_API_DECL LY_ERR ly_time_str2ts(const char *value, struct timespec *ts); */ LIBYANG_API_DECL LY_ERR ly_time_ts2str(const struct timespec *ts, char **str); +/** + * @brief Gets the leafref links record for given node + * + * This API requires usage of LY_CTX_LEAFREF_LINKING context flag. + * + * @param[in] node The term data node. + * @param[out] record The leafref links record + * @return LY_SUCCESS on success. + * @return LY_ERR value on error. + */ +LIBYANG_API_DECL LY_ERR lyd_leafref_get_links(const struct lyd_node_term *node, const struct lyd_leafref_links_rec **record); + +/** + * @brief Traverse through data tree including root node siblings and adds leafrefs links to the given nodes + * + * This API requires usage of LY_CTX_LEAFREF_LINKING context flag. + * + * @param[in] tree The data tree root node. + * @return LY_SUCCESS on success. + * @return LY_ERR value on error. + */ +LIBYANG_API_DECL LY_ERR lyd_leafref_link_node_tree(const struct lyd_node *tree); + #ifdef __cplusplus } #endif diff --git a/src/tree_data_free.c b/src/tree_data_free.c index 0281ae59d..100cf4954 100644 --- a/src/tree_data_free.c +++ b/src/tree_data_free.c @@ -138,6 +138,53 @@ lyd_free_attr_siblings(const struct ly_ctx *ctx, struct lyd_attr *attr) lyd_free_attr(ctx, attr, 1); } +void +lyd_free_leafref_links_rec(struct lyd_leafref_links_rec *rec) +{ + LY_ARRAY_COUNT_TYPE u; + struct lyd_leafref_links_rec *leafref_rec; + + assert(rec); + + /* remove stored leafref nodes */ + LY_ARRAY_FOR(rec->leafref_nodes, u) { + if (lyd_get_or_create_leafref_links_record(rec->leafref_nodes[u], &leafref_rec, 0) == LY_SUCCESS) { + leafref_rec->target_node = NULL; + if ((LY_ARRAY_COUNT(leafref_rec->leafref_nodes) == 0) && (leafref_rec->target_node == NULL)) { + lyd_free_leafref_nodes(rec->leafref_nodes[u]); + } + } + } + LY_ARRAY_FREE(rec->leafref_nodes); + rec->leafref_nodes = NULL; + /* remove stored target node */ + if (rec->target_node) { + lyd_unlink_leafref_node(rec->target_node, rec->node); + } +} + +void +lyd_free_leafref_nodes(const struct lyd_node_term *node) +{ + struct ly_ht *ht; + uint32_t hash; + struct lyd_leafref_links_rec *rec; + + assert(node); + + if (lyd_get_or_create_leafref_links_record(node, &rec, 0) != LY_SUCCESS) { + return; + } + + /* free entry content */ + lyd_free_leafref_links_rec(rec); + + /* free entry itself from hash table */ + ht = LYD_CTX(node)->leafref_links_ht; + hash = lyht_hash((const char *)&node, sizeof & node); + lyht_remove(ht, rec, hash); +} + /** * @brief Free Data (sub)tree. * @param[in] node Data node to be freed. @@ -177,7 +224,10 @@ lyd_free_subtree(struct lyd_node *node, ly_bool top) /* only frees the value this way */ lyd_any_copy_value(node, NULL, 0); } else if (node->schema->nodetype & LYD_NODE_TERM) { - ((struct lysc_node_leaf *)node->schema)->type->plugin->free(LYD_CTX(node), &((struct lyd_node_term *)node)->value); + struct lyd_node_term *node_term = (struct lyd_node_term *)node; + + ((struct lysc_node_leaf *)node->schema)->type->plugin->free(LYD_CTX(node), &node_term->value); + lyd_free_leafref_nodes(node_term); } if (!node->schema) { diff --git a/src/tree_data_internal.h b/src/tree_data_internal.h index cfb93f1d5..d63959122 100644 --- a/src/tree_data_internal.h +++ b/src/tree_data_internal.h @@ -591,4 +591,55 @@ char *lyd_path_set(const struct ly_set *dnodes, LYD_PATH_TYPE pathtype); */ LY_ERR ly_set_rm_index_ordered(struct ly_set *set, uint32_t index, void (*destructor)(void *obj)); +/** + * @brief Frees data within leafref links record + * + * @param[in] rec The leafref links record + */ +void lyd_free_leafref_links_rec(struct lyd_leafref_links_rec *rec); + +/** + * @brief Frees all leafref nodes and target node of given data node + * + * @param[in] node The data node, which leafref nodes and/or target node should be cleared. + */ +void lyd_free_leafref_nodes(const struct lyd_node_term *node); + +/** + * @brief Gets or creates the leafref links record. + * + * @param[in] node The term data node. + * @param[out] record The leafref links record. + * @param[in] create Whether to create record if not exists. + * @return LY_SUCCESS on success. + * @return LY_ERR value on error. + */ +LY_ERR lyd_get_or_create_leafref_links_record(const struct lyd_node_term *node, struct lyd_leafref_links_rec **record, ly_bool create); + +/** + * @brief Adds links between leafref adn data node. + * + * If the links were already added, it will not be added again. + * This API requires usage of LY_CTX_LEAFREF_LINKING context flag. + * + * @param[in] node Data node to which, the leafref is pointing to. + * @param[in] leafref_node The leafref, which points to given node. + * @return LY_SUCCESS on success. + * @return LY_ERR value on error. + */ +LY_ERR lyd_link_leafref_node(const struct lyd_node_term *node, const struct lyd_node_term *leafref_node); + +/** + * @brief Removes links between leafref adn data node. + * + * If the links were never added, it will be silently ignored. + * This API requires usage of LY_CTX_LEAFREF_LINKING context flag. + * + * @param[in] node Data node to which, the leafref is pointing to. + * @param[in] leafref_node The leafref, which points to given node. + * @return LY_SUCCESS on success. + * @return LY_ERR value on error. + */ +LY_ERR lyd_unlink_leafref_node(const struct lyd_node_term *node, const struct lyd_node_term *leafref_node); + #endif /* LY_TREE_DATA_INTERNAL_H_ */ diff --git a/src/tree_data_new.c b/src/tree_data_new.c index 5d9f429f1..e56e61e64 100644 --- a/src/tree_data_new.c +++ b/src/tree_data_new.c @@ -1315,6 +1315,11 @@ _lyd_change_term(struct lyd_node *term, const void *value, size_t value_len, LY_ val_change = 0; } + /* clear links to leafref nodes */ + if (ly_ctx_get_options(LYD_CTX(term)) & LY_CTX_LEAFREF_LINKING) { + lyd_free_leafref_nodes(t); + } + /* always clear the default flag */ if (term->flags & LYD_DEFAULT) { for (parent = term; parent; parent = lyd_parent(parent)) { diff --git a/src/tree_edit.h b/src/tree_edit.h index 951d95d41..113c9e352 100644 --- a/src/tree_edit.h +++ b/src/tree_edit.h @@ -231,6 +231,26 @@ void *ly_realloc(void *ptr, size_t size); #define LY_ARRAY_FREE(ARRAY) \ if (ARRAY){free((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1);} +/** + * @brief Remove item from array based on value + * + * @param[in, out] ARRAY A ([sized array](@ref sizedarrays)) to be modified. + * @param[in] VALUE The item value to be removed. Only the first occurence will be removed. + */ +#define LY_ARRAY_REMOVE_VALUE(ARRAY, VALUE) \ + { \ + LY_ARRAY_COUNT_TYPE index__; \ + LY_ARRAY_FOR(ARRAY, index__) { \ + if (ARRAY[index__] == VALUE) { \ + if (index__ != LY_ARRAY_COUNT(ARRAY) - 1) { \ + memmove(&(ARRAY[index__]), &(ARRAY[LY_ARRAY_COUNT(ARRAY) - 1]), sizeof *(ARRAY)); \ + } \ + LY_ARRAY_DECREMENT(ARRAY); \ + break; \ + } \ + } \ + } + /** * @brief Insert item into linked list. * diff --git a/tests/utests/basic/test_context.c b/tests/utests/basic/test_context.c index cfba1d30f..d1fa2ad02 100644 --- a/tests/utests/basic/test_context.c +++ b/tests/utests/basic/test_context.c @@ -220,6 +220,11 @@ test_options(void **state) assert_int_equal(LY_SUCCESS, ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_PREFER_SEARCHDIRS)); assert_int_equal(0, UTEST_LYCTX->flags & LY_CTX_PREFER_SEARCHDIRS); + /* LY_CTX_LEAFREF_LINKING */ + assert_int_not_equal(0, UTEST_LYCTX->flags & LY_CTX_LEAFREF_LINKING); + assert_int_equal(LY_SUCCESS, ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_LEAFREF_LINKING)); + assert_int_equal(0, UTEST_LYCTX->flags & LY_CTX_LEAFREF_LINKING); + assert_int_equal(UTEST_LYCTX->flags, ly_ctx_get_options(UTEST_LYCTX)); /* set back */ @@ -243,6 +248,10 @@ test_options(void **state) assert_int_equal(LY_SUCCESS, ly_ctx_set_options(UTEST_LYCTX, LY_CTX_PREFER_SEARCHDIRS)); assert_int_not_equal(0, UTEST_LYCTX->flags & LY_CTX_PREFER_SEARCHDIRS); + /* LY_CTX_LEAFREF_LINKING */ + assert_int_equal(LY_SUCCESS, ly_ctx_set_options(UTEST_LYCTX, LY_CTX_LEAFREF_LINKING)); + assert_int_not_equal(0, UTEST_LYCTX->flags & LY_CTX_LEAFREF_LINKING); + assert_int_equal(UTEST_LYCTX->flags, ly_ctx_get_options(UTEST_LYCTX)); } diff --git a/tests/utests/data/test_tree_data.c b/tests/utests/data/test_tree_data.c index 494fdf39e..f022e3cff 100644 --- a/tests/utests/data/test_tree_data.c +++ b/tests/utests/data/test_tree_data.c @@ -593,6 +593,97 @@ test_lyxp_vars(void **UNUSED(state)) vars = NULL; } +static void +test_data_leafref_nodes(void **state) +{ + struct lyd_node *tree, *iter; + struct lyd_node_term *target_node, *leafref_node; + const struct lyd_leafref_links_rec *rec; + const char *schema, *data, *value; + + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_LEAFREF_LINKING); + + schema = + "module test-data-hash {" + " yang-version 1.1;" + " namespace \"urn:tests:tdh\";" + " prefix t;" + " leaf-list ll {" + " type string;" + " }" + " container c1 {" + " leaf ref1 {" + " type leafref {" + " path \"../../ll\";" + " }" + " }" + " }" + " leaf ref2 {" + " type leafref {" + " path \"../ll\";" + " }" + " }" + "}"; + + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + data = + "{" + " \"test-data-hash:ll\": [\"qwe\", \"asd\"]," + " \"test-data-hash:c1\": { \"ref1\": \"qwe\"}," + " \"test-data-hash:ref2\": \"asd\"" + "}"; + + /* The run must not crash due to the assert that checks the hash. */ + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + LY_LIST_FOR(tree, iter) { + if (strcmp(iter->schema->name, "ll") == 0) { + value = lyd_get_value(iter); + if (strcmp(value, "asd") == 0) { + target_node = (struct lyd_node_term *)iter; + } + } + if (strcmp(iter->schema->name, "ref2") == 0) { + leafref_node = (struct lyd_node_term *)iter; + } + } + + /* verify state after leafref plugin validation */ + assert_int_equal(LY_SUCCESS, lyd_leafref_get_links(target_node, &rec)); + assert_int_equal(1, LY_ARRAY_COUNT(rec->leafref_nodes)); + assert_int_equal(LY_SUCCESS, lyd_leafref_get_links(leafref_node, &rec)); + assert_ptr_equal(rec->target_node, target_node); + /* value modification of target */ + assert_int_equal(LY_SUCCESS, lyd_change_term((struct lyd_node *)target_node, "ASD")); + assert_int_equal(LY_ENOTFOUND, lyd_leafref_get_links(target_node, &rec)); + assert_int_equal(LY_ENOTFOUND, lyd_leafref_get_links(leafref_node, &rec)); + /* change back to original value */ + assert_int_equal(LY_SUCCESS, lyd_change_term((struct lyd_node *)target_node, "asd")); + assert_int_equal(LY_ENOTFOUND, lyd_leafref_get_links(target_node, &rec)); + assert_int_equal(LY_ENOTFOUND, lyd_leafref_get_links(leafref_node, &rec)); + /* linking the whole tree again */ + assert_int_equal(LY_SUCCESS, lyd_leafref_link_node_tree(tree)); + assert_int_equal(LY_SUCCESS, lyd_leafref_get_links(target_node, &rec)); + assert_int_equal(1, LY_ARRAY_COUNT(rec->leafref_nodes)); + assert_int_equal(LY_SUCCESS, lyd_leafref_get_links(leafref_node, &rec)); + assert_ptr_equal(rec->target_node, target_node); + /* value modification of leafref */ + assert_int_equal(LY_SUCCESS, lyd_change_term((struct lyd_node *)leafref_node, "qwe")); + assert_int_equal(LY_ENOTFOUND, lyd_leafref_get_links(target_node, &rec)); + assert_int_equal(LY_ENOTFOUND, lyd_leafref_get_links(leafref_node, &rec)); + assert_int_equal(LY_SUCCESS, lyd_change_term((struct lyd_node *)leafref_node, "asd")); + assert_int_equal(LY_ENOTFOUND, lyd_leafref_get_links(target_node, &rec)); + assert_int_equal(LY_ENOTFOUND, lyd_leafref_get_links(leafref_node, &rec)); + /* linking the whole tree again */ + assert_int_equal(LY_SUCCESS, lyd_leafref_link_node_tree(tree)); + assert_int_equal(LY_SUCCESS, lyd_leafref_get_links(target_node, &rec)); + assert_int_equal(1, LY_ARRAY_COUNT(rec->leafref_nodes)); + assert_int_equal(LY_SUCCESS, lyd_leafref_get_links(leafref_node, &rec)); + assert_ptr_equal(rec->target_node, target_node); + /* freeing whole tree */ + lyd_free_all(tree); +} + int main(void) { @@ -606,6 +697,7 @@ main(void) UTEST(test_find_path, setup), UTEST(test_data_hash, setup), UTEST(test_lyxp_vars), + UTEST(test_data_leafref_nodes), }; return cmocka_run_group_tests(tests, NULL, NULL);