yin parser CHANGE add unit test for yin_parse_content function
diff --git a/src/parser_yin.c b/src/parser_yin.c
index faf9fb5..1857dec 100644
--- a/src/parser_yin.c
+++ b/src/parser_yin.c
@@ -167,15 +167,19 @@
{
LY_ERR ret = LY_SUCCESS;
struct yin_arg_record *argument_record = NULL;
+ struct sized_string prefix, name;
- /* load all attributes first */
+ /* load all attributes */
while (xml_ctx->status == LYXML_ATTRIBUTE) {
- LY_ARRAY_NEW_GOTO(xml_ctx->ctx, *args, argument_record, ret, cleanup);
- ret = lyxml_get_attribute(xml_ctx, data, &argument_record->prefix, &argument_record->prefix_len,
- &argument_record->name, &argument_record->name_len);
+ ret = lyxml_get_attribute(xml_ctx, data, &prefix.value, &prefix.len, &name.value, &name.len);
LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
if (xml_ctx->status == LYXML_ATTR_CONTENT) {
+ LY_ARRAY_NEW_GOTO(xml_ctx->ctx, *args, argument_record, ret, cleanup);
+ argument_record->name = name.value;
+ argument_record->name_len = name.len;
+ argument_record->prefix = prefix.value;
+ argument_record->prefix_len = prefix.len;
argument_record->content = NULL;
argument_record->content_len = 0;
argument_record->dynamic_content = 0;
@@ -262,7 +266,8 @@
for (size_t i = 0; i < subelem_info_size; ++i) {
/* if there is element that is mandatory and isn't parsed log error and rturn LY_EVALID */
if (subelem_info[i].flags & YIN_SUBELEM_MANDATORY && !(subelem_info[i].flags & YIN_SUBELEM_PARSED)) {
- LOGVAL_PARSER(xml_ctx, LYVE_SYNTAX_YIN, "Missing mandatory subelement %s of %s element", ly_stmt2str(subelem_info[i].type), ly_stmt2str(current_element));
+ LOGVAL_PARSER(xml_ctx, LYVE_SYNTAX_YIN, "Missing mandatory subelement %s of %s element.",
+ ly_stmt2str(subelem_info[i].type), ly_stmt2str(current_element));
return LY_EVALID;
}
}
@@ -276,7 +281,8 @@
{
for (size_t i = 0; i < subelem_info_size; ++i) {
if (subelem_info[i].flags & YIN_SUBELEM_PARSED) {
- LOGVAL_PARSER(xml_ctx, LYVE_SYNTAX_YIN, "Subelement %s of %s element must be defined as first subelement.", exp_first->type, current_element);
+ LOGVAL_PARSER(xml_ctx, LYVE_SYNTAX_YIN, "Subelement %s of %s element must be defined as first subelement.",
+ ly_stmt2str(exp_first->type), ly_stmt2str(current_element));
return LY_EVALID;
}
}
@@ -335,12 +341,12 @@
/* TODO macro to check order */
/* if element is unique and already defined log error */
- if (subelem_info_rec->flags & YIN_SUBELEM_UNIQUE & YIN_SUBELEM_PARSED) {
- LOGVAL_PARSER(xml_ctx, LYVE_SYNTAX_YIN, "Redefinition of %s element in %s element.", kw, current_element);
+ if ((subelem_info_rec->flags & YIN_SUBELEM_UNIQUE) && (subelem_info_rec->flags & YIN_SUBELEM_PARSED)) {
+ LOGVAL_PARSER(xml_ctx, LYVE_SYNTAX_YIN, "Redefinition of %s element in %s element.", ly_stmt2str(kw), ly_stmt2str(current_element));
return LY_EVALID;
}
if (subelem_info_rec->flags & YIN_SUBELEM_FIRST) {
- yin_check_subelem_first_constraint(xml_ctx, subelem_info, subelem_info_size, current_element, subelem_info_rec);
+ LY_CHECK_RET(yin_check_subelem_first_constraint(xml_ctx, subelem_info, subelem_info_size, current_element, subelem_info_rec));
}
subelem_info_rec->flags |= YIN_SUBELEM_PARSED;
@@ -508,8 +514,6 @@
subelem_attrs = NULL;
subelem_info_rec = NULL;
}
- LY_CHECK_RET(yin_check_subelem_mandatory_constraint(xml_ctx, subelem_info, subelem_info_size, current_element));
-
} else {
/* elements with text or none content */
/* save text content, if text_content isn't set, it's just ignored */
@@ -531,6 +535,8 @@
/* load closing element */
LY_CHECK_RET(lyxml_get_element(xml_ctx, data, &prefix.value, &prefix.len, &name.value, &name.len));
}
+
+ LY_CHECK_RET(yin_check_subelem_mandatory_constraint(xml_ctx, subelem_info, subelem_info_size, current_element));
}
cleanup:
@@ -807,10 +813,21 @@
}
last_subelem = new_subelem;
}
+ } else {
+ /* save text content */
+ if (dynamic) {
+ e->argument = lydict_insert_zc(xml_ctx->ctx, out);
+ if (!e->argument) {
+ free(out);
+ return LY_EMEM;
+ }
+ } else {
+ e->argument = lydict_insert(xml_ctx->ctx, out, out_len);
+ LY_CHECK_RET(!e->argument, LY_EMEM);
+ }
+ LY_CHECK_RET(lyxml_get_element(xml_ctx, data, &prefix, &prefix_len, &name, &name_len));
+ LY_CHECK_RET(name, LY_EINT);
}
- } else {
- LY_CHECK_RET(lyxml_get_element(xml_ctx, data, &prefix, &prefix_len, &name, &name_len));
- LY_CHECK_RET(name, LY_EINVAL);
}
return LY_SUCCESS;
diff --git a/src/parser_yin.h b/src/parser_yin.h
index 894570d..c4cc3b1 100644
--- a/src/parser_yin.h
+++ b/src/parser_yin.h
@@ -117,16 +117,21 @@
LYEXT_SUBSTMT kw2lyext_substmt(enum yang_keyword kw);
/**
- * @brief Parse content of whole element as text.
+ * @brief Generic function for content parsing
*
- * @param[in] xml_ctx Xml context.
- * @param[in] args Sized array of arguments of current element.
- * @param[in,out] data Data to read from.
- * @param[out] value Where content of element should be stored.
- *
+ * @param[in,out] xml_ctx Xml context.
+ * @param[in] subelem_info array of valid subelement types and meta information,
+ * array must be ordered by subelem_info->type in ascending order.
+ * @param[in] subelem_info_size Size of subelem_info array.
+ * @param[in,out] data Data to read from, always moved to currently handled character.
+ * @param[in] current_element Type of current element.
+ * @param[out] Where the text content of element should be stored. Text content is ignored if set to NULL.
+ * @param[in] exts Extension instance to add to. Can be null if element cannot have extension as subelement.
* @return LY_ERR values.
*/
-LY_ERR yin_parse_text_element(struct lyxml_context *xml_ctx, const char **data, const char **value);
+LY_ERR yin_parse_content(struct lyxml_context *xml_ctx, struct yin_subelement *subelem_info, size_t subelem_info_size,
+ const char **data, enum yang_keyword current_element, const char **text_content,
+ struct lysp_ext_instance **exts);
/**
* @brief Parse simple element without any special constaints and argument mapped to yin attribute.
diff --git a/tests/src/test_parser_yin.c b/tests/src/test_parser_yin.c
index ab45d8e..4c458f1 100644
--- a/tests/src/test_parser_yin.c
+++ b/tests/src/test_parser_yin.c
@@ -27,6 +27,9 @@
#include "../../src/parser_yin.h"
#include "../../src/xml.h"
+/* prototypes of static functions */
+void lysp_ext_instance_free(struct ly_ctx *ctx, struct lysp_ext_instance *ext);
+
struct state {
struct ly_ctx *ctx;
struct lys_module *mod;
@@ -140,6 +143,7 @@
reset_state(void **state)
{
((struct state *)*state)->finished_correctly = true;
+ logbuf[0] = '\0';
teardown_f(state);
setup_f(state);
@@ -648,6 +652,91 @@
st->finished_correctly = true;
}
+static void
+test_yin_parse_content(void **state)
+{
+ struct state *st = *state;
+ LY_ERR ret = LY_SUCCESS;
+ struct sized_string name, prefix;
+ const char *data = "<prefix value=\"a_mod\" xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">"
+ "<custom xmlns=\"my-ext\">"
+ "totally amazing extension"
+ "</custom>"
+ "<text xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">wsefsdf</text>"
+ "</prefix>";
+ struct lysp_ext_instance *exts = NULL;
+ struct yin_arg_record *attrs = NULL;
+ const char *value;
+
+ lyxml_get_element(st->xml_ctx, &data, &prefix.value, &prefix.len, &name.value, &name.len);
+ yin_load_attributes(st->xml_ctx, &data, &attrs);
+
+ struct yin_subelement subelems[2] = {{YANG_CUSTOM, NULL, 0},
+ {YIN_TEXT, &value, 0}};
+ ret = yin_parse_content(st->xml_ctx, subelems, 2, &data, YANG_ACTION, NULL, &exts);
+ assert_int_equal(ret, LY_SUCCESS);
+ assert_string_equal(exts->name, "custom");
+ assert_string_equal(exts->argument, "totally amazing extension");
+ assert_string_equal(value, "wsefsdf");
+ lysp_ext_instance_free(st->ctx, exts);
+ LY_ARRAY_FREE(exts);
+ LY_ARRAY_FREE(attrs);
+ attrs = NULL;
+ lydict_remove(st->ctx, value);
+ st = reset_state(state);
+
+ /* test unique subelem */
+ const char *prefix_value;
+ struct yin_subelement subelems2[2] = {{YANG_PREFIX, &prefix_value, 0},
+ {YIN_TEXT, &value, YIN_SUBELEM_UNIQUE}};
+ data = "<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">"
+ "<prefix value=\"inv_mod\" />"
+ "<text xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">wsefsdf</text>"
+ "<text xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">wsefsdf</text>"
+ "</module>";
+ lyxml_get_element(st->xml_ctx, &data, &prefix.value, &prefix.len, &name.value, &name.len);
+ yin_load_attributes(st->xml_ctx, &data, &attrs);
+ ret = yin_parse_content(st->xml_ctx, subelems2, 2, &data, YANG_MODULE, NULL, &exts);
+ assert_int_equal(ret, LY_EVALID);
+ logbuf_assert("Redefinition of text element in module element. Line number 1.");
+ lydict_remove(st->ctx, prefix_value);
+ lydict_remove(st->ctx, value);
+ st = reset_state(state);
+ LY_ARRAY_FREE(attrs);
+ attrs = NULL;
+
+ /* test first subelem */
+ data = "<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">"
+ "<prefix value=\"inv_mod\" />"
+ "<text xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">wsefsdf</text>"
+ "<text xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">wsefsdf</text>"
+ "</module>";
+ struct yin_subelement subelems3[2] = {{YANG_PREFIX, &prefix_value, 0},
+ {YIN_TEXT, &value, YIN_SUBELEM_FIRST}};
+ lyxml_get_element(st->xml_ctx, &data, &prefix.value, &prefix.len, &name.value, &name.len);
+ yin_load_attributes(st->xml_ctx, &data, &attrs);
+ ret = yin_parse_content(st->xml_ctx, subelems3, 2, &data, YANG_MODULE, NULL, &exts);
+ assert_int_equal(ret, LY_EVALID);
+ logbuf_assert("Subelement text of module element must be defined as first subelement. Line number 1.");
+ lydict_remove(st->ctx, prefix_value);
+ st = reset_state(state);
+ LY_ARRAY_FREE(attrs);
+ attrs = NULL;
+
+ /* test mandatory subelem */
+ data = "<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">"
+ "</module>";
+ struct yin_subelement subelems4[1] = {{YANG_PREFIX, &prefix_value, YIN_SUBELEM_MANDATORY}};
+ lyxml_get_element(st->xml_ctx, &data, &prefix.value, &prefix.len, &name.value, &name.len);
+ yin_load_attributes(st->xml_ctx, &data, &attrs);
+ ret = yin_parse_content(st->xml_ctx, subelems4, 1, &data, YANG_MODULE, NULL, &exts);
+ assert_int_equal(ret, LY_EVALID);
+ logbuf_assert("Missing mandatory subelement prefix of module element. Line number 1.");
+ LY_ARRAY_FREE(attrs);
+
+ st->finished_correctly = true;
+}
+
int
main(void)
{
@@ -662,6 +751,7 @@
cmocka_unit_test_setup_teardown(test_yin_parse_yin_element_element, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_yin_parse_element_generic, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_yin_parse_extension_instance, setup_f, teardown_f),
+ cmocka_unit_test_setup_teardown(test_yin_parse_content, setup_f, teardown_f),
cmocka_unit_test(test_yin_match_argument_name),
};