extensions CHANGE metadata substatements

Support processing of all the Metadata annotation's substatements. Also
add basic test for overall annotation implementation
diff --git a/Doxyfile.in b/Doxyfile.in
index dbc9674..e47a7ae 100644
--- a/Doxyfile.in
+++ b/Doxyfile.in
@@ -784,6 +784,7 @@
 INPUT                  = ./src/libyang.h \
 			 ./src/context.h \
                          ./src/tree_schema.h \
+                         ./src/plugins_exts.h \
                          ./src/log.h \
                          ./src/set.h \
                          ./src/dict.h
diff --git a/src/parser_stmt.c b/src/parser_stmt.c
index 225dd02..f282322 100644
--- a/src/parser_stmt.c
+++ b/src/parser_stmt.c
@@ -752,8 +752,9 @@
 }
 
 LY_ERR
-lysp_stmt_parse(struct lysc_ctx *ctx, const struct lysp_stmt *stmt, enum ly_stmt kw, void **result)
+lysp_stmt_parse(struct lysc_ctx *ctx, const struct lysp_stmt *stmt, enum ly_stmt kw, void **result, struct lysp_ext_instance **exts)
 {
+    LY_ERR ret = LY_SUCCESS;
     struct lys_parser_ctx pctx = {0};
 
     pctx.ctx = ctx->ctx;
@@ -762,11 +763,15 @@
     pctx.path = ctx->path;
 
     switch(kw) {
+    case LY_STMT_STATUS: {
+        ret = lysp_stmt_status(&pctx, stmt, *(uint16_t**)result, exts);
+        break;
+    }
     case LY_STMT_TYPE: {
         struct lysp_type *type;
         type = calloc(1, sizeof *type);
 
-        lysp_stmt_type(&pctx, stmt, type);
+        ret = lysp_stmt_type(&pctx, stmt, type);
         (*result) = type;
         break;
         }
@@ -775,5 +780,5 @@
         return LY_EINT;
     }
 
-    return LY_SUCCESS;
+    return ret;
 }
