schema compile CHANGE support list compilation
diff --git a/src/parser_yang.c b/src/parser_yang.c
index 64cb330..9c79d00 100644
--- a/src/parser_yang.c
+++ b/src/parser_yang.c
@@ -3638,7 +3638,7 @@
INSERT_WORD(ctx, buf, list->name, word, word_len);
/* parse substatements */
- YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret, goto checks) {
switch (kw) {
case YANG_CONFIG:
LY_CHECK_RET(parse_config(ctx, data, &list->flags, &list->exts));
@@ -3724,6 +3724,14 @@
return LY_EVALID;
}
}
+ LY_CHECK_RET(ret);
+checks:
+ if (list->max && list->min > list->max) {
+ LOGVAL_YANG(ctx, LYVE_SEMANTICS,
+ "Invalid combination of min-elements and max-elements: min value %u is bigger than the max value %u.",
+ list->min, list->max);
+ return LY_EVALID;
+ }
return ret;
}
diff --git a/src/tree_schema.c b/src/tree_schema.c
index 537d3b6..9cb5860 100644
--- a/src/tree_schema.c
+++ b/src/tree_schema.c
@@ -92,11 +92,11 @@
case LYS_CHOICE:
if (options & LYS_GETNEXT_WITHCHOICE) {
return next;
- } else if (((struct lysc_node_choice *)next)->cases) {
+ } else if ((options & LYS_GETNEXT_NOCHOICE) || !((struct lysc_node_choice *)next)->cases) {
+ next = next->next;
+ } else {
/* go into */
next = ((struct lysc_node_choice *)next)->cases[0].child;
- } else {
- next = next->next;
}
goto repeat;
default:
diff --git a/src/tree_schema.h b/src/tree_schema.h
index ec4e21e..ae4f1ff 100644
--- a/src/tree_schema.h
+++ b/src/tree_schema.h
@@ -562,7 +562,8 @@
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* 4 LYS_UNIQUE | | |x| | | | | | | | | | | |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * 5 LYS_FENABLED | | | | | | | | | | | |x| | |
+ * 5 LYS_KEY | | |x| | | | | | | | | | | |
+ * LYS_FENABLED | | | | | | | | | | | |x| | |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* 6 LYS_MAND_TRUE | |x|x| | |x| | | | | | | | |
* LYS_ORDBY_SYSTEM | | | |x|x| | | | | | | | | |
@@ -592,6 +593,7 @@
#define LYS_MAND_MASK 0x60 /**< mask for mandatory values */
#define LYS_PRESENCE 0x04 /**< flag for presence property of a container, applicable only to ::lysc_node_container */
#define LYS_UNIQUE 0x08 /**< flag for leafs being part of a unique set, applicable only to ::lysc_node_leaf */
+#define LYS_KEY 0x10 /**< flag for leafs being a key of a list, applicable only to ::lysc_node_leaf */
#define LYS_FENABLED 0x10 /**< feature enabled flag, applicable only to ::lysc_feature */
#define LYS_ORDBY_SYSTEM 0x20 /**< ordered-by user lists, applicable only to ::lysc_node_leaflist/::lysp_node_leaflist and
::lysc_node_list/::lysp_node_list */
@@ -1260,7 +1262,18 @@
const char *name; /**< node name (mandatory) */
struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
- struct lysc_node *child;
+ struct lysc_when *when; /**< when statement */
+ struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
+
+ struct lysc_node *child; /**< first child node (linked list) */
+ struct lysc_must *musts; /**< list of must restrictions ([sized array](@ref sizedarrays)) */
+ struct lysc_action *actions; /**< list of actions ([sized array](@ref sizedarrays)) */
+ struct lysc_notif *notifs; /**< list of notifications ([sized array](@ref sizedarrays)) */
+
+ struct lysc_node_leaf **keys; /**< list of pointers to the keys ([sized array](@ref sizedarrays)) */
+ struct lysc_node_leaf ***uniques; /**< list of sized arrays of pointers to the unique nodes ([sized array](@ref sizedarrays)) */
+ uint32_t min; /**< min-elements constraint */
+ uint32_t max; /**< max-elements constraint */
};
struct lysc_node_anydata {
@@ -1478,6 +1491,7 @@
* @{
*/
#define LYS_GETNEXT_WITHCHOICE 0x01 /**< lys_getnext() option to allow returning #LYS_CHOICE nodes instead of looking into them */
+#define LYS_GETNEXT_NOCHOICE 0x02 /**< lys_getnext() option to ignore (kind of conditional) nodes within choice node */
#define LYS_GETNEXT_INTONPCONT 0x40 /**< lys_getnext() option to look into non-presence container, instead of returning container itself */
#define LYS_GETNEXT_NOSTATECHECK 0x100 /**< lys_getnext() option to skip checking module validity (import-only, disabled) and
relevant if-feature conditions state */
diff --git a/src/tree_schema_compile.c b/src/tree_schema_compile.c
index 47ee438..ea6cf08 100644
--- a/src/tree_schema_compile.c
+++ b/src/tree_schema_compile.c
@@ -1590,6 +1590,8 @@
struct ly_set keys = {0};
int i;
+ assert(path_context);
+
while (**predicate == '[') {
start = (*predicate)++;
@@ -1627,6 +1629,12 @@
/* source (must be leaf or leaf-list) */
if (src_prefix) {
mod = lys_module_find_prefix(path_context, src_prefix, src_prefix_len);
+ if (!mod) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Invalid leafref path predicate \"%.*s\" - prefix \"%.*s\" not defined in module \"%s\".",
+ *predicate - start, start, src_prefix_len, src_prefix, path_context->compiled->name);
+ goto cleanup;
+ }
} else {
mod = start_node->module;
}
@@ -2024,6 +2032,7 @@
* @param[in] context_mod Module of the context node or the referencing typedef to correctly check status of referencing and referenced objects.
* @param[in] context_name Name of the context node or referencing typedef for logging.
* @param[in] type_p Parsed type to compile.
+ * @param[in] module Context module for the leafref path (to correctly resolve prefixes in path)
* @param[in] basetype Base YANG built-in type of the type to compile.
* @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
* @param[in] tpdfname Name of the type's typedef, serves as a flag - if it is leaf/leaf-list's type, it is NULL.
@@ -2268,6 +2277,18 @@
case LY_TYPE_LEAFREF:
/* RFC 7950 9.9.3 - require-instance */
if (type_p->flags & LYS_SET_REQINST) {
+ if (context_mod->version < LYS_VERSION_1_1) {
+ if (tpdfname) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+ "Leafref type \"%s\" can be restricted by require-instance statement only in YANG 1.1 modules.", tpdfname);
+ } else {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+ "Leafref type can be restricted by require-instance statement only in YANG 1.1 modules.");
+ free(*type);
+ *type = NULL;
+ }
+ return LY_EVALID;
+ }
((struct lysc_type_leafref*)(*type))->require_instance = type_p->require_instance;
} else if (base) {
/* inherit */
@@ -2770,6 +2791,168 @@
}
/**
+ * @brief Compile parsed list node information.
+ * @param[in] ctx Compile context
+ * @param[in] node_p Parsed list node.
+ * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
+ * @param[in,out] node Pre-prepared structure from lys_compile_node() with filled generic node information
+ * is enriched with the list-specific information.
+ * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
+ */
+static LY_ERR
+lys_compile_node_list(struct lysc_ctx *ctx, struct lysp_node *node_p, int options, struct lysc_node *node)
+{
+ struct lysp_node_list *list_p = (struct lysp_node_list*)node_p;
+ struct lysc_node_list *list = (struct lysc_node_list*)node;
+ struct lysp_node *child_p;
+ struct lysc_node_leaf **key, ***unique;
+ size_t len;
+ unsigned int u, v;
+ const char *keystr, *delim;
+ int config;
+ LY_ERR ret = LY_SUCCESS;
+
+ COMPILE_MEMBER_GOTO(ctx, list_p->when, list->when, options, lys_compile_when, ret, done);
+ COMPILE_ARRAY_GOTO(ctx, list_p->iffeatures, list->iffeatures, options, u, lys_compile_iffeature, ret, done);
+ list->min = list_p->min;
+ list->max = list_p->max ? list_p->max : (uint32_t)-1;
+
+ LY_LIST_FOR(list_p->child, child_p) {
+ LY_CHECK_RET(lys_compile_node(ctx, child_p, options, node));
+ }
+
+ COMPILE_ARRAY_GOTO(ctx, list_p->musts, list->musts, options, u, lys_compile_must, ret, done);
+
+ /* keys */
+ if ((list->flags & LYS_CONFIG_W) && (!list_p->key || !list_p->key[0])) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS, "Missing key in list representing configuration data.");
+ return LY_EVALID;
+ }
+
+ /* find all the keys (must be direct children) */
+ keystr = list_p->key;
+ while (keystr) {
+ delim = strpbrk(keystr, " \t\n");
+ if (delim) {
+ len = delim - keystr;
+ while (isspace(*delim)) {
+ ++delim;
+ }
+ } else {
+ len = strlen(keystr);
+ }
+
+ /* key node must be present */
+ LY_ARRAY_NEW_RET(ctx->ctx, list->keys, key, LY_EMEM);
+ *key = (struct lysc_node_leaf*)lys_child(node, node->module, keystr, len, LYS_LEAF, LYS_GETNEXT_NOCHOICE | LYS_GETNEXT_NOSTATECHECK);
+ if (!(*key)) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "The list's key \"%.*s\" not found.", len, keystr);
+ return LY_EVALID;
+ }
+ /* keys must be unique */
+ for(u = 0; u < LY_ARRAY_SIZE(list->keys) - 1; ++u) {
+ if (*key == list->keys[u]) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+ "Duplicated key identifier \"%.*s\".", len, keystr);
+ return LY_EVALID;
+ }
+ }
+ /* key must have the same config flag as the list itself */
+ if ((list->flags & LYS_CONFIG_MASK) != ((*key)->flags & LYS_CONFIG_MASK)) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS, "Key of the configuration list must not be status leaf.");
+ return LY_EVALID;
+ }
+ if (ctx->mod->compiled->version < LYS_VERSION_1_1) {
+ /* YANG 1.0 denies key to be of empty type */
+ if ((*key)->type->basetype == LY_TYPE_EMPTY) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+ "Key of a list can be of type \"empty\" only in YANG 1.1 modules.");
+ return LY_EVALID;
+ }
+ } else {
+ /* when and if-feature are illegal on list keys */
+ if ((*key)->when) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+ "List's key \"%s\" must not have any \"when\" statement.", (*key)->name);
+ return LY_EVALID;
+ }
+ if ((*key)->iffeatures) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+ "List's key \"%s\" must not have any \"if-feature\" statement.", (*key)->name);
+ return LY_EVALID;
+ }
+ }
+ /* ignore default values of the key */
+ if ((*key)->dflt) {
+ lydict_remove(ctx->ctx, (*key)->dflt);
+ (*key)->dflt = NULL;
+ }
+ /* mark leaf as key */
+ (*key)->flags |= LYS_KEY;
+
+ /* next key value */
+ keystr = delim;
+ }
+
+ /* uniques */
+ if (list_p->uniques) {
+ for (v = 0; v < LY_ARRAY_SIZE(list_p->uniques); ++v) {
+ config = -1;
+ LY_ARRAY_NEW_RET(ctx->ctx, list->uniques, unique, LY_EMEM);
+ keystr = list_p->uniques[v];
+ while (keystr) {
+ delim = strpbrk(keystr, " \t\n");
+ if (delim) {
+ len = delim - keystr;
+ while (isspace(*delim)) {
+ ++delim;
+ }
+ } else {
+ len = strlen(keystr);
+ }
+
+ /* unique node must be present */
+ LY_ARRAY_NEW_RET(ctx->ctx, *unique, key, LY_EMEM);
+ ret = lys_resolve_descendant_schema_nodeid(ctx, keystr, len, node, LYS_LEAF, (const struct lysc_node**)key);
+ if (ret != LY_SUCCESS) {
+ if (ret == LY_EDENIED) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Unique's descendant-schema-nodeid \"%.*s\" refers to a %s node instead of a leaf.",
+ len, keystr, lys_nodetype2str((*key)->nodetype));
+ }
+ return LY_EVALID;
+ }
+
+ /* all referenced leafs must be of the same config type */
+ if (config != -1 && ((((*key)->flags & LYS_CONFIG_W) && config == 0) || (((*key)->flags & LYS_CONFIG_R) && config == 1))) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+ "Unique statement \"%s\" refers to leafs with different config type.", list_p->uniques[v]);
+ return LY_EVALID;
+ } else if ((*key)->flags & LYS_CONFIG_W) {
+ config = 1;
+ } else { /* LYS_CONFIG_R */
+ config = 0;
+ }
+
+ /* mark leaf as unique */
+ (*key)->flags |= LYS_UNIQUE;
+
+ /* next unique value in line */
+ keystr = delim;
+ }
+ /* next unique definition */
+ }
+ }
+
+ //COMPILE_ARRAY_GOTO(ctx, list_p->actions, list->actions, options, u, lys_compile_action, ret, done);
+ //COMPILE_ARRAY_GOTO(ctx, list_p->notifs, list->notifs, options, u, lys_compile_notif, ret, done);
+
+done:
+ return ret;
+}
+
+/**
* @brief Compile parsed schema node information.
* @param[in] ctx Compile context
* @param[in] node_p Parsed schema node.
@@ -2798,6 +2981,7 @@
break;
case LYS_LIST:
node = (struct lysc_node*)calloc(1, sizeof(struct lysc_node_list));
+ node_compile_spec = lys_compile_node_list;
break;
case LYS_LEAFLIST:
node = (struct lysc_node*)calloc(1, sizeof(struct lysc_node_leaflist));
@@ -2830,6 +3014,11 @@
node->flags |= LYS_CONFIG_W;
}
}
+ if (parent && (parent->flags & LYS_CONFIG_R) && (node->flags & LYS_CONFIG_W)) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+ "Configuration node cannot be child of any state data node.");
+ goto error;
+ }
/* *list ordering */
if (node->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
diff --git a/src/tree_schema_free.c b/src/tree_schema_free.c
index 380bd3c..9b7a3b5 100644
--- a/src/tree_schema_free.c
+++ b/src/tree_schema_free.c
@@ -626,6 +626,28 @@
LY_ARRAY_FREE(node->dflts);
}
+static void
+lysc_node_list_free(struct ly_ctx *ctx, struct lysc_node_list *node)
+{
+ unsigned int u;
+ struct lysc_node *child, *child_next;
+
+ FREE_MEMBER(ctx, node->when, lysc_when_free);
+ FREE_ARRAY(ctx, node->iffeatures, lysc_iffeature_free);
+ LY_LIST_FOR_SAFE(node->child, child_next, child) {
+ lysc_node_free(ctx, child);
+ }
+ FREE_ARRAY(ctx, node->musts, lysc_must_free);
+
+ LY_ARRAY_FREE(node->keys);
+ LY_ARRAY_FOR(node->uniques, u) {
+ LY_ARRAY_FREE(node->uniques[u]);
+ }
+ LY_ARRAY_FREE(node->uniques);
+
+ /* TODO actions, notifs */
+}
+
void
lysc_node_free(struct ly_ctx *ctx, struct lysc_node *node)
{
@@ -643,6 +665,9 @@
case LYS_LEAFLIST:
lysc_node_leaflist_free(ctx, (struct lysc_node_leaflist*)node);
break;
+ case LYS_LIST:
+ lysc_node_list_free(ctx, (struct lysc_node_list*)node);
+ break;
default:
LOGINT(ctx);
}
diff --git a/src/tree_schema_helpers.c b/src/tree_schema_helpers.c
index b815bbc..7f952e2 100644
--- a/src/tree_schema_helpers.c
+++ b/src/tree_schema_helpers.c
@@ -85,6 +85,63 @@
}
LY_ERR
+lys_resolve_descendant_schema_nodeid(struct lysc_ctx *ctx, const char *nodeid, size_t nodeid_len, const struct lysc_node *context_node,
+ int nodetype, const struct lysc_node **target)
+{
+ LY_ERR ret = LY_EVALID;
+ const char *name, *prefix, *id;
+ const struct lysc_node *context;
+ size_t name_len, prefix_len;
+ const struct lys_module *mod;
+
+ assert(nodeid);
+ assert(context_node);
+ assert(target);
+ *target = NULL;
+
+ id = nodeid;
+ context = context_node;
+ while (*id && (ret = lys_parse_nodeid(&id, &prefix, &prefix_len, &name, &name_len)) == LY_SUCCESS) {
+ if (prefix) {
+ mod = lys_module_find_prefix(context_node->module, prefix, prefix_len);
+ if (!mod) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Invalid descendant-schema-nodeid value \"%.*s\" - prefix \"%.*s\" not defined in module \"%s\".",
+ id - nodeid, nodeid, prefix_len, prefix, context_node->module->compiled->name);
+ return LY_ENOTFOUND;
+ }
+ } else {
+ mod = context_node->module;
+ }
+ context = lys_child(context, mod, name, name_len, 0, LYS_GETNEXT_NOSTATECHECK | LYS_GETNEXT_WITHCHOICE);
+ if (!context) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Invalid descendant-schema-nodeid value \"%.*s\" - target node not found.", id - nodeid, nodeid);
+ return LY_ENOTFOUND;
+ }
+ if (nodeid_len && ((size_t)(id - nodeid) >= nodeid_len)) {
+ break;
+ }
+ if (id && *id != '/') {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Invalid descendant-schema-nodeid value \"%.*s\" - missing \"/\" as node-identifier separator.",
+ id - nodeid, nodeid);
+ return LY_EVALID;
+ }
+ ++id;
+ }
+
+ if (ret == LY_SUCCESS) {
+ *target = context;
+ if (nodetype && !(context->nodetype & nodetype)) {
+ return LY_EDENIED;
+ }
+ }
+
+ return ret;
+}
+
+LY_ERR
lysp_check_prefix(struct ly_parser_ctx *ctx, struct lysp_module *module, const char **value)
{
struct lysp_import *i;
diff --git a/src/tree_schema_internal.h b/src/tree_schema_internal.h
index 373b000..7b6d8de 100644
--- a/src/tree_schema_internal.h
+++ b/src/tree_schema_internal.h
@@ -251,6 +251,23 @@
LY_ERR lys_parse_nodeid(const char **id, const char **prefix, size_t *prefix_len, const char **name, size_t *name_len);
/**
+ * @brief Find the node according to the given descendant schema node id.
+ * Used in unique, refine and uses's augment statements
+ *
+ * @param[in] ctx Compile context
+ * @param[in] nodeid Descendant-schema-nodeid (according to the YANG grammar)
+ * @param[in] nodeid_len Length of the given nodeid, if it is not NULL-terminated string.
+ * @param[in] context_node Node where the nodeid is specified to correctly resolve prefixes and to start searching.
+ * @param[in] nodetype Optional (can be 0) restriction for target's nodetype. If target exists, but does not match
+ * the given nodetype, LY_EDENIED is returned, but no error message is printed. The value can be even an ORed value to allow
+ * multiple nodetypes.
+ * @param[out] target Found target node if any.
+ * @return LY_ERR values - LY_ENOTFOUND, LY_EVALID, LY_EDENIED or LY_SUCCESS.
+ */
+LY_ERR lys_resolve_descendant_schema_nodeid(struct lysc_ctx *ctx, const char *nodeid, size_t nodeid_len, const struct lysc_node *context_node,
+ int nodetype, const struct lysc_node **target);
+
+/**
* @brief Find the module referenced by prefix in the provided mod.
*
* @param[in] mod Schema module where the prefix was used.
diff --git a/tests/src/test_parser_yang.c b/tests/src/test_parser_yang.c
index ed46b79..9407c41 100644
--- a/tests/src/test_parser_yang.c
+++ b/tests/src/test_parser_yang.c
@@ -1510,6 +1510,71 @@
ly_ctx_destroy(ctx.ctx, NULL);
}
+static void
+test_list(void **state)
+{
+ *state = test_list;
+
+ struct lysp_module mod = {0};
+ struct ly_parser_ctx ctx = {0};
+ struct lysp_node_list *l = NULL;
+ const char *str;
+
+ assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, 0, &ctx.ctx));
+ assert_non_null(ctx.ctx);
+ ctx.line = 1;
+ ctx.mod = &mod;
+ ctx.mod->version = 2; /* simulate YANG 1.1 */
+
+ /* invalid cardinality */
+#define TEST_DUP(MEMBER, VALUE1, VALUE2) \
+ str = "l {" MEMBER" "VALUE1";"MEMBER" "VALUE2";} ..."; \
+ assert_int_equal(LY_EVALID, parse_list(&ctx, &str, NULL, (struct lysp_node**)&l)); \
+ logbuf_assert("Duplicate keyword \""MEMBER"\". Line number 1."); \
+ lysp_node_free(ctx.ctx, (struct lysp_node*)l); l = NULL;
+
+ TEST_DUP("config", "true", "false");
+ TEST_DUP("description", "text1", "text2");
+ TEST_DUP("key", "one", "two");
+ TEST_DUP("max-elements", "10", "20");
+ TEST_DUP("min-elements", "10", "20");
+ TEST_DUP("ordered-by", "user", "system");
+ TEST_DUP("reference", "1", "2");
+ TEST_DUP("status", "current", "obsolete");
+ TEST_DUP("when", "true", "false");
+#undef TEST_DUP
+
+ /* full content */
+ str = "l {action x;anydata any;anyxml anyxml; choice ch;config false;container c;description test;grouping g;if-feature f; key l; leaf l {type string;}"
+ "leaf-list ll {type string;} list li;max-elements 10; min-elements 1;must 'expr';notification not; ordered-by system; reference test;"
+ "status current;typedef t {type int8;}unique xxx;unique yyy;uses g;when true;m:ext;} ...";
+ assert_int_equal(LY_SUCCESS, parse_list(&ctx, &str, NULL, (struct lysp_node**)&l));
+ assert_non_null(l);
+ assert_int_equal(LYS_LIST, l->nodetype);
+ assert_string_equal("l", l->name);
+ assert_string_equal("test", l->dsc);
+ assert_string_equal("l", l->key);
+ assert_non_null(l->uniques);
+ assert_int_equal(2, LY_ARRAY_SIZE(l->uniques));
+ assert_string_equal("xxx", l->uniques[0]);
+ assert_string_equal("yyy", l->uniques[1]);
+ assert_int_equal(10, l->max);
+ assert_int_equal(1, l->min);
+ assert_non_null(l->exts);
+ assert_non_null(l->iffeatures);
+ assert_non_null(l->musts);
+ assert_string_equal("test", l->ref);
+ assert_non_null(l->when);
+ assert_null(l->parent);
+ assert_null(l->next);
+ assert_int_equal(LYS_CONFIG_R | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM | LYS_SET_MAX | LYS_SET_MIN, l->flags);
+ ly_set_erase(&ctx.tpdfs_nodes, NULL);
+ lysp_node_free(ctx.ctx, (struct lysp_node*)l); l = NULL;
+
+ *state = NULL;
+ ly_ctx_destroy(ctx.ctx, NULL);
+}
+
int main(void)
{
const struct CMUnitTest tests[] = {
@@ -1526,6 +1591,7 @@
cmocka_unit_test_setup(test_container, logger_setup),
cmocka_unit_test_setup_teardown(test_leaf, logger_setup, logger_teardown),
cmocka_unit_test_setup_teardown(test_leaflist, logger_setup, logger_teardown),
+ cmocka_unit_test_setup_teardown(test_list, logger_setup, logger_teardown),
};
return cmocka_run_group_tests(tests, NULL, NULL);
diff --git a/tests/src/test_tree_schema_compile.c b/tests/src/test_tree_schema_compile.c
index bec8afc..e5acd09 100644
--- a/tests/src/test_tree_schema_compile.c
+++ b/tests/src/test_tree_schema_compile.c
@@ -417,7 +417,7 @@
assert_int_equal((uint32_t)-1, ll->max);
assert_non_null(mod = lys_parse_mem(ctx, "module c {yang-version 1.1;namespace urn:c;prefix c;typedef mytype {type int8;default 10;}"
- "leaf-list ll1 {type mytype;default 1; default 2; config false;}"
+ "leaf-list ll1 {type mytype;default 1; default 1; config false;}"
"leaf-list ll2 {type mytype; ordered-by user;}}", LYS_IN_YANG));
assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
assert_non_null(mod->compiled);
@@ -426,7 +426,7 @@
assert_int_equal(3, ll->type->refcount);
assert_int_equal(2, LY_ARRAY_SIZE(ll->dflts));
assert_string_equal("1", ll->dflts[0]);
- assert_string_equal("2", ll->dflts[1]);
+ assert_string_equal("1", ll->dflts[1]);
assert_int_equal(LYS_CONFIG_R | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, ll->flags);
assert_non_null((ll = (struct lysc_node_leaflist*)mod->compiled->data->next));
assert_non_null(ll->dflts);
@@ -471,6 +471,107 @@
ly_ctx_destroy(ctx, NULL);
}
+static void
+test_node_list(void **state)
+{
+ *state = test_node_list;
+
+ struct ly_ctx *ctx;
+ struct lys_module *mod;
+ struct lysc_node_list *list;
+
+ assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_DISABLE_SEARCHDIRS, &ctx));
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module a {namespace urn:a;prefix a;feature f;"
+ "list l1 {key \"x y\"; ordered-by user; leaf x {type string; when 1;}leaf y{type string;if-feature f;}}"
+ "list l2 {config false;leaf value {type string;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ list = (struct lysc_node_list*)mod->compiled->data;
+ assert_non_null(list);
+ assert_non_null(list->keys);
+ assert_int_equal(2, LY_ARRAY_SIZE(list->keys));
+ assert_string_equal("x", list->keys[0]->name);
+ assert_string_equal("y", list->keys[1]->name);
+ assert_non_null(list->child);
+ assert_int_equal(LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_USER, list->flags);
+ assert_true(list->child->flags & LYS_KEY);
+ assert_true(list->child->next->flags & LYS_KEY);
+ list = (struct lysc_node_list*)mod->compiled->data->next;
+ assert_non_null(list);
+ assert_null(list->keys);
+ assert_non_null(list->child);
+ assert_int_equal(LYS_CONFIG_R | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, list->flags);
+ assert_false(list->child->flags & LYS_KEY);
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module b {namespace urn:b;prefix b;"
+ "list l {key a; unique \"a c/b:b\"; unique \"c/e d\";"
+ "leaf a {type string;} leaf d {type string;config false;}"
+ "container c {leaf b {type string;}leaf e{type string;config false;}}}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ list = (struct lysc_node_list*)mod->compiled->data;
+ assert_non_null(list);
+ assert_string_equal("l", list->name);
+ assert_non_null(list->keys);
+ assert_int_equal(1, LY_ARRAY_SIZE(list->keys));
+ assert_string_equal("a", list->keys[0]->name);
+ assert_true(list->keys[0]->flags & LYS_KEY);
+ assert_non_null(list->uniques);
+ assert_int_equal(2, LY_ARRAY_SIZE(list->uniques));
+ assert_int_equal(2, LY_ARRAY_SIZE(list->uniques[0]));
+ assert_string_equal("a", list->uniques[0][0]->name);
+ assert_true(list->uniques[0][0]->flags & LYS_UNIQUE);
+ assert_string_equal("b", list->uniques[0][1]->name);
+ assert_true(list->uniques[0][1]->flags & LYS_UNIQUE);
+ assert_int_equal(2, LY_ARRAY_SIZE(list->uniques[1]));
+ assert_string_equal("e", list->uniques[1][0]->name);
+ assert_true(list->uniques[1][0]->flags & LYS_UNIQUE);
+ assert_string_equal("d", list->uniques[1][1]->name);
+ assert_true(list->uniques[1][1]->flags & LYS_UNIQUE);
+
+ /* invalid */
+ assert_non_null(mod = lys_parse_mem(ctx, "module aa {namespace urn:aa;prefix aa;list l;}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Missing key in list representing configuration data.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module bb {yang-version 1.1; namespace urn:bb;prefix bb;"
+ "list l {key x; leaf x {type string; when 1;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("List's key \"x\" must not have any \"when\" statement.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module cc {yang-version 1.1;namespace urn:cc;prefix cc;feature f;"
+ "list l {key x; leaf x {type string; if-feature f;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("List's key \"x\" must not have any \"if-feature\" statement.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module dd {namespace urn:dd;prefix dd;"
+ "list l {key x; leaf x {type string; config false;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Key of the configuration list must not be status leaf.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module ee {namespace urn:ee;prefix ee;"
+ "list l {config false;key x; leaf x {type string; config true;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Configuration node cannot be child of any state data node.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module ff {namespace urn:ff;prefix ff;"
+ "list l {key x; leaf-list x {type string;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("The list's key \"x\" not found.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module gg {namespace urn:gg;prefix gg;"
+ "list l {key x; unique y;leaf x {type string;} leaf-list y {type string;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Unique's descendant-schema-nodeid \"y\" refers to a leaf-list node instead of a leaf.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module hh {namespace urn:hh;prefix hh;"
+ "list l {key x; unique \"x y\";leaf x {type string;} leaf y {config false; type string;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Unique statement \"x y\" refers to leafs with different config type.");
+
+ *state = NULL;
+ ly_ctx_destroy(ctx, NULL);
+}
+
/**
* actually the same as length restriction (tested in test_type_length()), so just check the correct handling in appropriate types,
* do not test the expression itself
@@ -1406,7 +1507,7 @@
assert_int_equal(0, has_predicate);
/* complete leafref paths */
- assert_non_null(mod = lys_parse_mem(ctx, "module a {namespace urn:a;prefix a;"
+ assert_non_null(mod = lys_parse_mem(ctx, "module a {yang-version 1.1;namespace urn:a;prefix a;"
"leaf ref1 {type leafref {path /a:target1;}} leaf ref2 {type leafref {path /a/target2; require-instance false;}}"
"leaf target1 {type string;}container a {leaf target2 {type uint8;}}}", LYS_IN_YANG));
assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
@@ -1442,7 +1543,7 @@
assert_int_equal(1, ((struct lysc_type_leafref* )type)->require_instance);
/* prefixes are reversed to check using correct context of the path! */
- assert_non_null(mod = lys_parse_mem(ctx, "module c {namespace urn:c;prefix b; import b {prefix c;}"
+ assert_non_null(mod = lys_parse_mem(ctx, "module c {yang-version 1.1;namespace urn:c;prefix b; import b {prefix c;}"
"typedef mytype3 {type c:mytype {require-instance false;}}"
"leaf ref1 {type b:mytype3;}leaf ref2 {type c:mytype2;}}", LYS_IN_YANG));
assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
@@ -1540,6 +1641,15 @@
assert_int_equal(LY_EVALID, lys_compile(mod, 0));
logbuf_assert("Invalid leafref path \"/target\" - target is supposed to represent configuration data (as the leafref does), but it does not.");
+ assert_non_null(mod = lys_parse_mem(ctx, "module ll {namespace urn:ll;prefix ll;"
+ "leaf ref {type leafref {path /target; require-instance true;}}leaf target {type string;}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Leafref type can be restricted by require-instance statement only in YANG 1.1 modules.");
+ assert_non_null(mod = lys_parse_mem(ctx, "module mm {namespace urn:mm;prefix mm;typedef mytype {type leafref {path /target;require-instance false;}}"
+ "leaf ref {type mytype;}leaf target {type string;}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Leafref type \"mytype\" can be restricted by require-instance statement only in YANG 1.1 modules.");
+
/* circular chain */
assert_non_null(mod = lys_parse_mem(ctx, "module aaa {namespace urn:aaa;prefix aaa;"
"leaf ref1 {type leafref {path /ref2;}}"
@@ -1766,6 +1876,7 @@
cmocka_unit_test_setup_teardown(test_type_dflt, logger_setup, logger_teardown),
cmocka_unit_test_setup_teardown(test_node_container, logger_setup, logger_teardown),
cmocka_unit_test_setup_teardown(test_node_leaflist, logger_setup, logger_teardown),
+ cmocka_unit_test_setup_teardown(test_node_list, logger_setup, logger_teardown),
};
return cmocka_run_group_tests(tests, NULL, NULL);