tree schema NEW unique validation

Several other fixes in lyd_path()
and XML parser are included.
Tests also included.
diff --git a/tests/src/test_tree_schema_compile.c b/tests/src/test_tree_schema_compile.c
index d596455..68ef8b1 100644
--- a/tests/src/test_tree_schema_compile.c
+++ b/tests/src/test_tree_schema_compile.c
@@ -719,7 +719,7 @@
 
     assert_null(lys_parse_mem(ctx, "module hh {namespace urn:hh;prefix hh;"
                               "list l {key x; unique \"x y\";leaf x {type string;} leaf y {config false; type string;}}}", LYS_IN_YANG));
-    logbuf_assert("Unique statement \"x y\" refers to leafs with different config type. /hh:l");
+    logbuf_assert("Unique statement \"x y\" refers to leaves with different config type. /hh:l");
 
     assert_null(lys_parse_mem(ctx, "module ii {namespace urn:ii;prefix ii;"
                               "list l {key x; unique a:x;leaf x {type string;}}}", LYS_IN_YANG));
diff --git a/tests/src/test_validation.c b/tests/src/test_validation.c
index eca882c..ca699ae 100644
--- a/tests/src/test_validation.c
+++ b/tests/src/test_validation.c
@@ -128,6 +128,55 @@
                 "type empty;"
             "}"
         "}";
+    const char *schema_d =
+        "module d {"
+            "namespace urn:tests:d;"
+            "prefix d;"
+            "yang-version 1.1;"
+
+            "list lt {"
+                "key \"k\";"
+                "unique \"l1\";"
+                "leaf k {"
+                    "type string;"
+                "}"
+                "leaf l1 {"
+                    "type string;"
+                "}"
+            "}"
+            "list lt2 {"
+                "key \"k\";"
+                "unique \"cont/l2 l4\";"
+                "unique \"l5 l6\";"
+                "leaf k {"
+                    "type string;"
+                "}"
+                "container cont {"
+                    "leaf l2 {"
+                        "type string;"
+                    "}"
+                "}"
+                "leaf l4 {"
+                    "type string;"
+                "}"
+                "leaf l5 {"
+                    "type string;"
+                "}"
+                "leaf l6 {"
+                    "type string;"
+                "}"
+                "list lt3 {"
+                    "key \"kk\";"
+                    "unique \"l3\";"
+                    "leaf kk {"
+                        "type string;"
+                    "}"
+                    "leaf l3 {"
+                        "type string;"
+                    "}"
+                "}"
+            "}"
+        "}";
 
 #if ENABLE_LOGGER_CHECKING
     ly_set_log_clb(logger, 1);
@@ -137,6 +186,7 @@
     assert_non_null(lys_parse_mem(ctx, schema_a, LYS_IN_YANG));
     assert_non_null(lys_parse_mem(ctx, schema_b, LYS_IN_YANG));
     assert_non_null(lys_parse_mem(ctx, schema_c, LYS_IN_YANG));
+    assert_non_null(lys_parse_mem(ctx, schema_d, LYS_IN_YANG));
 
     return 0;
 }
@@ -284,12 +334,391 @@
     *state = NULL;
 }
 
