schema compile CHANGE Notifications support
diff --git a/tests/src/test_parser_yang.c b/tests/src/test_parser_yang.c
index 92e4ec8..2c68caf 100644
--- a/tests/src/test_parser_yang.c
+++ b/tests/src/test_parser_yang.c
@@ -1943,6 +1943,66 @@
 }
 
 static void
+test_notification(void **state)
+{
+    *state = test_notification;
+
+    struct ly_parser_ctx ctx = {0};
+    struct lysp_notif *notifs = NULL;
+    struct lysp_node_container *c = 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_version = 2; /* simulate YANG 1.1 */
+
+    /* invalid cardinality */
+#define TEST_DUP(MEMBER, VALUE1, VALUE2) \
+    str = "func {" MEMBER" "VALUE1";"MEMBER" "VALUE2";} ..."; \
+    assert_int_equal(LY_EVALID, parse_notif(&ctx, &str, NULL, &notifs)); \
+    logbuf_assert("Duplicate keyword \""MEMBER"\". Line number 1."); \
+    FREE_ARRAY(ctx.ctx, notifs, lysp_notif_free); notifs = NULL;
+
+    TEST_DUP("description", "text1", "text2");
+    TEST_DUP("reference", "1", "2");
+    TEST_DUP("status", "current", "obsolete");
+#undef TEST_DUP
+
+    /* full content */
+    str = "top;";
+    assert_int_equal(LY_SUCCESS, parse_container(&ctx, &str, NULL, (struct lysp_node**)&c));
+    str = "ntf {anydata a1; anyxml a2; choice ch; container c; description test; grouping grp; if-feature f; leaf l {type int8;}"
+          "leaf-list ll {type int8;} list li; must 1; reference test; status current; typedef mytype {type int8;} uses grp; m:ext;}";
+    assert_int_equal(LY_SUCCESS, parse_notif(&ctx, &str, (struct lysp_node*)c, &notifs));
+    assert_non_null(notifs);
+    assert_int_equal(LYS_NOTIF, notifs->nodetype);
+    assert_string_equal("ntf", notifs->name);
+    assert_string_equal("test", notifs->dsc);
+    assert_non_null(notifs->exts);
+    assert_non_null(notifs->iffeatures);
+    assert_string_equal("test", notifs->ref);
+    assert_non_null(notifs->groupings);
+    assert_non_null(notifs->typedefs);
+    assert_non_null(notifs->musts);
+    assert_non_null(notifs->data);
+    assert_int_equal(LYS_STATUS_CURR, notifs->flags);
+
+    ly_set_erase(&ctx.tpdfs_nodes, NULL);
+    FREE_ARRAY(ctx.ctx, notifs, lysp_notif_free); notifs = NULL;
+
+    /* invalid content */
+    str = "ntf {config true} ...";
+    assert_int_equal(LY_EVALID, parse_notif(&ctx, &str, NULL, &notifs));
+    logbuf_assert("Invalid keyword \"config\" as a child of \"notification\". Line number 1.");
+    FREE_ARRAY(ctx.ctx, notifs, lysp_notif_free); notifs = NULL;
+
+    *state = NULL;
+    lysp_node_free(ctx.ctx, (struct lysp_node*)c);
+    ly_ctx_destroy(ctx.ctx, NULL);
+}
+
+static void
 test_uses(void **state)
 {
     *state = test_uses;
@@ -2061,6 +2121,7 @@
         cmocka_unit_test_setup_teardown(test_anydata, logger_setup, logger_teardown),
         cmocka_unit_test_setup_teardown(test_anyxml, logger_setup, logger_teardown),
         cmocka_unit_test_setup_teardown(test_action, logger_setup, logger_teardown),
+        cmocka_unit_test_setup_teardown(test_notification, logger_setup, logger_teardown),
         cmocka_unit_test_setup_teardown(test_grouping, logger_setup, logger_teardown),
         cmocka_unit_test_setup_teardown(test_uses, logger_setup, logger_teardown),
         cmocka_unit_test_setup_teardown(test_augment, logger_setup, logger_teardown),
diff --git a/tests/src/test_tree_schema.c b/tests/src/test_tree_schema.c
index 0a91f03..48fdfc1 100644
--- a/tests/src/test_tree_schema.c
+++ b/tests/src/test_tree_schema.c
@@ -135,12 +135,10 @@
     rpc = (const struct lysc_action*)node;
     assert_non_null(node = lys_getnext(node, NULL, mod->compiled, 0));
     assert_string_equal("i", node->name);
-    /* TODO Notifications
     assert_non_null(node = lys_getnext(node, NULL, mod->compiled, 0));
     assert_string_equal("j", node->name);
     assert_non_null(node = lys_getnext(node, NULL, mod->compiled, 0));
     assert_string_equal("k", node->name);
-    */
     assert_null(node = lys_getnext(node, NULL, mod->compiled, 0));
     /* Inside container */
     assert_non_null(node = lys_getnext(node, (const struct lysc_node*)cont, mod->compiled, 0));
@@ -159,10 +157,8 @@
     assert_string_equal("seven", node->name);
     assert_non_null(node = lys_getnext(node, (const struct lysc_node*)cont, mod->compiled, 0));
     assert_string_equal("eight", node->name);
-    /* TODO Notifications
     assert_non_null(node = lys_getnext(node, (const struct lysc_node*)cont, mod->compiled, 0));
     assert_string_equal("nine", node->name);
-    */
     assert_null(node = lys_getnext(node, (const struct lysc_node*)cont, mod->compiled, 0));
     /* Inside RPC */
     assert_non_null(node = lys_getnext(node, (const struct lysc_node*)rpc, mod->compiled, 0));
diff --git a/tests/src/test_tree_schema_compile.c b/tests/src/test_tree_schema_compile.c
index 8f50ead..b098ead 100644
--- a/tests/src/test_tree_schema_compile.c
+++ b/tests/src/test_tree_schema_compile.c
@@ -521,15 +521,24 @@
     assert_string_equal("10", ll->dflts[0]);
     assert_int_equal(LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_USER, ll->flags);
 
-    /* ordered-by is ignored for state data, RPC/action output parameters and notification content
-     * TODO test also, RPC output parameters and notification content */
+    /* ordered-by is ignored for state data, RPC/action output parameters and notification content */
     assert_non_null(mod = lys_parse_mem(ctx, "module d {yang-version 1.1;namespace urn:d;prefix d;"
                                         "leaf-list ll {config false; type string; ordered-by user;}}", LYS_IN_YANG));
     /* but warning is present: */
-    logbuf_assert("The ordered-by statement is ignored in lists representing state data, RPC/action output parameters or notification content ().");
+    logbuf_assert("The ordered-by statement is ignored in lists representing state data ().");
     assert_non_null(mod->compiled);
     assert_non_null((ll = (struct lysc_node_leaflist*)mod->compiled->data));
     assert_int_equal(LYS_CONFIG_R | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM | LYS_SET_CONFIG, ll->flags);
+    logbuf_clean();
+
+    assert_non_null(mod = lys_parse_mem(ctx, "module e {yang-version 1.1;namespace urn:e;prefix e;"
+                                        "rpc oper {output {leaf-list ll {type string; ordered-by user;}}}}", LYS_IN_YANG));
+    logbuf_assert("The ordered-by statement is ignored in lists representing RPC/action output parameters ().");
+    logbuf_clean();
+
+    assert_non_null(mod = lys_parse_mem(ctx, "module f {yang-version 1.1;namespace urn:f;prefix f;"
+                                        "notification event {leaf-list ll {type string; ordered-by user;}}}", LYS_IN_YANG));
+    logbuf_assert("The ordered-by statement is ignored in lists representing notification content ().");
 
     /* invalid */
     assert_null(lys_parse_mem(ctx, "module aa {namespace urn:aa;prefix aa;leaf-list ll {type empty;}}", LYS_IN_YANG));
@@ -817,10 +826,93 @@
     logbuf_assert("Duplicate identifier \"y\" of data definition/RPC/action/Notification statement.");
     assert_null(lys_parse_mem(ctx, "module dd {yang-version 1.1; namespace urn:dd;prefix dd;container c {action z; action z;}}", LYS_IN_YANG));
     logbuf_assert("Duplicate identifier \"z\" of data definition/RPC/action/Notification statement.");
-    ly_ctx_set_module_imp_clb(ctx, test_imp_clb, "submodule eesub {belongs-to ee {prefix ee;} rpc w;}");
+    ly_ctx_set_module_imp_clb(ctx, test_imp_clb, "submodule eesub {belongs-to ee {prefix ee;} notification w;}");
     assert_null(lys_parse_mem(ctx, "module ee {yang-version 1.1; namespace urn:ee;prefix ee;include eesub; rpc w;}", LYS_IN_YANG));
     logbuf_assert("Duplicate identifier \"w\" of data definition/RPC/action/Notification statement.");
 
+    assert_null(lys_parse_mem(ctx, "module ff {yang-version 1.1; namespace urn:ff;prefix ff; rpc test {input {container a {leaf b {type string;}}}}"
+                              "augment /test/input/a {action invalid {input {leaf x {type string;}}}}}", LYS_IN_YANG));
+    logbuf_assert("Action \"invalid\" is placed inside another RPC/action.");
+
+    assert_null(lys_parse_mem(ctx, "module gg {yang-version 1.1; namespace urn:gg;prefix gg; notification test {container a {leaf b {type string;}}}"
+                              "augment /test/a {action invalid {input {leaf x {type string;}}}}}", LYS_IN_YANG));
+    logbuf_assert("Action \"invalid\" is placed inside Notification.");
+
+    assert_null(lys_parse_mem(ctx, "module hh {yang-version 1.1; namespace urn:hh;prefix hh; notification test {container a {uses grp;}}"
+                              "grouping grp {action invalid {input {leaf x {type string;}}}}}", LYS_IN_YANG));
+    logbuf_assert("Action \"invalid\" is placed inside Notification.");
+
+    *state = NULL;
+    ly_ctx_destroy(ctx, NULL);
+}
+
+static void
+test_notification(void **state)
+{
+    *state = test_notification;
+
+    struct ly_ctx *ctx;
+    struct lys_module *mod;
+    const struct lysc_notif *notif;
+
+    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;"
+                                        "notification a1 {leaf x {type int8;}} notification a2;}", LYS_IN_YANG));
+    notif = mod->compiled->notifs;
+    assert_non_null(notif);
+    assert_int_equal(2, LY_ARRAY_SIZE(notif));
+    assert_int_equal(LYS_NOTIF, notif->nodetype);
+    assert_int_equal(LYS_STATUS_CURR, notif->flags);
+    assert_string_equal("a1", notif->name);
+    assert_non_null(notif->data);
+    assert_string_equal("x", notif->data->name);
+    assert_int_equal(LYS_NOTIF, notif[1].nodetype);
+    assert_int_equal(LYS_STATUS_CURR, notif[1].flags);
+    assert_string_equal("a2", notif[1].name);
+    assert_null(notif[1].data);
+
+    assert_non_null(mod = lys_parse_mem(ctx, "module b {yang-version 1.1; namespace urn:b;prefix b; container top {"
+                                        "notification b1 {leaf x {type int8;}} notification b2;}}", LYS_IN_YANG));
+    notif = lysc_node_notifs(mod->compiled->data);
+    assert_non_null(notif);
+    assert_int_equal(2, LY_ARRAY_SIZE(notif));
+    assert_int_equal(LYS_NOTIF, notif->nodetype);
+    assert_int_equal(LYS_STATUS_CURR, notif->flags);
+    assert_string_equal("b1", notif->name);
+    assert_non_null(notif->data);
+    assert_string_equal("x", notif->data->name);
+    assert_int_equal(LYS_NOTIF, notif[1].nodetype);
+    assert_int_equal(LYS_STATUS_CURR, notif[1].flags);
+    assert_string_equal("b2", notif[1].name);
+    assert_null(notif[1].data);
+
+    /* invalid */
+    assert_null(lys_parse_mem(ctx, "module aa {namespace urn:aa;prefix aa;container top {notification x;}}", LYS_IN_YANG));
+    logbuf_assert("Invalid keyword \"notification\" as a child of \"container\" - the statement is allowed only in YANG 1.1 modules. Line number 1.");
+
+    assert_null(lys_parse_mem(ctx, "module bb {namespace urn:bb;prefix bb;leaf x{type string;} notification x;}", LYS_IN_YANG));
+    logbuf_assert("Duplicate identifier \"x\" of data definition/RPC/action/Notification statement.");
+    assert_null(lys_parse_mem(ctx, "module cc {yang-version 1.1; namespace urn:cc;prefix cc;container c {leaf y {type string;} notification y;}}", LYS_IN_YANG));
+    logbuf_assert("Duplicate identifier \"y\" of data definition/RPC/action/Notification statement.");
+    assert_null(lys_parse_mem(ctx, "module dd {yang-version 1.1; namespace urn:dd;prefix dd;container c {notification z; notification z;}}", LYS_IN_YANG));
+    logbuf_assert("Duplicate identifier \"z\" of data definition/RPC/action/Notification statement.");
+    ly_ctx_set_module_imp_clb(ctx, test_imp_clb, "submodule eesub {belongs-to ee {prefix ee;} rpc w;}");
+    assert_null(lys_parse_mem(ctx, "module ee {yang-version 1.1; namespace urn:ee;prefix ee;include eesub; notification w;}", LYS_IN_YANG));
+    logbuf_assert("Duplicate identifier \"w\" of data definition/RPC/action/Notification statement.");
+
+    assert_null(lys_parse_mem(ctx, "module ff {yang-version 1.1; namespace urn:ff;prefix ff; rpc test {input {container a {leaf b {type string;}}}}"
+                              "augment /test/input/a {notification invalid {leaf x {type string;}}}}", LYS_IN_YANG));
+    logbuf_assert("Notification \"invalid\" is placed inside RPC/action.");
+
+    assert_null(lys_parse_mem(ctx, "module gg {yang-version 1.1; namespace urn:gg;prefix gg; notification test {container a {leaf b {type string;}}}"
+                              "augment /test/a {notification invalid {leaf x {type string;}}}}", LYS_IN_YANG));
+    logbuf_assert("Notification \"invalid\" is placed inside another Notification.");
+
+    assert_null(lys_parse_mem(ctx, "module hh {yang-version 1.1; namespace urn:hh;prefix hh; rpc test {input {container a {uses grp;}}}"
+                              "grouping grp {notification invalid {leaf x {type string;}}}}", LYS_IN_YANG));
+    logbuf_assert("Notification \"invalid\" is placed inside RPC/action.");
+
     *state = NULL;
     ly_ctx_destroy(ctx, NULL);
 }