diff --git a/src/plugins_exts_metadata.c b/src/plugins_exts_metadata.c
index 2d85fc8..80a116e 100644
--- a/src/plugins_exts_metadata.c
+++ b/src/plugins_exts_metadata.c
@@ -93,6 +93,10 @@
 void
 annotation_free(struct ly_ctx *ctx, struct lysc_ext_instance *ext)
 {
+    if (!ext->data) {
+        return;
+    }
+
     struct lyext_metadata *annotation = (struct lyext_metadata*)ext->data;
     annotation_substmt[0].storage = &annotation->iffeatures;
     annotation_substmt[1].storage = &annotation->units;
diff --git a/src/tree_schema.h b/src/tree_schema.h
index abe4908..fe84d17 100644
--- a/src/tree_schema.h
+++ b/src/tree_schema.h
@@ -33,7 +33,7 @@
 
 /**
  * @brief Macro to iterate via all elements in a schema tree which can be instantiated in data tree
- * (skips cases, input, output). This is the opening part to the #LYS_TREE_DFS_END - they always have to be used together.
+ * (skips cases, input, output). This is the opening part to the #LYSC_TREE_DFS_END - they always have to be used together.
  *
  * The function follows deep-first search algorithm:
  * <pre>
@@ -166,12 +166,14 @@
  */
 enum ly_stmt {
     LY_STMT_NONE = 0,
-    LY_STMT_STATUS,
-    LY_STMT_CONFIG,
+    LY_STMT_STATUS,             /**< in lysc_ext_substmt::storage stored as a pointer to `uint16_t`, only cardinality < #LY_STMT_CARD_SOME is allowed */
+    LY_STMT_CONFIG,             /**< in lysc_ext_substmt::storage stored as a pointer to `uint16_t`, only cardinality < #LY_STMT_CARD_SOME is allowed */
     LY_STMT_MANDATORY,
-    LY_STMT_UNITS,
+    LY_STMT_UNITS,              /**< in lysc_ext_substmt::storage stored as a pointer to `const char *` (cardinality < #LY_STMT_CARD_SOME)
+                                     or as a pointer to a [sized array](@ref sizedarrays) `const char **` */
     LY_STMT_DEFAULT,
-    LY_STMT_TYPE,
+    LY_STMT_TYPE,               /**< in lysc_ext_substmt::storage stored as a pointer to `struct lysc_type *` (cardinality < #LY_STMT_CARD_SOME)
+                                     or as a pointer to a [sized array](@ref sizedarrays) `struct lysc_type **` */
 
     LY_STMT_ACTION,
     LY_STMT_ANYDATA,
@@ -196,7 +198,8 @@
     LY_STMT_FRACTION_DIGITS,
     LY_STMT_GROUPING,
     LY_STMT_IDENTITY,
-    LY_STMT_IF_FEATURE,
+    LY_STMT_IF_FEATURE,         /**< in lysc_ext_substmt::storage stored as a pointer to `struct lysc_iffeature` (cardinality < #LY_STMT_CARD_SOME)
+                                     or as a pointer to a [sized array](@ref sizedarrays) `struct lysc_iffeature *` */
     LY_STMT_IMPORT,
     LY_STMT_INCLUDE,
     LY_STMT_INPUT,
@@ -1114,14 +1117,14 @@
  * @brief YANG extension instance
  */
 struct lysc_ext_instance {
-    struct lysc_ext *def;            /**< pointer to the extension definition */
+    uint32_t insubstmt_index;        /**< in case the instance is in a substatement that can appear multiple times,
+                                          this identifies the index of the substatement for this extension instance */
     struct lys_module *module;       /**< module where the extension instantiated is defined */
+    struct lysc_ext *def;            /**< pointer to the extension definition */
     void *parent;                    /**< pointer to the parent element holding the extension instance(s), use
                                           ::lysc_ext_instance#parent_type to access the schema element */
     const char *argument;            /**< optional value of the extension's argument */
     LYEXT_SUBSTMT insubstmt;         /**< value identifying placement of the extension instance */
-    uint32_t insubstmt_index;        /**< in case the instance is in a substatement that can appear multiple times,
-                                          this identifies the index of the substatement for this extension instance */
     LYEXT_PARENT parent_type;        /**< type of the parent structure */
     struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
     void *data;                      /**< private plugins's data, not used by libyang */
diff --git a/src/tree_schema_compile.c b/src/tree_schema_compile.c
index 25e9ffa..d680606 100644
--- a/src/tree_schema_compile.c
+++ b/src/tree_schema_compile.c
@@ -470,7 +470,17 @@
                      LY_EVALID);
 
     if (ext->def->plugin && ext->def->plugin->compile) {
+        lysc_update_path(ctx, ext->parent_type == LYEXT_PAR_NODE ? (struct lysc_node*)ext->parent : NULL, "{extension}");
+        lysc_update_path(ctx, NULL, ext_p->name );
+        if (ext->argument) {
+            lysc_update_path(ctx, (struct lysc_node*)ext, ext->argument);
+        }
         LY_CHECK_RET(ext->def->plugin->compile(ctx, ext_p, ext),LY_EVALID);
+        if (ext->argument) {
+            lysc_update_path(ctx, NULL, NULL);
+        }
+        lysc_update_path(ctx, NULL, NULL);
+        lysc_update_path(ctx, NULL, NULL);
     }
 
     return LY_SUCCESS;
@@ -6652,7 +6662,6 @@
     unsigned int u;
     struct lysp_stmt *stmt;
     void *parsed = NULL, **compiled = NULL;
-    struct ly_set mandatory_stmts = {0};
 
     /* check for invalid substatements */
     for (stmt = ext->child; stmt; stmt = stmt->next) {
@@ -6662,32 +6671,56 @@
             }
         }
         if (!substmts[u].stmt) {
-            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG, "Invalid keyword \"%s\" as a child of \"%s\" extension instance.",
-                   stmt->stmt, ext->name);
+            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG, "Invalid keyword \"%s\" as a child of \"%s%s%s\" extension instance.",
+                   stmt->stmt, ext->name, ext->argument ? " " : "", ext->argument ? ext->argument : "");
             goto cleanup;
         }
-        if (substmts[u].cardinality == LY_STMT_CARD_MAND || substmts[u].cardinality == LY_STMT_CARD_SOME) {
-            ly_set_add(&mandatory_stmts, &substmts[u], LY_SET_OPT_USEASLIST);
-        }
     }
 
