yin parser CHANGE add support for deviate element
diff --git a/src/common.h b/src/common.h
index dc23869..05d4893 100644
--- a/src/common.h
+++ b/src/common.h
@@ -199,6 +199,7 @@
#define LY_VCODE_INCHILDSTMT_YIN LYVE_SYNTAX_YIN, "Invalid element \"%.*s\" as a child of \"%.*s\"."
#define LY_VCODE_MISSATTR LYVE_SYNTAX_YIN, "Missing mandatory child element \"%s\" of %s element ."
#define LY_VCODE_UNEXP_SUBELEM LYVE_SYNTAX_YIN, "Unexpected child element \"%.*s\" of %s element."
+#define LY_VCODE_INDEV_YIN LYVE_SYNTAX_YIN, "Deviate of this type doesn't allow \"%s\" as it's sub-element."
#define LY_VCODE_XP_EOE LYVE_XPATH, "Unterminated string delimited with %c (%.15s)."
#define LY_VCODE_XP_INEXPR LYVE_XPATH, "Invalid character number %u of expression \'%s\'."
diff --git a/src/parser_yin.c b/src/parser_yin.c
index 4d75d66..805f5a6 100644
--- a/src/parser_yin.c
+++ b/src/parser_yin.c
@@ -1008,7 +1008,7 @@
enum yang_keyword parent, enum yang_keyword current, void *dest)
{
assert(current == YANG_MAX_ELEMENTS || current == YANG_MIN_ELEMENTS);
- assert(parent == YANG_LEAF_LIST || parent == YANG_REFINE || parent == YANG_LIST);
+ assert(parent == YANG_LEAF_LIST || parent == YANG_REFINE || parent == YANG_LIST || parent == YANG_DEVIATE);
uint32_t *lim;
uint16_t *flags;
struct lysp_ext_instance **exts;
@@ -1021,10 +1021,14 @@
lim = (current == YANG_MAX_ELEMENTS) ? &((struct lysp_refine *)dest)->max : &((struct lysp_refine *)dest)->min;
flags = &((struct lysp_refine *)dest)->flags;
exts = &((struct lysp_refine *)dest)->exts;
- } else {
+ } else if (parent == YANG_LIST) {
lim = (current == YANG_MAX_ELEMENTS) ? &((struct lysp_node_list *)dest)->max : &((struct lysp_node_list *)dest)->min;
flags = &((struct lysp_node_list *)dest)->flags;
exts = &((struct lysp_node_list *)dest)->exts;
+ } else {
+ lim = ((struct minmax_dev_meta *)dest)->lim;
+ flags = ((struct minmax_dev_meta *)dest)->flags;
+ exts = ((struct minmax_dev_meta *)dest)->exts;
}
if (current == YANG_MAX_ELEMENTS) {
@@ -2039,6 +2043,113 @@
return LY_SUCCESS;
}
+static LY_ERR
+yin_parse_deviate(struct yin_parser_ctx *ctx, struct yin_arg_record *attrs, const char **data,
+ struct lysp_deviate **deviates)
+{
+ LY_ERR ret = LY_SUCCESS;
+ uint8_t dev_mod;
+ const char *temp_val;
+ struct lysp_deviate *iter, *d;
+ struct lysp_deviate_add *d_add = NULL;
+ struct lysp_deviate_rpl *d_rpl = NULL;
+ struct lysp_deviate_del *d_del = NULL;
+
+ /* parse argument */
+ LY_CHECK_RET(yin_parse_attribute(ctx, attrs, YIN_ARG_VALUE, &temp_val, Y_STR_ARG, YANG_DEVIATE));
+
+ if (strcmp(temp_val, "not-supported") == 0) {
+ dev_mod = LYS_DEV_NOT_SUPPORTED;
+ } else if (strcmp(temp_val, "add") == 0) {
+ dev_mod = LYS_DEV_ADD;
+ } else if (strcmp(temp_val, "replace") == 0) {
+ dev_mod = LYS_DEV_REPLACE;
+ } else if (strcmp(temp_val, "delete") == 0) {
+ dev_mod = LYS_DEV_DELETE;
+ } else {
+ LOGVAL_PARSER((struct lys_parser_ctx *)ctx, LY_VCODE_INVAL, strlen(temp_val), temp_val, "deviate");
+ FREE_STRING(ctx->xml_ctx.ctx, temp_val);
+ return LY_EVALID;
+ }
+ FREE_STRING(ctx->xml_ctx.ctx, temp_val);
+
+ if (dev_mod == LYS_DEV_NOT_SUPPORTED) {
+ d = calloc(1, sizeof *d);
+ LY_CHECK_ERR_RET(!d, LOGMEM(ctx->xml_ctx.ctx), LY_EMEM);
+ struct yin_subelement subelems[1] = {
+ {YANG_CUSTOM, NULL, 0}
+ };
+ ret = yin_parse_content(ctx, subelems, 1, data, YANG_DEVIATE, NULL, &d->exts);
+
+ } else if (dev_mod == LYS_DEV_ADD) {
+ d_add = calloc(1, sizeof *d_add);
+ LY_CHECK_ERR_RET(!d_add, LOGMEM(ctx->xml_ctx.ctx), LY_EMEM);
+ d = (struct lysp_deviate *)d_add;
+ struct minmax_dev_meta min = {&d_add->min, &d_add->flags, &d_add->exts};
+ struct minmax_dev_meta max = {&d_add->max, &d_add->flags, &d_add->exts};
+ struct yin_subelement subelems[9] = {
+ {YANG_CONFIG, &d_add->flags, YIN_SUBELEM_UNIQUE},
+ {YANG_DEFAULT, &d_add->dflts, 0},
+ {YANG_MANDATORY, &d_add->flags, YIN_SUBELEM_UNIQUE},
+ {YANG_MAX_ELEMENTS, &max, YIN_SUBELEM_UNIQUE},
+ {YANG_MIN_ELEMENTS, &min, YIN_SUBELEM_UNIQUE},
+ {YANG_MUST, &d_add->musts, 0},
+ {YANG_UNIQUE, &d_add->uniques, 0},
+ {YANG_UNITS, &d_add->units, YIN_SUBELEM_UNIQUE},
+ {YANG_CUSTOM, NULL, 0},
+ };
+ ret = yin_parse_content(ctx, subelems, 9, data, YANG_DEVIATE, NULL, &d_add->exts);
+
+ } else if (dev_mod == LYS_DEV_REPLACE) {
+ d_rpl = calloc(1, sizeof *d_rpl);
+ LY_CHECK_ERR_RET(!d_rpl, LOGMEM(ctx->xml_ctx.ctx), LY_EMEM);
+ d = (struct lysp_deviate *)d_rpl;
+ struct minmax_dev_meta min = {&d_rpl->min, &d_rpl->flags, &d_rpl->exts};
+ struct minmax_dev_meta max = {&d_rpl->max, &d_rpl->flags, &d_rpl->exts};
+ struct yin_subelement subelems[8] = {
+ {YANG_CONFIG, &d_rpl->flags, YIN_SUBELEM_UNIQUE},
+ {YANG_DEFAULT, &d_rpl->dflt, YIN_SUBELEM_UNIQUE},
+ {YANG_MANDATORY, &d_rpl->flags, YIN_SUBELEM_UNIQUE},
+ {YANG_MAX_ELEMENTS, &max, YIN_SUBELEM_UNIQUE},
+ {YANG_MIN_ELEMENTS, &min, YIN_SUBELEM_UNIQUE},
+ {YANG_TYPE, &d_rpl->type, YIN_SUBELEM_UNIQUE},
+ {YANG_UNITS, &d_rpl->units, YIN_SUBELEM_UNIQUE},
+ {YANG_CUSTOM, NULL, 0},
+ };
+ ret = yin_parse_content(ctx, subelems, 8, data, YANG_DEVIATE, NULL, &d_rpl->exts);
+
+ } else {
+ d_del = calloc(1, sizeof *d_del);
+ LY_CHECK_ERR_RET(!d_del, LOGMEM(ctx->xml_ctx.ctx), LY_EMEM);
+ d = (struct lysp_deviate *)d_del;
+ struct yin_subelement subelems[5] = {
+ {YANG_DEFAULT, &d_del->dflts, 0},
+ {YANG_MUST, &d_del->musts, 0},
+ {YANG_UNIQUE, &d_del->uniques, 0},
+ {YANG_UNITS, &d_del->units, YIN_SUBELEM_UNIQUE},
+ {YANG_CUSTOM, NULL, 0},
+ };
+ ret = yin_parse_content(ctx, subelems, 5, data, YANG_DEVIATE, NULL, &d_del->exts);
+ }
+ LY_CHECK_GOTO(ret, cleanup);
+
+ d->mod = dev_mod;
+ /* insert into siblings */
+ if (!*deviates) {
+ *deviates = d;
+ } else {
+ for (iter = *deviates; iter->next; iter = iter->next);
+ iter->next = d;
+ }
+
+ return ret;
+
+cleanup:
+ free(d);
+ /* TODO log deviate error */
+ return ret;
+}
+
/**
* @brief Map keyword type to substatement info.
*
@@ -2153,33 +2264,41 @@
/* check if this element can be child of current element */
subelem = get_record(kw, subelem_info_size, subelem_info);
if (!subelem) {
- LOGVAL_PARSER((struct lys_parser_ctx *)ctx, LY_VCODE_UNEXP_SUBELEM, name.len, name.value, ly_stmt2str(current_element));
+ if (current_element == YANG_DEVIATE && isdevsub(kw)) {
+ LOGVAL_PARSER((struct lys_parser_ctx *)ctx, LY_VCODE_INDEV_YIN, ly_stmt2str(kw));
+ } else {
+ LOGVAL_PARSER((struct lys_parser_ctx *)ctx, LY_VCODE_UNEXP_SUBELEM, name.len, name.value, ly_stmt2str(current_element));
+ }
ret = LY_EVALID;
goto cleanup;
}
/* TODO check relative order */
- /* check flags */
- /* if element is unique and already defined log error */
+ /* flag check */
if ((subelem->flags & YIN_SUBELEM_UNIQUE) && (subelem->flags & YIN_SUBELEM_PARSED)) {
+ /* subelement uniquenes */
LOGVAL_PARSER((struct lys_parser_ctx *)ctx, LYVE_SYNTAX_YIN, "Redefinition of %s element in %s element.", ly_stmt2str(kw), ly_stmt2str(current_element));
return LY_EVALID;
}
if (subelem->flags & YIN_SUBELEM_FIRST) {
+ /* subelement is supposed to be defined as first subelement */
ret = yin_check_subelem_first_constraint(ctx, subelem_info, subelem_info_size, current_element, subelem);
LY_CHECK_GOTO(ret, cleanup);
}
if (subelem->flags & YIN_SUBELEM_VER2) {
+ /* subelement is supported only in version 1.1 or higher */
if (ctx->mod_version < 2) {
LOGVAL_PARSER((struct lys_parser_ctx *)ctx, LYVCODE_INSUBELEM2, ly_stmt2str(kw), ly_stmt2str(current_element));
ret = LY_EVALID;
goto cleanup;
}
}
+ /* note that element was parsed for easy uniqueness check in next iterations */
subelem->flags |= YIN_SUBELEM_PARSED;
switch (kw) {
+ /* call responsible function */
case YANG_CUSTOM:
ret = yin_parse_extension_instance(ctx, attrs, data, name2fullname(name.value, prefix.len),
namelen2fulllen(name.len, prefix.len),
@@ -2249,6 +2368,7 @@
}
break;
case YANG_DEVIATE:
+ ret = yin_parse_deviate(ctx, attrs, data, (struct lysp_deviate **)subelem->dest);
break;
case YANG_DEVIATION:
break;
@@ -2380,8 +2500,14 @@
case YANG_SUBMODULE:
break;
case YANG_TYPE:
+ if (current_element == YANG_DEVIATE) {
+ *(struct lysp_type **)subelem->dest = calloc(1, sizeof **(struct lysp_type **)subelem->dest);
+ LY_CHECK_ERR_GOTO(!(*(struct lysp_type **)subelem->dest), LOGMEM(ctx->xml_ctx.ctx); ret = LY_EMEM, cleanup);
+ type = *((struct lysp_type **)subelem->dest);
+ } else {
+ type = (struct lysp_type *)subelem->dest;
+ }
/* type as child of another type */
- type = (struct lysp_type *)subelem->dest;
if (current_element == YANG_TYPE) {
LY_ARRAY_NEW_GOTO(ctx->xml_ctx.ctx, type->types, nested_type, ret, cleanup);
type->flags |= LYS_SET_TYPE;
@@ -2428,6 +2554,7 @@
} else {
/* elements with text or none content */
/* save text content, if text_content isn't set, it's just ignored */
+ /* no resources are allocated in this branch so no need to use cleanup label */
LY_CHECK_RET(yin_validate_value(ctx, Y_STR_ARG, out, out_len));
if (text_content) {
if (dynamic) {
@@ -2448,6 +2575,7 @@
LY_CHECK_RET(lyxml_get_element(&ctx->xml_ctx, data, &prefix.value, &prefix.len, &name.value, &name.len));
}
+ /* mandatory subelemnts are checked only after whole element was succesfully parsed */
LY_CHECK_RET(yin_check_subelem_mandatory_constraint(ctx, subelem_info, subelem_info_size, current_element));
}
@@ -2706,7 +2834,7 @@
/* end of extension instance reached */
break;
}
- LY_CHECK_RET(yin_parse_element_generic(ctx, name, name_len, prefix, prefix_len, data, &new_subelem));
+ LY_CHECK_RET(yin_parse_element_generic(ctx, name, name_len, data, &new_subelem));
if (!e->child) {
e->child = new_subelem;
} else {
@@ -2735,13 +2863,13 @@
}
LY_ERR
-yin_parse_element_generic(struct yin_parser_ctx *ctx, const char *name, size_t name_len, const char *prefix,
- size_t prefix_len, const char **data, struct lysp_stmt **element)
+yin_parse_element_generic(struct yin_parser_ctx *ctx, const char *name, size_t name_len, const char **data,
+ struct lysp_stmt **element)
{
LY_ERR ret = LY_SUCCESS;
const char *temp_prefix, *temp_name;
char *out = NULL;
- size_t out_len, temp_name_len, temp_prefix_len;
+ size_t out_len, temp_name_len, temp_prefix_len, prefix_len;
int dynamic;
struct yin_arg_record *subelem_args = NULL;
struct lysp_stmt *last = NULL, *new = NULL;
@@ -2800,7 +2928,7 @@
/* end of element reached */
break;
}
- ret = yin_parse_element_generic(ctx, temp_name, temp_name_len, temp_prefix, temp_prefix_len, data, &last->next);
+ ret = yin_parse_element_generic(ctx, temp_name, temp_name_len, data, &last->next);
LY_CHECK_GOTO(ret, err);
last = last->next;
}
diff --git a/src/parser_yin.h b/src/parser_yin.h
index fca7c04..f6b0d6b 100644
--- a/src/parser_yin.h
+++ b/src/parser_yin.h
@@ -29,6 +29,14 @@
#define name2fullname(name, prefix_len) (prefix_len != 0 ? name - (prefix_len + 1) : name)
#define namelen2fulllen(name_len, prefix_len) (prefix_len != 0 ? name_len + prefix_len + 1 : name_len)
+/* shortcut to determin if keyword can in general be subelement of deviation regardles of it's type */
+#define isdevsub(kw) (kw == YANG_CONFIG || kw == YANG_DEFAULT || kw == YANG_MANDATORY || \
+ kw == YANG_MAX_ELEMENTS || kw == YANG_MIN_ELEMENTS || \
+ kw == YANG_MUST || kw == YANG_TYPE || kw == YANG_UNIQUE || \
+ kw == YANG_UNITS || kw == YANG_CUSTOM)
+
+/* get deviate type from */
+
enum YIN_ARGUMENT {
YIN_ARG_UNKNOWN = 0, /**< parsed argument can not be matched with any supported yin argument keyword */
YIN_ARG_NAME, /**< argument name */
@@ -139,6 +147,12 @@
struct lysp_action **actions; /**< Actions to add to. */
};
+struct minmax_dev_meta {
+ uint32_t *lim;
+ uint16_t *flags;
+ struct lysp_ext_instance **exts;
+};
+
/**
* @brief Match argument name.
*
@@ -351,14 +365,12 @@
* @param[in,out] ctx Yin parser context for logging and to store current state.
* @param[in] name Name of element.
* @param[in] name_len Length of elements Name.
- * @param[in] prefix Element prefix.
- * @param[in] prefix_len Length of element prefix.
* @param[in,out] data Data to read from, always moved to currently handled character.
* @param[out] element Where the element structure should be stored.
*
* @return LY_ERR values.
*/
-LY_ERR yin_parse_element_generic(struct yin_parser_ctx *ctx, const char *name, size_t name_len, const char *prefix,
- size_t prefix_len, const char **data, struct lysp_stmt **element);
+LY_ERR yin_parse_element_generic(struct yin_parser_ctx *ctx, const char *name, size_t name_len, const char **data,
+ struct lysp_stmt **element);
#endif /* LY_PARSER_YIN_H_*/
diff --git a/tests/src/test_parser_yin.c b/tests/src/test_parser_yin.c
index b1fa9e8..6153792 100644
--- a/tests/src/test_parser_yin.c
+++ b/tests/src/test_parser_yin.c
@@ -44,6 +44,7 @@
void lysp_action_inout_free(struct ly_ctx *ctx, struct lysp_action_inout *inout);
void lysp_action_free(struct ly_ctx *ctx, struct lysp_action *action);
void lysp_augment_free(struct ly_ctx *ctx, struct lysp_augment *augment);
+void lysp_deviate_free(struct ly_ctx *ctx, struct lysp_deviate *d);
struct state {
struct ly_ctx *ctx;
@@ -58,7 +59,7 @@
int store = -1; /* negative for infinite logging, positive for limited logging */
/* set to 0 to printing error messages to stderr instead of checking them in code */
-#define ENABLE_LOGGER_CHECKING 0
+#define ENABLE_LOGGER_CHECKING 1
#if ENABLE_LOGGER_CHECKING
static void
@@ -353,7 +354,7 @@
const char *data = "<elem attr=\"value\">text_value</elem>";
lyxml_get_element(&st->yin_ctx->xml_ctx, &data, &prefix, &prefix_len, &name, &name_len);
- ret = yin_parse_element_generic(st->yin_ctx, name, name_len, prefix, prefix_len, &data, &exts.child);
+ ret = yin_parse_element_generic(st->yin_ctx, name, name_len, &data, &exts.child);
assert_int_equal(ret, LY_SUCCESS);
assert_int_equal(st->yin_ctx->xml_ctx.status, LYXML_END);
assert_string_equal(exts.child->stmt, "elem");
@@ -366,7 +367,7 @@
data = "<elem></elem>";
lyxml_get_element(&st->yin_ctx->xml_ctx, &data, &prefix, &prefix_len, &name, &name_len);
- ret = yin_parse_element_generic(st->yin_ctx, name, name_len, prefix, prefix_len, &data, &exts.child);
+ ret = yin_parse_element_generic(st->yin_ctx, name, name_len, &data, &exts.child);
assert_int_equal(ret, LY_SUCCESS);
assert_string_equal(exts.child->stmt, "elem");
assert_null(exts.child->child);
@@ -3247,6 +3248,163 @@
st->finished_correctly = true;
}
+static void
+test_deviate_elem(void **state)
+{
+ struct state *st = *state;
+ const char *data;
+ struct lysp_deviate *deviates = NULL;
+ struct lysp_deviate_add *d_add;
+ struct lysp_deviate_rpl *d_rpl;
+ struct lysp_deviate_del *d_del;
+
+ /* all valid arguments with min subelems */
+ data = ELEMENT_WRAPPER_START "<deviate value=\"not-supported\" />" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(st, &data, &deviates, NULL, NULL, true), LY_SUCCESS);
+ assert_int_equal(deviates->mod, LYS_DEV_NOT_SUPPORTED);
+ lysp_deviate_free(st->ctx, deviates);
+ free(deviates);
+ deviates = NULL;
+
+ data = ELEMENT_WRAPPER_START "<deviate value=\"add\" />" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(st, &data, &deviates, NULL, NULL, true), LY_SUCCESS);
+ assert_int_equal(deviates->mod, LYS_DEV_ADD);
+ lysp_deviate_free(st->ctx, deviates);
+ free(deviates);
+ deviates = NULL;
+
+ data = ELEMENT_WRAPPER_START "<deviate value=\"replace\" />" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(st, &data, &deviates, NULL, NULL, true), LY_SUCCESS);
+ assert_int_equal(deviates->mod, LYS_DEV_REPLACE);
+ lysp_deviate_free(st->ctx, deviates);
+ free(deviates);
+ deviates = NULL;
+
+ data = ELEMENT_WRAPPER_START "<deviate value=\"delete\" />" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(st, &data, &deviates, NULL, NULL, true), LY_SUCCESS);
+ assert_int_equal(deviates->mod, LYS_DEV_DELETE);
+ lysp_deviate_free(st->ctx, deviates);
+ free(deviates);
+ deviates = NULL;
+
+ /* max subelems and valid arguments */
+ data = ELEMENT_WRAPPER_START
+ "<deviate value=\"not-supported\">"
+ "</deviate>"
+ ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(st, &data, &deviates, NULL, NULL, true), LY_SUCCESS);
+ assert_int_equal(deviates->mod, LYS_DEV_NOT_SUPPORTED);
+ lysp_deviate_free(st->ctx, deviates);
+ free(deviates);
+ deviates = NULL;
+
+ data = ELEMENT_WRAPPER_START
+ "<deviate value=\"add\">"
+ "<units name=\"units\"/>"
+ "<must condition=\"cond\"/>"
+ "<unique tag=\"utag\"/>"
+ "<default value=\"def\"/>"
+ "<config value=\"true\"/>"
+ "<mandatory value=\"true\"/>"
+ "<min-elements value=\"5\"/>"
+ "<max-elements value=\"15\"/>"
+ "</deviate>"
+ ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(st, &data, &deviates, NULL, NULL, true), LY_SUCCESS);
+ d_add = (struct lysp_deviate_add *)deviates;
+ assert_int_equal(d_add->mod, LYS_DEV_ADD);
+ assert_null(d_add->next);
+ assert_null(d_add->exts);
+ assert_string_equal(d_add->units, "units");
+ assert_string_equal(d_add->musts->arg, "cond");
+ assert_string_equal(*d_add->uniques, "utag");
+ assert_string_equal(*d_add->dflts, "def");
+ assert_true(d_add->flags & LYS_MAND_TRUE && d_add->flags & LYS_CONFIG_W);
+ assert_int_equal(d_add->min, 5);
+ assert_int_equal(d_add->max, 15);
+ lysp_deviate_free(st->ctx, deviates);
+ free(deviates);
+ deviates = NULL;
+
+ data = ELEMENT_WRAPPER_START
+ "<deviate value=\"replace\">"
+ "<type name=\"newtype\"/>"
+ "<units name=\"uni\"/>"
+ "<default value=\"def\"/>"
+ "<config value=\"true\"/>"
+ "<mandatory value=\"true\"/>"
+ "<min-elements value=\"5\"/>"
+ "<max-elements value=\"15\"/>"
+ "</deviate>"
+ ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(st, &data, &deviates, NULL, NULL, true), LY_SUCCESS);
+ d_rpl = (struct lysp_deviate_rpl *)deviates;
+ assert_int_equal(d_rpl->mod, LYS_DEV_REPLACE);
+ assert_null(d_rpl->next);
+ assert_null(d_rpl->exts);
+ assert_string_equal(d_rpl->type->name, "newtype");
+ assert_string_equal(d_rpl->units, "uni");
+ assert_string_equal(d_rpl->dflt, "def");
+ assert_true(d_rpl->flags & LYS_MAND_TRUE && d_rpl->flags & LYS_CONFIG_W);
+ assert_int_equal(d_rpl->min, 5);
+ assert_int_equal(d_rpl->max, 15);
+ lysp_deviate_free(st->ctx, deviates);
+ free(deviates);
+ deviates = NULL;
+
+ data = ELEMENT_WRAPPER_START
+ "<deviate value=\"delete\">"
+ "<units name=\"u\"/>"
+ "<must condition=\"c\"/>"
+ "<unique tag=\"tag\"/>"
+ "<default value=\"default\"/>"
+ "</deviate>"
+ ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(st, &data, &deviates, NULL, NULL, true), LY_SUCCESS);
+ d_del = (struct lysp_deviate_del *)deviates;
+ assert_int_equal(d_del->mod, LYS_DEV_DELETE);
+ assert_null(d_del->next);
+ assert_null(d_del->exts);
+ assert_string_equal(d_del->units, "u");
+ assert_string_equal(d_del->musts->arg, "c");
+ assert_string_equal(*d_del->uniques, "tag");
+ assert_string_equal(*d_del->dflts, "default");
+ lysp_deviate_free(st->ctx, deviates);
+ free(deviates);
+ deviates = NULL;
+
+ /* invalid arguments */
+ data = ELEMENT_WRAPPER_START "<deviate value=\"\" />" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(st, &data, &deviates, NULL, NULL, false), LY_EVALID);
+ logbuf_assert("Invalid value \"\" of \"deviate\". Line number 1.");
+ deviates = NULL;
+
+ data = ELEMENT_WRAPPER_START "<deviate value=\"invalid\" />" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(st, &data, &deviates, NULL, NULL, false), LY_EVALID);
+ logbuf_assert("Invalid value \"invalid\" of \"deviate\". Line number 1.");
+ deviates = NULL;
+
+ data = ELEMENT_WRAPPER_START "<deviate value=\"ad\" />" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(st, &data, &deviates, NULL, NULL, false), LY_EVALID);
+ logbuf_assert("Invalid value \"ad\" of \"deviate\". Line number 1.");
+ deviates = NULL;
+
+ data = ELEMENT_WRAPPER_START "<deviate value=\"adds\" />" ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(st, &data, &deviates, NULL, NULL, false), LY_EVALID);
+ logbuf_assert("Invalid value \"adds\" of \"deviate\". Line number 1.");
+ deviates = NULL;
+
+ data = ELEMENT_WRAPPER_START
+ "<deviate value=\"not-supported\">"
+ "<must condition=\"c\"/>"
+ "</deviate>"
+ ELEMENT_WRAPPER_END;
+ assert_int_equal(test_element_helper(st, &data, &deviates, NULL, NULL, false), LY_EVALID);
+ logbuf_assert("Deviate of this type doesn't allow \"must\" as it's sub-element. Line number 1.");
+
+ st->finished_correctly = true;
+}
+
int
main(void)
{
@@ -3316,6 +3474,7 @@
cmocka_unit_test_setup_teardown(test_inout_elem, setup_element_test, teardown_element_test),
cmocka_unit_test_setup_teardown(test_action_elem, setup_element_test, teardown_element_test),
cmocka_unit_test_setup_teardown(test_augment_elem, setup_element_test, teardown_element_test),
+ cmocka_unit_test_setup_teardown(test_deviate_elem, setup_element_test, teardown_element_test),
};
return cmocka_run_group_tests(tests, setup_ly_ctx, destroy_ly_ctx);