@@ -3017,10 +3109,6 @@
                               "deviation /x {deviate replace {type empty;}}}", LYS_IN_YANG));
     logbuf_assert("Leaf-list of type \"empty\" is allowed only in YANG 1.1 modules.");
 
-    assert_null(lys_parse_mem(ctx, "module oo {yang-version 1.1; namespace urn:oo;prefix oo; rpc test {input {container a {leaf b {type string;}}}}"
-                              "augment /test/input/a {action invalid {input {leaf x {type string;}}}}}", LYS_IN_YANG));
-    logbuf_assert("Action \"invalid\" is placed inside another RPC/action.");
-
     *state = NULL;
     ly_ctx_destroy(ctx, NULL);
 }
@@ -3050,6 +3138,7 @@
         cmocka_unit_test_setup_teardown(test_node_choice, logger_setup, logger_teardown),
         cmocka_unit_test_setup_teardown(test_node_anydata, logger_setup, logger_teardown),
         cmocka_unit_test_setup_teardown(test_action, logger_setup, logger_teardown),
+        cmocka_unit_test_setup_teardown(test_notification, logger_setup, logger_teardown),
         cmocka_unit_test_setup_teardown(test_uses, logger_setup, logger_teardown),
         cmocka_unit_test_setup_teardown(test_refine, logger_setup, logger_teardown),
         cmocka_unit_test_setup_teardown(test_augment, logger_setup, logger_teardown),