+static void
+test_unique(void **state)
+{
+    *state = test_unique;
+
+    const char *data;
+    struct lyd_node *tree;
+
+    data =
+    "<lt xmlns=\"urn:tests:d\">"
+        "<k>val1</k>"
+        "<l1>same</l1>"
+    "</lt>"
+    "<lt xmlns=\"urn:tests:d\">"
+        "<k>val2</k>"
+    "</lt>";
+    assert_int_equal(LY_SUCCESS, lyd_parse_xml(ctx, data, LYD_OPT_VAL_DATA_ONLY, &tree));
+    assert_non_null(tree);
+    lyd_free_withsiblings(tree);
+
+    data =
+    "<lt xmlns=\"urn:tests:d\">"
+        "<k>val1</k>"
+        "<l1>same</l1>"
+    "</lt>"
+    "<lt xmlns=\"urn:tests:d\">"
+        "<k>val2</k>"
+        "<l1>not-same</l1>"
+    "</lt>";
+    assert_int_equal(LY_SUCCESS, lyd_parse_xml(ctx, data, LYD_OPT_VAL_DATA_ONLY, &tree));
+    assert_non_null(tree);
+    lyd_free_withsiblings(tree);
+
+    data =
+    "<lt xmlns=\"urn:tests:d\">"
+        "<k>val1</k>"
+        "<l1>same</l1>"
+    "</lt>"
+    "<lt xmlns=\"urn:tests:d\">"
+        "<k>val2</k>"
+        "<l1>same</l1>"
+    "</lt>";
+    assert_int_equal(LY_EVALID, lyd_parse_xml(ctx, data, LYD_OPT_VAL_DATA_ONLY, &tree));
+    assert_null(tree);
+    logbuf_assert("Unique data leaf(s) \"l1\" not satisfied in \"/d:lt[k='val1']\" and \"/d:lt[k='val2']\".");
+
+    /* now try with more instances */
+    data =
+    "<lt xmlns=\"urn:tests:d\">"
+        "<k>val1</k>"
+        "<l1>1</l1>"
+    "</lt>"
+    "<lt xmlns=\"urn:tests:d\">"
+        "<k>val2</k>"
+        "<l1>2</l1>"
+    "</lt>"
+    "<lt xmlns=\"urn:tests:d\">"
+        "<k>val3</k>"
+        "<l1>3</l1>"
+    "</lt>"
+    "<lt xmlns=\"urn:tests:d\">"
+        "<k>val4</k>"
+        "<l1>4</l1>"
+    "</lt>"
+    "<lt xmlns=\"urn:tests:d\">"
+        "<k>val5</k>"
+        "<l1>5</l1>"
+    "</lt>"
+    "<lt xmlns=\"urn:tests:d\">"
+        "<k>val6</k>"
+        "<l1>6</l1>"
+    "</lt>"
+    "<lt xmlns=\"urn:tests:d\">"
+        "<k>val7</k>"
+        "<l1>7</l1>"
+    "</lt>"
+    "<lt xmlns=\"urn:tests:d\">"
+        "<k>val8</k>"
+        "<l1>8</l1>"
+    "</lt>";
+    assert_int_equal(LY_SUCCESS, lyd_parse_xml(ctx, data, LYD_OPT_VAL_DATA_ONLY, &tree));
+    assert_non_null(tree);
+    lyd_free_withsiblings(tree);
+
+    data =
+    "<lt xmlns=\"urn:tests:d\">"
+        "<k>val1</k>"
+        "<l1>1</l1>"
+    "</lt>"
+    "<lt xmlns=\"urn:tests:d\">"
+        "<k>val2</k>"
+        "<l1>2</l1>"
+    "</lt>"
+    "<lt xmlns=\"urn:tests:d\">"
+        "<k>val3</k>"
+        "<l1>3</l1>"
+    "</lt>"
+    "<lt xmlns=\"urn:tests:d\">"
+        "<k>val4</k>"
+    "</lt>"
+    "<lt xmlns=\"urn:tests:d\">"
+        "<k>val5</k>"
+        "<l1>5</l1>"
+    "</lt>"
+    "<lt xmlns=\"urn:tests:d\">"
+        "<k>val6</k>"
+        "<l1>6</l1>"
+    "</lt>"
+    "<lt xmlns=\"urn:tests:d\">"
+        "<k>val7</k>"
+    "</lt>"
+    "<lt xmlns=\"urn:tests:d\">"
+        "<k>val8</k>"
+    "</lt>";
+    assert_int_equal(LY_SUCCESS, lyd_parse_xml(ctx, data, LYD_OPT_VAL_DATA_ONLY, &tree));
+    assert_non_null(tree);
+    lyd_free_withsiblings(tree);
+
+    data =
+    "<lt xmlns=\"urn:tests:d\">"
+        "<k>val1</k>"
+        "<l1>1</l1>"
+    "</lt>"
+    "<lt xmlns=\"urn:tests:d\">"
+        "<k>val2</k>"
+        "<l1>2</l1>"
+    "</lt>"
+    "<lt xmlns=\"urn:tests:d\">"
+        "<k>val3</k>"
+    "</lt>"
+    "<lt xmlns=\"urn:tests:d\">"
+        "<k>val4</k>"
+        "<l1>4</l1>"
+    "</lt>"
+    "<lt xmlns=\"urn:tests:d\">"
+        "<k>val5</k>"
+    "</lt>"
+    "<lt xmlns=\"urn:tests:d\">"
+        "<k>val6</k>"
+    "</lt>"
+    "<lt xmlns=\"urn:tests:d\">"
+        "<k>val7</k>"
+        "<l1>2</l1>"
+    "</lt>"
+    "<lt xmlns=\"urn:tests:d\">"
+        "<k>val8</k>"
+        "<l1>8</l1>"
+    "</lt>";
+    assert_int_equal(LY_EVALID, lyd_parse_xml(ctx, data, LYD_OPT_VAL_DATA_ONLY, &tree));
+    assert_null(tree);
+    logbuf_assert("Unique data leaf(s) \"l1\" not satisfied in \"/d:lt[k='val7']\" and \"/d:lt[k='val2']\".");
+
+    *state = NULL;
+}
+
+static void
+test_unique_nested(void **state)
+{
+    *state = test_unique_nested;
+
+    const char *data;
+    struct lyd_node *tree;
+
+    /* nested list uniquest are compared only with instances in the same parent list instance */
+    data =
+    "<lt2 xmlns=\"urn:tests:d\">"
+        "<k>val1</k>"
+        "<cont>"
+            "<l2>1</l2>"
+        "</cont>"
+        "<l4>1</l4>"
+    "</lt2>"
+    "<lt2 xmlns=\"urn:tests:d\">"
+        "<k>val2</k>"
+        "<cont>"
+            "<l2>2</l2>"
+        "</cont>"
+        "<l4>2</l4>"
+        "<lt3>"
+            "<kk>val1</kk>"
+            "<l3>1</l3>"
+        "</lt3>"
+        "<lt3>"
+            "<kk>val2</kk>"
+            "<l3>2</l3>"
+        "</lt3>"
+    "</lt2>"
+    "<lt2 xmlns=\"urn:tests:d\">"
+        "<k>val3</k>"
+        "<cont>"
+            "<l2>3</l2>"
+        "</cont>"
+        "<l4>3</l4>"
+        "<lt3>"
+            "<kk>val1</kk>"
+            "<l3>2</l3>"
+        "</lt3>"
+    "</lt2>"
+    "<lt2 xmlns=\"urn:tests:d\">"
+        "<k>val4</k>"
+        "<cont>"
+            "<l2>4</l2>"
+        "</cont>"
+        "<l4>4</l4>"
+        "<lt3>"
+            "<kk>val1</kk>"
+            "<l3>3</l3>"
+        "</lt3>"
+    "</lt2>"
+    "<lt2 xmlns=\"urn:tests:d\">"
+        "<k>val5</k>"
+        "<cont>"
+            "<l2>5</l2>"
+        "</cont>"
+        "<l4>5</l4>"
+        "<lt3>"
+            "<kk>val1</kk>"
+            "<l3>3</l3>"
+        "</lt3>"
+    "</lt2>";
+    assert_int_equal(LY_SUCCESS, lyd_parse_xml(ctx, data, LYD_OPT_VAL_DATA_ONLY | LYD_OPT_STRICT, &tree));
+    assert_non_null(tree);
+    lyd_free_withsiblings(tree);
+
+    data =
+    "<lt2 xmlns=\"urn:tests:d\">"
+        "<k>val1</k>"
+        "<cont>"
+            "<l2>1</l2>"
+        "</cont>"
+        "<l4>1</l4>"
+    "</lt2>"
+    "<lt2 xmlns=\"urn:tests:d\">"
+        "<k>val2</k>"
+        "<cont>"
+            "<l2>2</l2>"
+        "</cont>"
+        "<lt3>"
+            "<kk>val1</kk>"
+            "<l3>1</l3>"
+        "</lt3>"
+        "<lt3>"
+            "<kk>val2</kk>"
+            "<l3>2</l3>"
+        "</lt3>"
+        "<lt3>"
+            "<kk>val3</kk>"
+            "<l3>1</l3>"
+        "</lt3>"
+    "</lt2>"
+    "<lt2 xmlns=\"urn:tests:d\">"
+        "<k>val3</k>"
+        "<cont>"
+            "<l2>3</l2>"
+        "</cont>"
+        "<l4>1</l4>"
+        "<lt3>"
+            "<kk>val1</kk>"
+            "<l3>2</l3>"
+        "</lt3>"
+    "</lt2>"
+    "<lt2 xmlns=\"urn:tests:d\">"
+        "<k>val4</k>"
+        "<cont>"
+            "<l2>4</l2>"
+        "</cont>"
+        "<lt3>"
+            "<kk>val1</kk>"
+            "<l3>3</l3>"
+        "</lt3>"
+    "</lt2>"
+    "<lt2 xmlns=\"urn:tests:d\">"
+        "<k>val5</k>"
+        "<cont>"
+            "<l2>5</l2>"
+        "</cont>"
+        "<lt3>"
+            "<kk>val1</kk>"
+            "<l3>3</l3>"
+        "</lt3>"
+    "</lt2>";
+    assert_int_equal(LY_EVALID, lyd_parse_xml(ctx, data, LYD_OPT_VAL_DATA_ONLY, &tree));
+    assert_null(tree);
+    logbuf_assert("Unique data leaf(s) \"l3\" not satisfied in \"/d:lt2[k='val2']/lt3[kk='val3']\" and"
+                  " \"/d:lt2[k='val2']/lt3[kk='val1']\".");
+
+    data =
+    "<lt2 xmlns=\"urn:tests:d\">"
+        "<k>val1</k>"
+        "<cont>"
+            "<l2>1</l2>"
+        "</cont>"
+        "<l4>1</l4>"
+    "</lt2>"
+    "<lt2 xmlns=\"urn:tests:d\">"
+        "<k>val2</k>"
+        "<cont>"
+            "<l2>2</l2>"
+        "</cont>"
+        "<l4>2</l4>"
+    "</lt2>"
+    "<lt2 xmlns=\"urn:tests:d\">"
+        "<k>val3</k>"
+        "<cont>"
+            "<l2>3</l2>"
+        "</cont>"
+        "<l4>3</l4>"
+    "</lt2>"
+    "<lt2 xmlns=\"urn:tests:d\">"
+        "<k>val4</k>"
+        "<cont>"
+            "<l2>2</l2>"
+        "</cont>"
+        "<l4>2</l4>"
+    "</lt2>"
+    "<lt2 xmlns=\"urn:tests:d\">"
+        "<k>val5</k>"
+        "<cont>"
+            "<l2>5</l2>"
+        "</cont>"
+        "<l4>5</l4>"
+    "</lt2>";
+    assert_int_equal(LY_EVALID, lyd_parse_xml(ctx, data, LYD_OPT_VAL_DATA_ONLY, &tree));
+    assert_null(tree);
+    logbuf_assert("Unique data leaf(s) \"cont/l2 l4\" not satisfied in \"/d:lt2[k='val4']\" and \"/d:lt2[k='val2']\".");
+
+    data =
+    "<lt2 xmlns=\"urn:tests:d\">"
+        "<k>val1</k>"
+        "<cont>"
+            "<l2>1</l2>"
+        "</cont>"
+        "<l4>1</l4>"
+        "<l5>1</l5>"
+        "<l6>1</l6>"
+    "</lt2>"
+    "<lt2 xmlns=\"urn:tests:d\">"
+        "<k>val2</k>"
+        "<cont>"
+            "<l2>2</l2>"
+        "</cont>"
+        "<l4>1</l4>"
+        "<l5>1</l5>"
+    "</lt2>"
+    "<lt2 xmlns=\"urn:tests:d\">"
+        "<k>val3</k>"
+        "<cont>"
+            "<l2>3</l2>"
+        "</cont>"
+        "<l4>1</l4>"
+        "<l5>3</l5>"
+        "<l6>3</l6>"
+    "</lt2>"
+    "<lt2 xmlns=\"urn:tests:d\">"
+        "<k>val4</k>"
+        "<cont>"
+            "<l2>4</l2>"
+        "</cont>"
+        "<l4>1</l4>"
+        "<l6>1</l6>"
+    "</lt2>"
+    "<lt2 xmlns=\"urn:tests:d\">"
+        "<k>val5</k>"
+        "<cont>"
+            "<l2>5</l2>"
+        "</cont>"
+        "<l4>1</l4>"
+        "<l5>3</l5>"
+        "<l6>3</l6>"
+    "</lt2>";
+    assert_int_equal(LY_EVALID, lyd_parse_xml(ctx, data, LYD_OPT_VAL_DATA_ONLY, &tree));
+    assert_null(tree);
+    logbuf_assert("Unique data leaf(s) \"l5 l6\" not satisfied in \"/d:lt2[k='val5']\" and \"/d:lt2[k='val3']\".");
+
+    *state = NULL;
+}
+
 int main(void)
 {
     const struct CMUnitTest tests[] = {
         cmocka_unit_test_teardown(test_when, teardown_s),
         cmocka_unit_test_teardown(test_mandatory, teardown_s),
         cmocka_unit_test_teardown(test_minmax, teardown_s),
+        cmocka_unit_test_teardown(test_unique, teardown_s),
+        cmocka_unit_test_teardown(test_unique_nested, teardown_s),
     };
 
     return cmocka_run_group_tests(tests, setup, teardown);
diff --git a/tests/src/test_xml.c b/tests/src/test_xml.c
index ffa1e2d..6af5a79 100644
--- a/tests/src/test_xml.c
+++ b/tests/src/test_xml.c
@@ -230,7 +230,7 @@
     assert_string_equal("text</b>", str);
     assert_int_equal(LYXML_ELEM_CONTENT, ctx.status);
     assert_int_equal(LY_EVALID, lyxml_get_string(&ctx, &str, &buf, &buf_len, &out, &len, &dynamic));
-    logbuf_assert("Opening and closing elements tag missmatch (\"b\"). Line number 1.");
+    logbuf_assert("Opening and closing elements tag missmatch (\"b\", expected \"a\"). Line number 1.");
     lyxml_context_clear(&ctx);
 
 }