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),
     };