+    /* TODO store inherited data, e.g. status first, but mark them somehow to allow to overwrite them and not detect duplicity */
+
     /* keep order of the processing the same as the order in the defined substmts,
      * the order is important for some of the statements depending on others (e.g. type needs status and units) */
     for (u = 0; substmts[u].stmt; ++u) {
+        int stmt_present = 0;
+
         for (stmt = ext->child; stmt; stmt = stmt->next) {
             if (substmts[u].stmt != stmt->kw) {
                 continue;
             }
 
+            stmt_present = 1;
             if (substmts[u].storage) {
                 switch (stmt->kw) {
+                case LY_STMT_STATUS:
+                    assert(substmts[u].cardinality < LY_STMT_CARD_SOME);
+                    LY_CHECK_ERR_GOTO(r = lysp_stmt_parse(ctx, stmt, stmt->kw, &substmts[u].storage, /* TODO */ NULL), ret = r, cleanup);
+                    break;
+                case LY_STMT_UNITS: {
+                    const char **units;
+
+                    if (substmts[u].cardinality < LY_STMT_CARD_SOME) {
+                        /* single item */
+                        if (*((const char **)substmts[u].storage)) {
+                            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_DUPSTMT, stmt->stmt);
+                            goto cleanup;
+                        }
+                        units = (const char **)substmts[u].storage;
+                    } else {
+                        /* sized array */
+                        const char ***units_array = (const char ***)substmts[u].storage;
+                        LY_ARRAY_NEW_GOTO(ctx->ctx, *units_array, units, ret, cleanup);
+                    }
+                    *units = lydict_insert(ctx->ctx, stmt->arg, 0);
+                    break;
+                }
                 case LY_STMT_TYPE: {
                     uint16_t *flags = lys_compile_extension_instance_storage(LY_STMT_STATUS, substmts);
                     const char **units = lys_compile_extension_instance_storage(LY_STMT_UNITS, substmts);
 
                     if (substmts[u].cardinality < LY_STMT_CARD_SOME) {
                         /* single item */
-                        if (*(struct lysc_type**)substmts->storage) {
+                        if (*(struct lysc_type**)substmts[u].storage) {
                             LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_DUPSTMT, stmt->stmt);
                             goto cleanup;
                         }
@@ -6699,7 +6732,7 @@
                         compiled = (void*)type;
                     }
 
-                    LY_CHECK_ERR_GOTO(r = lysp_stmt_parse(ctx, stmt, stmt->kw, &parsed), ret = r, cleanup);
+                    LY_CHECK_ERR_GOTO(r = lysp_stmt_parse(ctx, stmt, stmt->kw, &parsed, NULL), ret = r, cleanup);
                     LY_CHECK_ERR_GOTO(r = lys_compile_type(ctx, ext->parent_type == LYEXT_PAR_NODE ? ((struct lysc_node*)ext->parent)->sp : NULL,
                                       flags ? *flags : 0, ctx->mod_def->parsed, ext->name, parsed, (struct lysc_type**)compiled,
                                       units && !*units ? units : NULL), lysp_type_free(ctx->ctx, parsed); free(parsed); ret = r, cleanup);
@@ -6707,33 +6740,44 @@
                     free(parsed);
                     break;
                 }
-                /* TODO support other substatements (parse stmt to lysp and then compile lysp to lysc) */
+                case LY_STMT_IF_FEATURE: {
+                    struct lysc_iffeature *iff = NULL;
+
+                    if (substmts[u].cardinality < LY_STMT_CARD_SOME) {
+                        /* single item */
+                        if (((struct lysc_iffeature*)substmts[u].storage)->features) {
+                            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_DUPSTMT, stmt->stmt);
+                            goto cleanup;
+                        }
+                        iff = (struct lysc_iffeature*)substmts[u].storage;
+                    } else {
+                        /* sized array */
+                        struct lysc_iffeature **iffs = (struct lysc_iffeature**)substmts[u].storage;
+                        LY_ARRAY_NEW_GOTO(ctx->ctx, *iffs, iff, ret, cleanup);
+                    }
+                    LY_CHECK_ERR_GOTO(r = lys_compile_iffeature(ctx, &stmt->arg, iff), ret = r, cleanup);
+                    break;
+                }
+                /* TODO support other substatements (parse stmt to lysp and then compile lysp to lysc),
+                 * also note that in many statements their extensions are not taken into account  */
                 default:
-                    LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG, "Statement \"%s\" is not supported as an extension (found in \"%s\") substatement.",
-                           stmt->stmt, ext->name);
+                    LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG, "Statement \"%s\" is not supported as an extension (found in \"%s%s%s\") substatement.",
+                           stmt->stmt, ext->name, ext->argument ? " " : "", ext->argument ? ext->argument : "");
                     goto cleanup;
                 }
             }
-
-            if (substmts[u].cardinality == LY_STMT_CARD_MAND || substmts[u].cardinality == LY_STMT_CARD_SOME) {
-                int i = ly_set_contains(&mandatory_stmts, &substmts[u]);
-                if (i != -1) {
-                    ly_set_rm_index(&mandatory_stmts, i, NULL);
-                }
-            }
         }
-    }
 
-    if (mandatory_stmts.count) {
-        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_MISSTMT, "type", ext->name);
-        goto cleanup;
+        if ((substmts[u].cardinality == LY_STMT_CARD_MAND || substmts[u].cardinality == LY_STMT_CARD_SOME) && !stmt_present) {
+            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG, "Missing mandatory keyword \"%s\" as a child of \"%s%s%s\".",
+                   ly_stmt2str(substmts[u].stmt), ext->name, ext->argument ? " " : "", ext->argument ? ext->argument : "");
+            goto cleanup;
+        }
     }
 
     ret = LY_SUCCESS;
 
 cleanup:
-    ly_set_erase(&mandatory_stmts, NULL);
-
     return ret;
 }
 
diff --git a/src/tree_schema_free.c b/src/tree_schema_free.c
index 3535414..790b89f 100644
--- a/src/tree_schema_free.c
+++ b/src/tree_schema_free.c
@@ -918,6 +918,7 @@
             }
             break;
         case LY_STMT_STATUS:
+        case LY_STMT_CONFIG:
             /* nothing to do */
             break;
         case LY_STMT_IF_FEATURE: {
diff --git a/src/tree_schema_internal.h b/src/tree_schema_internal.h
index c9c6134..5ca89c5 100644
--- a/src/tree_schema_internal.h
+++ b/src/tree_schema_internal.h
@@ -710,7 +710,10 @@
  */
 void lysp_type_free(struct ly_ctx *ctx, struct lysp_type *type);
 
-LY_ERR lysp_stmt_parse(struct lysc_ctx *ctx, const struct lysp_stmt *stmt, enum ly_stmt kw, void **result);
+/**
+ * @param[in,out] exts [sized array](@ref sizedarrays) For extension instances in case of statements that do not store extension instances in their own list.
+ */
+LY_ERR lysp_stmt_parse(struct lysc_ctx *ctx, const struct lysp_stmt *stmt, enum ly_stmt kw, void **result, struct lysp_ext_instance **exts);
 
 /**
  * @brief Free the compiled type structure.
diff --git a/tests/features/CMakeLists.txt b/tests/features/CMakeLists.txt
index 20c5d94..6e4a366 100644
--- a/tests/features/CMakeLists.txt
+++ b/tests/features/CMakeLists.txt
@@ -1,8 +1,10 @@
 set(local_tests
     features_types
+    features_metadata
     features_nacm)
 set(local_tests_wraps
     " "
+    " "
     " ")
 set(tests ${tests} ${local_tests} PARENT_SCOPE)
 set(tests_wraps ${tests_wraps} ${local_tests_wraps} PARENT_SCOPE)
diff --git a/tests/features/test_metadata.c b/tests/features/test_metadata.c
new file mode 100644
index 0000000..9c60e37
--- /dev/null
+++ b/tests/features/test_metadata.c
@@ -0,0 +1,186 @@
+/*
+ * @file test_metadata.c
+ * @author: Radek Krejci <rkrejci@cesnet.cz>
+ * @brief unit tests for Metadata extension (annotation) support
+ *
+ * Copyright (c) 2019 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#include "tests/config.h"
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+#include <stdio.h>
+#include <string.h>
+
+#include "../../src/libyang.h"
+#include "../../src/plugins_exts_metadata.h"
+
+#define BUFSIZE 1024
+char logbuf[BUFSIZE] = {0};
+int store = -1; /* negative for infinite logging, positive for limited logging */
+
+struct state_s {
+    void *func;
+    struct ly_ctx *ctx;
+};
+
+/* set to 0 to printing error messages to stderr instead of checking them in code */
+#define ENABLE_LOGGER_CHECKING 1
+
+#if ENABLE_LOGGER_CHECKING
+static void
+logger(LY_LOG_LEVEL level, const char *msg, const char *path)
+{
+    (void) level; /* unused */
+    if (store) {
+        if (path && path[0]) {
+            snprintf(logbuf, BUFSIZE - 1, "%s %s", msg, path);
+        } else {
+            strncpy(logbuf, msg, BUFSIZE - 1);
+        }
+        if (store > 0) {
+            --store;
+        }
+    }
+}
+#endif
+
+static int
+setup(void **state)
+{
+    struct state_s *s;
+
+    s = calloc(1, sizeof *s);
+    assert_non_null(s);
+
+#if ENABLE_LOGGER_CHECKING
+    ly_set_log_clb(logger, 1);
+#endif
+
+    assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, 0, &s->ctx));
+    *state = s;
+
+    return 0;
+}
+
+static int
+teardown(void **state)
+{
+    struct state_s *s = (struct state_s*)(*state);
+
+#if ENABLE_LOGGER_CHECKING
+    if (s->func) {
+        fprintf(stderr, "%s\n", logbuf);
+    }
+#endif
+
+    ly_ctx_destroy(s->ctx, NULL);
+    free(s);
+
+    return 0;
+}
+
+void
+logbuf_clean(void)
+{
+    logbuf[0] = '\0';
+}
+
+#if ENABLE_LOGGER_CHECKING
+#   define logbuf_assert(str) assert_string_equal(logbuf, str)
+#else
+#   define logbuf_assert(str)
+#endif
+
+static void
+test_yang(void **state)
+{
+    struct state_s *s = (struct state_s*)(*state);
+    s->func = test_yang;
+
+    struct lys_module *mod;
+    struct lysc_ext_instance *e;
+    struct lyext_metadata *ant;
+
+    const char *data = "module a {yang-version 1.1; namespace urn:tests:extensions:metadata:a; prefix a;"
+            "import ietf-yang-metadata {prefix md;}"
+            "feature f;"
+            "md:annotation x {"
+            "  description \"test\";"
+            "  if-feature f;"
+            "  reference \"test\";"
+            "  status \"current\";"
+            "  type uint8;"
+            "  units meters;"
+            "}}";
+    assert_non_null(mod = lys_parse_mem(s->ctx, data, LYS_IN_YANG));
+    assert_int_equal(1, LY_ARRAY_SIZE(mod->compiled->exts));
+    e = &mod->compiled->exts[0];
+    assert_non_null(ant = (struct lyext_metadata*)e->data);
+    assert_string_equal("meters", ant->units);
+
+    /* invalid */
+    /* missing mandatory type substatement */
+    data = "module aa {yang-version 1.1; namespace urn:tests:extensions:metadata:aa; prefix aa;"
+            "import ietf-yang-metadata {prefix md;}"
+            "md:annotation aa;}";
+    assert_null(lys_parse_mem(s->ctx, data, LYS_IN_YANG));
+    logbuf_assert("Missing mandatory keyword \"type\" as a child of \"md:annotation aa\". /aa:{extension='md:annotation'}/aa");
+
+    /* not allowed substatement */
+    data = "module aa {yang-version 1.1; namespace urn:tests:extensions:metadata:aa; prefix aa;"
+            "import ietf-yang-metadata {prefix md;}"
+            "md:annotation aa {default x;}}";
+    assert_null(lys_parse_mem(s->ctx, data, LYS_IN_YANG));
+    logbuf_assert("Invalid keyword \"default\" as a child of \"md:annotation aa\" extension instance. /aa:{extension='md:annotation'}/aa");
+
+    /* invalid cardinality of units substatement */
+    data = "module aa {yang-version 1.1; namespace urn:tests:extensions:metadata:aa; prefix aa;"
+            "import ietf-yang-metadata {prefix md;}"
+            "md:annotation aa {type string; units x; units y;}}";
+    assert_null(lys_parse_mem(s->ctx, data, LYS_IN_YANG));
+    logbuf_assert("Duplicate keyword \"units\". /aa:{extension='md:annotation'}/aa");
+
+    /* invalid cardinality of status substatement */
+    data = "module aa {yang-version 1.1; namespace urn:tests:extensions:metadata:aa; prefix aa;"
+            "import ietf-yang-metadata {prefix md;}"
+            "md:annotation aa {type string; status current; status obsolete;}}";
+    assert_null(lys_parse_mem(s->ctx, data, LYS_IN_YANG));
+    logbuf_assert("Duplicate keyword \"status\". /aa:{extension='md:annotation'}/aa");
+
+    /* invalid cardinality of status substatement */
+    data = "module aa {yang-version 1.1; namespace urn:tests:extensions:metadata:aa; prefix aa;"
+            "import ietf-yang-metadata {prefix md;}"
+            "md:annotation aa {type string; type uint8;}}";
+    assert_null(lys_parse_mem(s->ctx, data, LYS_IN_YANG));
+    logbuf_assert("Duplicate keyword \"type\". /aa:{extension='md:annotation'}/aa");
+
+    /* duplication of the same annotation */
+    data = "module aa {yang-version 1.1; namespace urn:tests:extensions:metadata:aa; prefix aa;"
+            "import ietf-yang-metadata {prefix md;}"
+            "md:annotation aa {type string;} md:annotation aa {type uint8;}}";
+    assert_null(lys_parse_mem(s->ctx, data, LYS_IN_YANG));
+    logbuf_assert("Extension plugin \"libyang 2 - metadata, version 1\": "
+            "Extension md:annotation is instantiated multiple times.) /aa:{extension='md:annotation'}/aa");
+
+    s->func = NULL;
+}
+
+int main(void)
+{
+    const struct CMUnitTest tests[] = {
+        cmocka_unit_test_setup_teardown(test_yang, setup, teardown),
+    };
+
+    return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/features/test_nacm.c b/tests/features/test_nacm.c
index 87882da..6ae1900 100644
--- a/tests/features/test_nacm.c
+++ b/tests/features/test_nacm.c
@@ -136,14 +136,14 @@
             "nacm:default-deny-all;}";
     assert_null(lys_parse_mem(s->ctx, data, LYS_IN_YANG));
     logbuf_assert("Extension plugin \"libyang 2 - NACM, version 1\": "
-            "Extension nacm:default-deny-all is allowed only in a data nodes, but it is placed in \"module\" statement.) /");
+            "Extension nacm:default-deny-all is allowed only in a data nodes, but it is placed in \"module\" statement.) /aa:{extension='nacm:default-deny-all'}");
 
     data = "module aa {yang-version 1.1; namespace urn:tests:extensions:nacm:aa; prefix en;"
             "import ietf-netconf-acm {revision-date 2018-02-14; prefix nacm;}"
             "leaf l { type string; nacm:default-deny-all; nacm:default-deny-write;}}";
     assert_null(lys_parse_mem(s->ctx, data, LYS_IN_YANG));
     logbuf_assert("Extension plugin \"libyang 2 - NACM, version 1\": "
-            "Extension nacm:default-deny-write is mixed with nacm:default-deny-all.) /aa:l");
+            "Extension nacm:default-deny-write is mixed with nacm:default-deny-all.) /aa:l/{extension='nacm:default-deny-write'}");
 
     s->func = NULL;
 }
@@ -181,14 +181,14 @@
             "notification notif {nacm:default-deny-write;}}";
     assert_null(lys_parse_mem(s->ctx, data, LYS_IN_YANG));
     logbuf_assert("Extension plugin \"libyang 2 - NACM, version 1\": "
-            "Extension nacm:default-deny-write is not allowed in Notification statement.) /aa:notif");
+            "Extension nacm:default-deny-write is not allowed in Notification statement.) /aa:notif/{extension='nacm:default-deny-write'}");
 
     data = "module aa {yang-version 1.1; namespace urn:tests:extensions:nacm:aa; prefix en;"
             "import ietf-netconf-acm {revision-date 2018-02-14; prefix nacm;}"
             "leaf l { type string; nacm:default-deny-write; nacm:default-deny-write;}}";
     assert_null(lys_parse_mem(s->ctx, data, LYS_IN_YANG));
     logbuf_assert("Extension plugin \"libyang 2 - NACM, version 1\": "
-            "Extension nacm:default-deny-write is instantiated multiple times.) /aa:l");
+            "Extension nacm:default-deny-write is instantiated multiple times.) /aa:l/{extension='nacm:default-deny-write'}");
 
     s->func = NULL;
 }