summaryrefslogtreecommitdiffstats
path: root/tests/utests/basic
diff options
context:
space:
mode:
Diffstat (limited to 'tests/utests/basic')
-rw-r--r--tests/utests/basic/test_common.c416
-rw-r--r--tests/utests/basic/test_context.c1103
-rw-r--r--tests/utests/basic/test_hash_table.c262
-rw-r--r--tests/utests/basic/test_inout.c407
-rw-r--r--tests/utests/basic/test_json.c773
-rw-r--r--tests/utests/basic/test_plugins.c153
-rw-r--r--tests/utests/basic/test_set.c276
-rw-r--r--tests/utests/basic/test_xml.c689
-rw-r--r--tests/utests/basic/test_xpath.c1263
-rw-r--r--tests/utests/basic/test_yanglib.c144
10 files changed, 5486 insertions, 0 deletions
diff --git a/tests/utests/basic/test_common.c b/tests/utests/basic/test_common.c
new file mode 100644
index 0000000..36269e7
--- /dev/null
+++ b/tests/utests/basic/test_common.c
@@ -0,0 +1,416 @@
+/**
+ * @file test_common.c
+ * @author: Radek Krejci <rkrejci@cesnet.cz>
+ * @brief unit tests for functions from common.c
+ *
+ * Copyright (c) 2018 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
+ */
+#define _UTEST_MAIN_
+#include "utests.h"
+
+#include "ly_common.h"
+
+static void
+test_utf8(void **UNUSED(state))
+{
+ char buf[5] = {0};
+ const char *str = buf;
+ unsigned int c;
+ size_t len;
+
+ /* test invalid UTF-8 characters in lyxml_getutf8
+ * - https://en.wikipedia.org/wiki/UTF-8 */
+ buf[0] = (char)0x04;
+ assert_int_equal(LY_EINVAL, ly_getutf8(&str, &c, &len));
+ buf[0] = (char)0x80;
+ assert_int_equal(LY_EINVAL, ly_getutf8(&str, &c, &len));
+
+ buf[0] = (char)0xc0;
+ buf[1] = (char)0x00;
+ assert_int_equal(LY_EINVAL, ly_getutf8(&str, &c, &len));
+ buf[1] = (char)0x80;
+ assert_int_equal(LY_EINVAL, ly_getutf8(&str, &c, &len));
+
+ buf[0] = (char)0xe0;
+ buf[1] = (char)0x00;
+ buf[2] = (char)0x80;
+ assert_int_equal(LY_EINVAL, ly_getutf8(&str, &c, &len));
+ buf[1] = (char)0x80;
+ assert_int_equal(LY_EINVAL, ly_getutf8(&str, &c, &len));
+
+ buf[0] = (char)0xf0;
+ buf[1] = (char)0x00;
+ buf[2] = (char)0x80;
+ buf[3] = (char)0x80;
+ assert_int_equal(LY_EINVAL, ly_getutf8(&str, &c, &len));
+ buf[1] = (char)0x80;
+ assert_int_equal(LY_EINVAL, ly_getutf8(&str, &c, &len));
+}
+
+static void
+test_parse_int(void **UNUSED(state))
+{
+ const char *str;
+ int64_t i = 500;
+
+ str = "10";
+ assert_int_equal(LY_SUCCESS, ly_parse_int(str, strlen(str), -10, 10, 10, &i));
+ assert_int_equal(i, 10);
+
+ /* leading zeros are allowed, trailing whitespaces are allowed */
+ str = "000\n\t ";
+ assert_int_equal(LY_SUCCESS, ly_parse_int(str, strlen(str), -10, 10, 10, &i));
+ assert_int_equal(i, 0);
+
+ /* negative value */
+ str = "-10";
+ assert_int_equal(LY_SUCCESS, ly_parse_int(str, strlen(str), -10, 10, 10, &i));
+ assert_int_equal(i, -10);
+
+ /* non-NULL terminated string */
+ str = "+5sometext";
+ assert_int_equal(LY_SUCCESS, ly_parse_int(str, 2, -10, 10, 10, &i));
+ assert_int_equal(i, 5);
+
+ /* out of bounds value */
+ str = "11";
+ assert_int_equal(LY_EDENIED, ly_parse_int(str, strlen(str), -10, 10, 10, &i));
+ str = "-11";
+ assert_int_equal(LY_EDENIED, ly_parse_int(str, strlen(str), -10, 10, 10, &i));
+
+ /* NaN */
+ str = "zero";
+ assert_int_equal(LY_EVALID, ly_parse_int(str, strlen(str), -10, 10, 10, &i));
+
+ /* mixing number with text */
+ str = "10zero";
+ assert_int_equal(LY_EVALID, ly_parse_int(str, strlen(str), -10, 10, 10, &i));
+
+ str = "10 zero";
+ assert_int_equal(LY_EVALID, ly_parse_int(str, strlen(str), -10, 10, 10, &i));
+}
+
+static void
+test_parse_uint(void **UNUSED(state))
+{
+ const char *str;
+ uint64_t u = 500;
+
+ str = "10";
+ assert_int_equal(LY_SUCCESS, ly_parse_uint(str, strlen(str), 10, 10, &u));
+ assert_int_equal(u, 10);
+
+ /* leading zeros are allowed, trailing whitespaces are allowed */
+ str = "000\n\t ";
+ assert_int_equal(LY_SUCCESS, ly_parse_uint(str, strlen(str), 10, 10, &u));
+ assert_int_equal(u, 0);
+ /* non-NULL terminated string */
+ str = "+5sometext";
+ assert_int_equal(LY_SUCCESS, ly_parse_uint(str, 2, 10, 10, &u));
+ assert_int_equal(u, 5);
+
+ /* out of bounds value */
+ str = "11";
+ assert_int_equal(LY_EDENIED, ly_parse_uint(str, strlen(str), 10, 10, &u));
+ str = "-1";
+ assert_int_equal(LY_EDENIED, ly_parse_uint(str, strlen(str), (uint64_t)-1, 10, &u));
+
+ /* NaN */
+ str = "zero";
+ assert_int_equal(LY_EVALID, ly_parse_uint(str, strlen(str), 10, 10, &u));
+
+ /* mixing number with text */
+ str = "10zero";
+ assert_int_equal(LY_EVALID, ly_parse_uint(str, strlen(str), 10, 10, &u));
+
+ str = "10 zero";
+ assert_int_equal(LY_EVALID, ly_parse_uint(str, strlen(str), 10, 10, &u));
+}
+
+static void
+test_parse_nodeid(void **UNUSED(state))
+{
+ const char *str;
+ const char *prefix, *name;
+ size_t prefix_len, name_len;
+
+ str = "123";
+ assert_int_equal(LY_EINVAL, ly_parse_nodeid(&str, &prefix, &prefix_len, &name, &name_len));
+
+ str = "a12_-.!";
+ assert_int_equal(LY_SUCCESS, ly_parse_nodeid(&str, &prefix, &prefix_len, &name, &name_len));
+ assert_null(prefix);
+ assert_int_equal(0, prefix_len);
+ assert_non_null(name);
+ assert_int_equal(6, name_len);
+ assert_int_equal(0, strncmp("a12_-.", name, name_len));
+ assert_string_equal("!", str);
+
+ str = "a12_-.:_b2 xxx";
+ assert_int_equal(LY_SUCCESS, ly_parse_nodeid(&str, &prefix, &prefix_len, &name, &name_len));
+ assert_non_null(prefix);
+ assert_int_equal(6, prefix_len);
+ assert_int_equal(0, strncmp("a12_-.", prefix, prefix_len));
+ assert_non_null(name);
+ assert_int_equal(3, name_len);
+ assert_int_equal(0, strncmp("_b2", name, name_len));
+ assert_string_equal(" xxx", str);
+}
+
+static void
+test_parse_instance_predicate(void **UNUSED(state))
+{
+ const char *str, *errmsg;
+ const char *prefix, *id, *value;
+ size_t prefix_len, id_len, value_len;
+
+ str = "[ex:name='fred']";
+ assert_int_equal(LY_SUCCESS, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg));
+ assert_string_equal(str, "");
+ assert_string_equal(prefix, "ex:name='fred']");
+ assert_int_equal(prefix_len, 2);
+ assert_string_equal(id, "name='fred']");
+ assert_int_equal(id_len, 4);
+ assert_string_equal(value, "fred']");
+ assert_int_equal(value_len, 4);
+
+ str = "[ex:ip = \"[192.0.2.1]\"][ex:port='80']";
+ assert_int_equal(LY_SUCCESS, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg));
+ assert_string_equal(str, "[ex:port='80']");
+ assert_string_equal(prefix, "ex:ip = \"[192.0.2.1]\"][ex:port='80']");
+ assert_int_equal(prefix_len, 2);
+ assert_string_equal(id, "ip = \"[192.0.2.1]\"][ex:port='80']");
+ assert_int_equal(id_len, 2);
+ assert_string_equal(value, "[192.0.2.1]\"][ex:port='80']");
+ assert_int_equal(value_len, 11);
+
+ str = "[. = 'blowfish-cbc']";
+ assert_int_equal(LY_SUCCESS, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg));
+ assert_string_equal(str, "");
+ assert_null(prefix);
+ assert_int_equal(prefix_len, 0);
+ assert_string_equal(id, ". = 'blowfish-cbc']");
+ assert_int_equal(id_len, 1);
+ assert_string_equal(value, "blowfish-cbc']");
+ assert_int_equal(value_len, 12);
+
+ str = "[ 3 ]";
+ assert_int_equal(LY_SUCCESS, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg));
+ assert_string_equal(str, "");
+ assert_null(prefix);
+ assert_int_equal(prefix_len, 0);
+ assert_null(id);
+ assert_int_equal(id_len, 0);
+ assert_string_equal(value, "3 ]");
+ assert_int_equal(value_len, 1);
+
+ /* invalid predicates */
+ /* position must be positive integer */
+ str = "[0]";
+ assert_int_equal(LY_EVALID, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg));
+ assert_string_equal(errmsg, "The position predicate cannot be zero.");
+ str = "[-1]";
+ assert_int_equal(LY_EVALID, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg));
+ assert_string_equal(errmsg, "Invalid instance predicate format (negative position or invalid node-identifier).");
+
+ /* invalid node-identifier */
+ str = "[$node='value']";
+ assert_int_equal(LY_EVALID, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg));
+ assert_string_equal(errmsg, "Invalid node-identifier.");
+ str = "[.node='value']";
+ assert_int_equal(LY_EVALID, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg));
+ assert_string_equal(errmsg, "Unexpected character instead of '=' in leaf-list-predicate.");
+ str = "[13node='value']";
+ assert_int_equal(LY_EVALID, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg));
+ assert_string_equal(errmsg, "Predicate (pos) is not terminated by \']\' character.");
+
+ str = "[ex:node]";
+ assert_int_equal(LY_EVALID, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg));
+ assert_string_equal(errmsg, "Unexpected character instead of '=' in key-predicate.");
+
+ str = "[ex:node= value]";
+ assert_int_equal(LY_EVALID, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg));
+ assert_string_equal(errmsg, "String value is not quoted.");
+
+ str = "[ex:node='value\"]";
+ assert_int_equal(LY_EVALID, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg));
+ assert_string_equal(errmsg, "Value is not terminated quoted-string.");
+
+ str = "[ex:node='value ]";
+ assert_int_equal(LY_EVALID, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg));
+ assert_string_equal(errmsg, "Value is not terminated quoted-string.");
+
+ str = "[ex:node=\"value\"[3]";
+ assert_int_equal(LY_EVALID, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg));
+ assert_string_equal(errmsg, "Predicate (key-predicate) is not terminated by \']\' character.");
+ str = "[.=\"value\"[3]";
+ assert_int_equal(LY_EVALID, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg));
+ assert_string_equal(errmsg, "Predicate (leaf-list-predicate) is not terminated by \']\' character.");
+
+ /* the limit of the string is too short, it ends one character earlier */
+ str = "[ex:node='value']";
+ assert_int_equal(LY_EINVAL, ly_parse_instance_predicate(&str, strlen(str) - 1, LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg));
+ assert_string_equal(errmsg, "Predicate is incomplete.");
+}
+
+static void
+test_value_prefix_next(void **UNUSED(state))
+{
+ const char *next;
+ ly_bool is_prefix;
+ uint32_t bytes;
+
+ assert_int_equal(LY_SUCCESS, ly_value_prefix_next(NULL, NULL, &bytes, &is_prefix, &next));
+ assert_int_equal(0, bytes);
+ assert_int_equal(LY_SUCCESS, ly_value_prefix_next("", NULL, &bytes, &is_prefix, &next));
+ assert_int_equal(0, bytes);
+
+ /* prefix */
+ next = "pref:";
+ assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next));
+ assert_int_equal(4, bytes);
+ assert_null(next);
+ assert_int_equal(1, is_prefix);
+
+ /* no-prefix */
+ next = "node";
+ assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next));
+ assert_int_equal(4, bytes);
+ assert_null(next);
+ assert_int_equal(0, is_prefix);
+
+ /* no-prefix */
+ next = "::::";
+ assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next));
+ assert_int_equal(4, bytes);
+ assert_null(next);
+ assert_int_equal(0, is_prefix);
+
+ /* no-prefix */
+ next = "//a/:";
+ assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next));
+ assert_int_equal(5, bytes);
+ assert_null(next);
+ assert_int_equal(0, is_prefix);
+
+ /* no-prefix */
+ next = "//a//";
+ assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next));
+ assert_int_equal(5, bytes);
+ assert_null(next);
+ assert_int_equal(0, is_prefix);
+
+ /* prefix, prefix */
+ next = "pref1:pref2:";
+ assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next));
+ assert_int_equal(5, bytes);
+ assert_string_equal(next, "pref2:");
+ assert_int_equal(1, is_prefix);
+ assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next));
+ assert_int_equal(5, bytes);
+ assert_null(next);
+ assert_int_equal(1, is_prefix);
+
+ /* prefix, no-prefix */
+ next = "pref:node";
+ assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next));
+ assert_int_equal(4, bytes);
+ assert_string_equal(next, "node");
+ assert_int_equal(1, is_prefix);
+ assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next));
+ assert_int_equal(4, bytes);
+ assert_null(next);
+ assert_int_equal(0, is_prefix);
+
+ /* no-prefix, prefix */
+ next = "/pref:";
+ assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next));
+ assert_int_equal(1, bytes);
+ assert_string_equal(next, "pref:");
+ assert_int_equal(0, is_prefix);
+ assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next));
+ assert_int_equal(4, bytes);
+ assert_null(next);
+ assert_int_equal(1, is_prefix);
+
+ /* no-prefix, prefix */
+ next = "//pref:";
+ assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next));
+ assert_int_equal(2, bytes);
+ assert_string_equal(next, "pref:");
+ assert_int_equal(0, is_prefix);
+ assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next));
+ assert_int_equal(4, bytes);
+ assert_null(next);
+ assert_int_equal(1, is_prefix);
+
+ /* no-prefix, prefix, no-prefix */
+ next = "/pref:node";
+ assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next));
+ assert_int_equal(1, bytes);
+ assert_string_equal(next, "pref:node");
+ assert_int_equal(0, is_prefix);
+ assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next));
+ assert_int_equal(4, bytes);
+ assert_string_equal(next, "node");
+ assert_int_equal(1, is_prefix);
+ assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next));
+ assert_int_equal(4, bytes);
+ assert_null(next);
+ assert_int_equal(0, is_prefix);
+
+ /* prefix, no-prefix, prefix */
+ next = "pref:node pref:";
+ assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next));
+ assert_int_equal(4, bytes);
+ assert_string_equal(next, "node pref:");
+ assert_int_equal(1, is_prefix);
+ assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next));
+ assert_int_equal(5, bytes);
+ assert_string_equal(next, "pref:");
+ assert_int_equal(0, is_prefix);
+ assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next));
+ assert_int_equal(4, bytes);
+ assert_null(next);
+ assert_int_equal(1, is_prefix);
+
+ /* prefix, no-prefix, prefix, no-prefix */
+ next = "pref:node /pref:node";
+ assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next));
+ assert_int_equal(4, bytes);
+ assert_string_equal(next, "node /pref:node");
+ assert_int_equal(1, is_prefix);
+ assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next));
+ assert_int_equal(6, bytes);
+ assert_string_equal(next, "pref:node");
+ assert_int_equal(0, is_prefix);
+ assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next));
+ assert_int_equal(4, bytes);
+ assert_string_equal(next, "node");
+ assert_int_equal(1, is_prefix);
+ assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next));
+ assert_int_equal(4, bytes);
+ assert_null(next);
+ assert_int_equal(0, is_prefix);
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_utf8),
+ UTEST(test_parse_int),
+ UTEST(test_parse_uint),
+ UTEST(test_parse_nodeid),
+ UTEST(test_parse_instance_predicate),
+ UTEST(test_value_prefix_next),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/basic/test_context.c b/tests/utests/basic/test_context.c
new file mode 100644
index 0000000..7feb65f
--- /dev/null
+++ b/tests/utests/basic/test_context.c
@@ -0,0 +1,1103 @@
+/**
+ * @file test_context.c
+ * @author: Radek Krejci <rkrejci@cesnet.cz>
+ * @brief unit tests for functions from context.c
+ *
+ * Copyright (c) 2018 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
+ */
+#define _UTEST_MAIN_
+#include "utests.h"
+
+#include "context.h"
+#include "in.h"
+#include "ly_common.h"
+#include "schema_compile.h"
+#include "tests_config.h"
+#include "tree_schema_internal.h"
+
+#ifdef _WIN32
+
+static void
+slashes_to_backslashes(char *path)
+{
+ while ((path = strchr(path, '/'))) {
+ *path++ = '\\';
+ }
+}
+
+static void
+test_searchdirs(void **state)
+{
+ const char * const *list;
+ char *path1 = strdup(TESTS_BIN "/utests");
+ char *path2 = strdup(TESTS_SRC);
+
+ slashes_to_backslashes(path1);
+ slashes_to_backslashes(path2);
+
+ assert_int_equal(LY_EINVAL, ly_ctx_set_searchdir(NULL, NULL));
+ CHECK_LOG_LASTMSG("Invalid argument ctx (ly_ctx_set_searchdir()).");
+ assert_null(ly_ctx_get_searchdirs(NULL));
+ CHECK_LOG_LASTMSG("Invalid argument ctx (ly_ctx_get_searchdirs()).");
+ assert_int_equal(LY_EINVAL, ly_ctx_unset_searchdir(NULL, NULL));
+ CHECK_LOG_LASTMSG("Invalid argument ctx (ly_ctx_unset_searchdir()).");
+
+ /* correct path */
+ assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, path1));
+ assert_int_equal(1, UTEST_LYCTX->search_paths.count);
+ assert_string_equal(path1, UTEST_LYCTX->search_paths.objs[0]);
+
+ /* duplicated paths */
+ assert_int_equal(LY_EEXIST, ly_ctx_set_searchdir(UTEST_LYCTX, path1));
+ assert_int_equal(1, UTEST_LYCTX->search_paths.count);
+ assert_string_equal(path1, UTEST_LYCTX->search_paths.objs[0]);
+
+ /* another path */
+ assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, path2));
+ assert_int_equal(2, UTEST_LYCTX->search_paths.count);
+ assert_string_equal(path2, UTEST_LYCTX->search_paths.objs[1]);
+
+ /* get searchpaths */
+ list = ly_ctx_get_searchdirs(UTEST_LYCTX);
+ assert_non_null(list);
+ assert_string_equal(path1, list[0]);
+ assert_string_equal(path2, list[1]);
+ assert_null(list[2]);
+
+ /* removing searchpaths */
+ /* nonexisting */
+ assert_int_equal(LY_EINVAL, ly_ctx_unset_searchdir(UTEST_LYCTX, "/nonexistingfile"));
+ CHECK_LOG_CTX("Invalid argument value (ly_ctx_unset_searchdir()).", NULL, 0);
+
+ /* first */
+ assert_int_equal(LY_SUCCESS, ly_ctx_unset_searchdir(UTEST_LYCTX, path1));
+ assert_int_equal(1, UTEST_LYCTX->search_paths.count);
+ assert_string_not_equal(path1, list[0]);
+
+ /* second */
+ assert_int_equal(LY_SUCCESS, ly_ctx_unset_searchdir(UTEST_LYCTX, path2));
+ assert_int_equal(0, UTEST_LYCTX->search_paths.count);
+
+ free(path1);
+ free(path2);
+}
+
+#else
+
+static void
+test_searchdirs(void **state)
+{
+ const char * const *list;
+
+ /* invalid arguments */
+ assert_int_equal(LY_EINVAL, ly_ctx_set_searchdir(NULL, NULL));
+ CHECK_LOG_LASTMSG("Invalid argument ctx (ly_ctx_set_searchdir()).");
+ assert_null(ly_ctx_get_searchdirs(NULL));
+ CHECK_LOG_LASTMSG("Invalid argument ctx (ly_ctx_get_searchdirs()).");
+ assert_int_equal(LY_EINVAL, ly_ctx_unset_searchdir(NULL, NULL));
+ CHECK_LOG_LASTMSG("Invalid argument ctx (ly_ctx_unset_searchdir()).");
+
+ /* readable and executable, but not a directory */
+ assert_int_equal(LY_EINVAL, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_BIN "/utest_context"));
+ CHECK_LOG_CTX("Given search directory \""TESTS_BIN "/utest_context\" is not a directory.", NULL, 0);
+ /* not existing */
+ assert_int_equal(LY_EINVAL, ly_ctx_set_searchdir(UTEST_LYCTX, "/nonexistingfile"));
+ CHECK_LOG_CTX("Unable to use search directory \"/nonexistingfile\" (No such file or directory).", NULL, 0);
+
+ /* ly_set_add() fails */
+ /* no change */
+ assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, NULL));
+
+ /* correct path */
+ assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_BIN "/utests"));
+ assert_int_equal(1, UTEST_LYCTX->search_paths.count);
+ assert_string_equal(TESTS_BIN "/utests", UTEST_LYCTX->search_paths.objs[0]);
+
+ /* duplicated paths */
+ assert_int_equal(LY_EEXIST, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_BIN "/utests"));
+ assert_int_equal(1, UTEST_LYCTX->search_paths.count);
+ assert_string_equal(TESTS_BIN "/utests", UTEST_LYCTX->search_paths.objs[0]);
+
+ /* another paths - add 8 to fill the initial buffer of the searchpaths list */
+ assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_BIN "/CMakeFiles"));
+ assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_SRC "/../src"));
+ assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_SRC "/../CMakeModules"));
+ assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_SRC "/../doc"));
+ assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_SRC));
+ assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_BIN));
+ assert_int_equal(7, UTEST_LYCTX->search_paths.count);
+
+ /* get searchpaths */
+ list = ly_ctx_get_searchdirs(UTEST_LYCTX);
+ assert_non_null(list);
+ assert_string_equal(TESTS_BIN "/utests", list[0]);
+ assert_string_equal(TESTS_BIN "/CMakeFiles", list[1]);
+ assert_string_equal(TESTS_SRC, list[5]);
+ assert_string_equal(TESTS_BIN, list[6]);
+ assert_null(list[7]);
+
+ /* removing searchpaths */
+ /* nonexisting */
+ assert_int_equal(LY_EINVAL, ly_ctx_unset_searchdir(UTEST_LYCTX, "/nonexistingfile"));
+ CHECK_LOG_CTX("Invalid argument value (ly_ctx_unset_searchdir()).", NULL, 0);
+ /* first */
+ assert_int_equal(LY_SUCCESS, ly_ctx_unset_searchdir(UTEST_LYCTX, TESTS_BIN "/utests"));
+ assert_string_not_equal(TESTS_BIN "/utests", list[0]);
+ assert_int_equal(6, UTEST_LYCTX->search_paths.count);
+ /* middle */
+ assert_int_equal(LY_SUCCESS, ly_ctx_unset_searchdir(UTEST_LYCTX, TESTS_SRC));
+ assert_int_equal(5, UTEST_LYCTX->search_paths.count);
+ /* last */
+ assert_int_equal(LY_SUCCESS, ly_ctx_unset_searchdir(UTEST_LYCTX, TESTS_BIN));
+ assert_int_equal(4, UTEST_LYCTX->search_paths.count);
+ /* all */
+ assert_int_equal(LY_SUCCESS, ly_ctx_unset_searchdir(UTEST_LYCTX, NULL));
+ assert_int_equal(0, UTEST_LYCTX->search_paths.count);
+
+ /* again - no change */
+ assert_int_equal(LY_SUCCESS, ly_ctx_unset_searchdir(UTEST_LYCTX, NULL));
+
+ /* cleanup */
+ ly_ctx_destroy(UTEST_LYCTX);
+
+ /* test searchdir list in ly_ctx_new() */
+ assert_int_equal(LY_EINVAL, ly_ctx_new("/nonexistingfile", 0, &UTEST_LYCTX));
+ CHECK_LOG_LASTMSG("Unable to use search directory \"/nonexistingfile\" (No such file or directory).");
+ assert_int_equal(LY_SUCCESS,
+ ly_ctx_new(TESTS_SRC PATH_SEPARATOR TESTS_BIN PATH_SEPARATOR TESTS_BIN PATH_SEPARATOR TESTS_SRC,
+ LY_CTX_DISABLE_SEARCHDIRS, &UTEST_LYCTX));
+ assert_int_equal(2, UTEST_LYCTX->search_paths.count);
+ assert_string_equal(TESTS_SRC, UTEST_LYCTX->search_paths.objs[0]);
+ assert_string_equal(TESTS_BIN, UTEST_LYCTX->search_paths.objs[1]);
+}
+
+#endif
+
+static void
+test_options(void **state)
+{
+ /* use own context with extra flags */
+ ly_ctx_destroy(UTEST_LYCTX);
+
+ assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, 0xffff, &UTEST_LYCTX));
+
+ /* invalid arguments */
+ assert_int_equal(0, ly_ctx_get_options(NULL));
+ CHECK_LOG_LASTMSG("Invalid argument ctx (ly_ctx_get_options()).");
+
+ assert_int_equal(LY_EINVAL, ly_ctx_set_options(NULL, 0));
+ CHECK_LOG_LASTMSG("Invalid argument ctx (ly_ctx_set_options()).");
+ assert_int_equal(LY_EINVAL, ly_ctx_unset_options(NULL, 0));
+ CHECK_LOG_LASTMSG("Invalid argument ctx (ly_ctx_unset_options()).");
+
+ /* unset */
+ /* LY_CTX_ALL_IMPLEMENTED */
+ assert_int_not_equal(0, UTEST_LYCTX->flags & LY_CTX_ALL_IMPLEMENTED);
+ assert_int_equal(LY_SUCCESS, ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_ALL_IMPLEMENTED));
+ assert_int_equal(0, UTEST_LYCTX->flags & LY_CTX_ALL_IMPLEMENTED);
+
+ /* LY_CTX_REF_IMPLEMENTED */
+ assert_int_not_equal(0, UTEST_LYCTX->flags & LY_CTX_REF_IMPLEMENTED);
+ assert_int_equal(LY_SUCCESS, ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_REF_IMPLEMENTED));
+ assert_int_equal(0, UTEST_LYCTX->flags & LY_CTX_REF_IMPLEMENTED);
+
+ /* LY_CTX_DISABLE_SEARCHDIRS */
+ assert_int_not_equal(0, UTEST_LYCTX->flags & LY_CTX_DISABLE_SEARCHDIRS);
+ assert_int_equal(LY_SUCCESS, ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_DISABLE_SEARCHDIRS));
+ assert_int_equal(0, UTEST_LYCTX->flags & LY_CTX_DISABLE_SEARCHDIRS);
+
+ /* LY_CTX_DISABLE_SEARCHDIR_CWD */
+ assert_int_not_equal(0, UTEST_LYCTX->flags & LY_CTX_DISABLE_SEARCHDIR_CWD);
+ assert_int_equal(LY_SUCCESS, ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_DISABLE_SEARCHDIR_CWD));
+ assert_int_equal(0, UTEST_LYCTX->flags & LY_CTX_DISABLE_SEARCHDIR_CWD);
+
+ /* LY_CTX_PREFER_SEARCHDIRS */
+ assert_int_not_equal(0, UTEST_LYCTX->flags & LY_CTX_PREFER_SEARCHDIRS);
+ assert_int_equal(LY_SUCCESS, ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_PREFER_SEARCHDIRS));
+ assert_int_equal(0, UTEST_LYCTX->flags & LY_CTX_PREFER_SEARCHDIRS);
+
+ /* LY_CTX_LEAFREF_EXTENDED */
+ assert_int_not_equal(0, UTEST_LYCTX->flags & LY_CTX_LEAFREF_EXTENDED);
+ assert_int_equal(LY_SUCCESS, ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_LEAFREF_EXTENDED));
+ assert_int_equal(0, UTEST_LYCTX->flags & LY_CTX_LEAFREF_EXTENDED);
+
+ /* LY_CTX_LEAFREF_LINKING */
+ assert_int_not_equal(0, UTEST_LYCTX->flags & LY_CTX_LEAFREF_LINKING);
+ assert_int_equal(LY_SUCCESS, ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_LEAFREF_LINKING));
+ assert_int_equal(0, UTEST_LYCTX->flags & LY_CTX_LEAFREF_LINKING);
+
+ /* LY_CTX_BUILTIN_PLUGINS_ONLY */
+ assert_int_not_equal(0, UTEST_LYCTX->flags & LY_CTX_BUILTIN_PLUGINS_ONLY);
+ assert_int_equal(LY_SUCCESS, ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_BUILTIN_PLUGINS_ONLY));
+ assert_int_equal(0, UTEST_LYCTX->flags & LY_CTX_BUILTIN_PLUGINS_ONLY);
+
+ assert_int_equal(UTEST_LYCTX->flags, ly_ctx_get_options(UTEST_LYCTX));
+
+ /* set back */
+ /* LY_CTX_ALL_IMPLEMENTED */
+ assert_int_equal(LY_SUCCESS, ly_ctx_set_options(UTEST_LYCTX, LY_CTX_ALL_IMPLEMENTED));
+ assert_int_not_equal(0, UTEST_LYCTX->flags & LY_CTX_ALL_IMPLEMENTED);
+
+ /* LY_CTX_REF_IMPLEMENTED */
+ assert_int_equal(LY_SUCCESS, ly_ctx_set_options(UTEST_LYCTX, LY_CTX_REF_IMPLEMENTED));
+ assert_int_not_equal(0, UTEST_LYCTX->flags & LY_CTX_REF_IMPLEMENTED);
+
+ /* LY_CTX_DISABLE_SEARCHDIRS */
+ assert_int_equal(LY_SUCCESS, ly_ctx_set_options(UTEST_LYCTX, LY_CTX_DISABLE_SEARCHDIRS));
+ assert_int_not_equal(0, UTEST_LYCTX->flags & LY_CTX_DISABLE_SEARCHDIRS);
+
+ /* LY_CTX_DISABLE_SEARCHDIR_CWD */
+ assert_int_equal(LY_SUCCESS, ly_ctx_set_options(UTEST_LYCTX, LY_CTX_DISABLE_SEARCHDIR_CWD));
+ assert_int_not_equal(0, UTEST_LYCTX->flags & LY_CTX_DISABLE_SEARCHDIR_CWD);
+
+ /* LY_CTX_PREFER_SEARCHDIRS */
+ assert_int_equal(LY_SUCCESS, ly_ctx_set_options(UTEST_LYCTX, LY_CTX_PREFER_SEARCHDIRS));
+ assert_int_not_equal(0, UTEST_LYCTX->flags & LY_CTX_PREFER_SEARCHDIRS);
+
+ /* LY_CTX_LEAFREF_EXTENDED */
+ assert_int_equal(LY_SUCCESS, ly_ctx_set_options(UTEST_LYCTX, LY_CTX_LEAFREF_EXTENDED));
+ assert_int_not_equal(0, UTEST_LYCTX->flags & LY_CTX_LEAFREF_EXTENDED);
+
+ /* LY_CTX_LEAFREF_LINKING */
+ assert_int_equal(LY_SUCCESS, ly_ctx_set_options(UTEST_LYCTX, LY_CTX_LEAFREF_LINKING));
+ assert_int_not_equal(0, UTEST_LYCTX->flags & LY_CTX_LEAFREF_LINKING);
+
+ /* LY_CTX_BUILTIN_PLUGINS_ONLY */
+ assert_int_equal(LY_EINVAL, ly_ctx_set_options(UTEST_LYCTX, LY_CTX_BUILTIN_PLUGINS_ONLY));
+ CHECK_LOG_CTX("Invalid argument option (LY_CTX_BUILTIN_PLUGINS_ONLY can be set only when creating a new context) (ly_ctx_set_options()).", NULL, 0);
+
+ assert_int_equal(UTEST_LYCTX->flags, ly_ctx_get_options(UTEST_LYCTX));
+}
+
+static LY_ERR
+test_imp_clb(const char *UNUSED(mod_name), const char *UNUSED(mod_rev), const char *UNUSED(submod_name),
+ const char *UNUSED(sub_rev), void *user_data, LYS_INFORMAT *format,
+ const char **module_data, void (**free_module_data)(void *model_data, void *user_data))
+{
+ *module_data = user_data;
+ *format = LYS_IN_YANG;
+ *free_module_data = NULL;
+ return LY_SUCCESS;
+}
+
+static void
+test_models(void **state)
+{
+ struct ly_in *in;
+ const char *str;
+ struct lys_module *mod1, *mod2;
+ struct lys_glob_unres unres = {0};
+
+ /* use own context with extra flags */
+ ly_ctx_destroy(UTEST_LYCTX);
+
+ /* invalid arguments */
+ assert_int_equal(0, ly_ctx_get_change_count(NULL));
+ CHECK_LOG_LASTMSG("Invalid argument ctx (ly_ctx_get_change_count()).");
+
+ assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_DISABLE_SEARCHDIRS, &UTEST_LYCTX));
+ assert_int_equal(UTEST_LYCTX->change_count, ly_ctx_get_change_count(UTEST_LYCTX));
+
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory("module x {namespace urn:x;prefix x;}", &in));
+ assert_int_equal(LY_EINVAL, lys_parse_in(UTEST_LYCTX, in, 4, NULL, NULL, &unres.creating, &mod1));
+ lys_unres_glob_erase(&unres);
+ ly_in_free(in, 0);
+ CHECK_LOG_CTX("Invalid schema input format.", NULL, 0);
+
+ /* import callback */
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, (void *)(str = "test"));
+ assert_ptr_equal(test_imp_clb, UTEST_LYCTX->imp_clb);
+ assert_ptr_equal(str, UTEST_LYCTX->imp_clb_data);
+ assert_ptr_equal(test_imp_clb, ly_ctx_get_module_imp_clb(UTEST_LYCTX, (void **)&str));
+ assert_string_equal("test", str);
+
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, NULL, NULL);
+ assert_null(UTEST_LYCTX->imp_clb);
+ assert_null(UTEST_LYCTX->imp_clb_data);
+
+ /* name collision of module and submodule */
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule y {belongs-to a {prefix a;} revision 2018-10-30;}");
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory("module y {namespace urn:y;prefix y;include y;}", &in));
+ assert_int_equal(LY_EVALID, lys_parse_in(UTEST_LYCTX, in, LYS_IN_YANG, NULL, NULL, &unres.creating, &mod1));
+ lys_unres_glob_erase(&unres);
+ ly_in_free(in, 0);
+ CHECK_LOG_CTX("Parsing module \"y\" failed.", NULL, 0);
+ CHECK_LOG_CTX("Name collision between module and submodule of name \"y\".", NULL, 1);
+
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory("module a {namespace urn:a;prefix a;include y;revision 2018-10-30; }", &in));
+ assert_int_equal(LY_SUCCESS, lys_parse_in(UTEST_LYCTX, in, LYS_IN_YANG, NULL, NULL, &unres.creating, &mod1));
+ ly_in_free(in, 0);
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory("module y {namespace urn:y;prefix y;}", &in));
+ assert_int_equal(LY_EVALID, lys_parse_in(UTEST_LYCTX, in, LYS_IN_YANG, NULL, NULL, &unres.creating, &mod1));
+ lys_unres_glob_erase(&unres);
+ ly_in_free(in, 0);
+ CHECK_LOG_CTX("Parsing module \"y\" failed.", NULL, 0);
+ CHECK_LOG_CTX("Name collision between module and submodule of name \"y\".", NULL, 1);
+
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule y {belongs-to b {prefix b;}}");
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory("module b {namespace urn:b;prefix b;include y;}", &in));
+ assert_int_equal(LY_EVALID, lys_parse_in(UTEST_LYCTX, in, LYS_IN_YANG, NULL, NULL, &unres.creating, &mod1));
+ lys_unres_glob_revert(UTEST_LYCTX, &unres);
+ lys_unres_glob_erase(&unres);
+ ly_in_free(in, 0);
+ CHECK_LOG_CTX("Parsing module \"b\" failed.", NULL, 0);
+ CHECK_LOG_CTX("Including \"y\" submodule into \"b\" failed.", NULL, 0);
+ CHECK_LOG_CTX("Parsing submodule failed.", NULL, 0);
+ CHECK_LOG_CTX("Name collision between submodules of name \"y\".", NULL, 1);
+
+ /* selecting correct revision of the submodules */
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule y {belongs-to a {prefix a;} revision 2018-10-31;}");
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory("module a {namespace urn:a;prefix a;include y; revision 2018-10-31;}", &in));
+ assert_int_equal(LY_SUCCESS, lys_parse_in(UTEST_LYCTX, in, LYS_IN_YANG, NULL, NULL, &unres.creating, &mod2));
+ lys_unres_glob_erase(&unres);
+ ly_in_free(in, 0);
+ assert_string_equal("2018-10-31", mod2->parsed->includes[0].submodule->revs[0].date);
+
+ /* reloading module in case only the compiled module resists in the context */
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory("module w {namespace urn:w;prefix w;revision 2018-10-24;}", &in));
+ assert_int_equal(LY_SUCCESS, lys_parse(UTEST_LYCTX, in, LYS_IN_YANG, NULL, &mod1));
+ ly_in_free(in, 0);
+ assert_non_null(mod1->compiled);
+ assert_non_null(mod1->parsed);
+
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory("module z {namespace urn:z;prefix z;import w {prefix w;revision-date 2018-10-24;}}", &in));
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module w {namespace urn:w;prefix w;revision 2018-10-24;}");
+ assert_int_equal(LY_SUCCESS, lys_parse(UTEST_LYCTX, in, LYS_IN_YANG, NULL, &mod2));
+ ly_in_free(in, 0);
+ assert_non_null(mod2);
+ assert_non_null(mod1->parsed);
+ assert_string_equal("w", mod1->name);
+}
+
+static void
+test_imports(void **state)
+{
+ struct lys_module *mod1, *mod2, *mod3, *import;
+ char *str;
+ uint16_t ctx_options;
+
+ /* use own context with extra flags */
+ ly_ctx_destroy(UTEST_LYCTX);
+ ctx_options = LY_CTX_DISABLE_SEARCHDIRS | LY_CTX_NO_YANGLIBRARY;
+
+ /* Import callback provides newer revision of module 'a',
+ * however the older revision is implemented soon and therefore it is preferred. */
+ assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, ctx_options, &UTEST_LYCTX));
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module a {namespace urn:a; prefix a; revision 2019-09-17;}");
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {namespace urn:a;prefix a;revision 2019-09-16;}",
+ LYS_IN_YANG, &mod1));
+ assert_true(LYS_MOD_LATEST_REV & mod1->latest_revision);
+ assert_int_equal(1, mod1->implemented);
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {namespace urn:b;prefix b;import a {prefix a;}}",
+ LYS_IN_YANG, &mod2));
+ assert_ptr_equal(mod1, mod2->parsed->imports[0].module);
+ assert_true((LYS_MOD_LATEST_REV | LYS_MOD_IMPORTED_REV) & mod1->latest_revision);
+ assert_string_equal("2019-09-16", mod1->revision);
+ assert_int_equal(1, mod1->implemented);
+ assert_non_null(ly_ctx_get_module(UTEST_LYCTX, "a", "2019-09-16"));
+ ly_ctx_destroy(UTEST_LYCTX);
+
+ /* Import callback provides older revision of module 'a' and it is
+ * imported by another module, so it is preferred even if newer
+ * revision is implemented later. */
+ assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, ctx_options, &UTEST_LYCTX));
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module a {namespace urn:a; prefix a; revision 2019-09-16;}");
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {namespace urn:b;prefix b;import a {prefix a;}}",
+ LYS_IN_YANG, &mod2));
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {namespace urn:a;prefix a;revision 2019-09-17;}",
+ LYS_IN_YANG, &mod1));
+ ly_log_level(LY_LLVRB);
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module c {namespace urn:c;prefix c;import a {prefix a;}}",
+ LYS_IN_YANG, &mod3));
+ CHECK_LOG_LASTMSG("Implemented module \"a@2019-09-17\" is not used for import, revision \"2019-09-16\" is imported instead.");
+ ly_log_level(LY_LLWRN);
+ assert_true(LYS_MOD_LATEST_SEARCHDIRS & mod1->latest_revision);
+ assert_int_equal(1, mod1->implemented);
+ import = mod2->parsed->imports[0].module;
+ assert_true(LYS_MOD_IMPORTED_REV & import->latest_revision);
+ assert_string_equal("2019-09-16", import->revision);
+ assert_int_equal(0, import->implemented);
+ import = mod3->parsed->imports[0].module;
+ assert_string_equal("2019-09-16", import->revision);
+ assert_non_null(ly_ctx_get_module(UTEST_LYCTX, "a", "2019-09-16"));
+ assert_non_null(ly_ctx_get_module(UTEST_LYCTX, "a", "2019-09-17"));
+ assert_string_equal("2019-09-17", ly_ctx_get_module_implemented(UTEST_LYCTX, "a")->revision);
+ ly_ctx_destroy(UTEST_LYCTX);
+
+ /* check of circular dependency */
+ assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, ctx_options, &UTEST_LYCTX));
+ str = "module a {namespace urn:a; prefix a;"
+ "import b {prefix b;}"
+ "}";
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, str);
+ str = "module b { yang-version 1.1; namespace urn:b; prefix b;"
+ "import a {prefix a;}"
+ "}";
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL));
+ ly_err_clean(UTEST_LYCTX, NULL);
+}
+
+static void
+test_get_models(void **state)
+{
+ struct lys_module *mod, *mod2;
+ const char *str0 = "module a {namespace urn:a;prefix a;}";
+ const char *str1 = "module a {namespace urn:a;prefix a;revision 2018-10-23;}";
+ const char *str2 = "module a {namespace urn:a;prefix a;revision 2018-10-23;revision 2018-10-24;}";
+ struct ly_in *in0, *in1, *in2;
+ struct lys_glob_unres unres = {0};
+
+ unsigned int index = 0;
+ const char *names[] = {
+ "ietf-yang-metadata", "yang", "ietf-inet-types", "ietf-yang-types", "ietf-yang-schema-mount",
+ "ietf-yang-structure-ext", "ietf-datastores", "ietf-yang-library", "a", "a", "a"
+ };
+
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str0, &in0));
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str1, &in1));
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str2, &in2));
+
+ /* invalid arguments */
+ assert_ptr_equal(NULL, ly_ctx_get_module(NULL, NULL, NULL));
+ CHECK_LOG_LASTMSG("Invalid argument ctx (ly_ctx_get_module()).");
+ assert_ptr_equal(NULL, ly_ctx_get_module(UTEST_LYCTX, NULL, NULL));
+ CHECK_LOG_CTX("Invalid argument name (ly_ctx_get_module()).", NULL, 0);
+ assert_ptr_equal(NULL, ly_ctx_get_module_ns(NULL, NULL, NULL));
+ CHECK_LOG_LASTMSG("Invalid argument ctx (ly_ctx_get_module_ns()).");
+ assert_ptr_equal(NULL, ly_ctx_get_module_ns(UTEST_LYCTX, NULL, NULL));
+ CHECK_LOG_CTX("Invalid argument ns (ly_ctx_get_module_ns()).", NULL, 0);
+ assert_null(ly_ctx_get_module(UTEST_LYCTX, "nonsence", NULL));
+
+ /* internal modules */
+ assert_null(ly_ctx_get_module_implemented(UTEST_LYCTX, "ietf-yang-types"));
+ mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "yang");
+ assert_non_null(mod);
+ assert_non_null(mod->parsed);
+ assert_string_equal("yang", mod->name);
+ mod2 = ly_ctx_get_module_implemented_ns(UTEST_LYCTX, mod->ns);
+ assert_ptr_equal(mod, mod2);
+ assert_non_null(ly_ctx_get_module(UTEST_LYCTX, "ietf-yang-metadata", "2016-08-05"));
+ assert_non_null(ly_ctx_get_module(UTEST_LYCTX, "ietf-yang-types", "2013-07-15"));
+ assert_non_null(ly_ctx_get_module(UTEST_LYCTX, "ietf-inet-types", "2013-07-15"));
+ assert_non_null(ly_ctx_get_module_ns(UTEST_LYCTX, "urn:ietf:params:xml:ns:yang:ietf-datastores", "2018-02-14"));
+
+ /* select module by revision */
+ assert_int_equal(LY_SUCCESS, lys_parse(UTEST_LYCTX, in1, LYS_IN_YANG, NULL, &mod));
+ /* invalid attempts - implementing module of the same name and inserting the same module */
+ assert_int_equal(LY_SUCCESS, lys_parse_in(UTEST_LYCTX, in2, LYS_IN_YANG, NULL, NULL, &unres.creating, &mod2));
+ assert_int_equal(LY_EDENIED, lys_implement(mod2, NULL, &unres));
+ CHECK_LOG_CTX("Module \"a@2018-10-24\" is already implemented in revision \"2018-10-23\".", NULL, 0);
+ lys_unres_glob_erase(&unres);
+ ly_in_reset(in1);
+ /* it is already there, fine */
+ assert_int_equal(LY_SUCCESS, lys_parse_in(UTEST_LYCTX, in1, LYS_IN_YANG, NULL, NULL, &unres.creating, NULL));
+ /* insert the second module only as imported, not implemented */
+ lys_unres_glob_erase(&unres);
+ ly_in_reset(in2);
+ assert_int_equal(LY_SUCCESS, lys_parse_in(UTEST_LYCTX, in2, LYS_IN_YANG, NULL, NULL, &unres.creating, &mod2));
+ lys_unres_glob_erase(&unres);
+ assert_non_null(mod2);
+ assert_ptr_not_equal(mod, mod2);
+ mod = ly_ctx_get_module_latest(UTEST_LYCTX, "a");
+ assert_ptr_equal(mod, mod2);
+ mod2 = ly_ctx_get_module_latest_ns(UTEST_LYCTX, mod->ns);
+ assert_ptr_equal(mod, mod2);
+ /* work with module with no revision */
+ assert_int_equal(LY_SUCCESS, lys_parse_in(UTEST_LYCTX, in0, LYS_IN_YANG, NULL, NULL, &unres.creating, &mod));
+ lys_unres_glob_erase(&unres);
+ assert_ptr_equal(mod, ly_ctx_get_module(UTEST_LYCTX, "a", NULL));
+ assert_ptr_not_equal(mod, ly_ctx_get_module_latest(UTEST_LYCTX, "a"));
+
+ str1 = "submodule b {belongs-to a {prefix a;}}";
+ ly_in_free(in1, 0);
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str1, &in1));
+ assert_int_equal(LY_EINVAL, lys_parse_in(UTEST_LYCTX, in1, LYS_IN_YANG, NULL, NULL, &unres.creating, &mod));
+ CHECK_LOG_CTX("Input data contains submodule which cannot be parsed directly without its main module.", NULL, 0);
+ lys_unres_glob_erase(&unres);
+
+ while ((mod = (struct lys_module *)ly_ctx_get_module_iter(UTEST_LYCTX, &index))) {
+ assert_string_equal(names[index - 1], mod->name);
+ }
+ assert_int_equal(11, index);
+
+ /* cleanup */
+ ly_in_free(in0, 0);
+ ly_in_free(in1, 0);
+ ly_in_free(in2, 0);
+}
+
+static void
+test_ylmem(void **state)
+{
+#define DATA_YANG_LIBRARY_START "<yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">\n"\
+ " <module-set>\n"\
+ " <name>complete</name>\n"\
+ " <module>\n"\
+ " <name>yang</name>\n"\
+ " <revision>2022-06-16</revision>\n"\
+ " <namespace>urn:ietf:params:xml:ns:yang:1</namespace>\n"\
+ " </module>\n"\
+ " <module>\n"\
+ " <name>ietf-yang-library</name>\n"\
+ " <revision>2019-01-04</revision>\n"\
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>\n"\
+ " </module>\n"
+
+#define DATA_YANG_BASE_IMPORTS " <import-only-module>\n"\
+ " <name>ietf-yang-metadata</name>\n"\
+ " <revision>2016-08-05</revision>\n"\
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-metadata</namespace>\n"\
+ " </import-only-module>\n"\
+ " <import-only-module>\n"\
+ " <name>ietf-inet-types</name>\n"\
+ " <revision>2013-07-15</revision>\n"\
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-inet-types</namespace>\n"\
+ " </import-only-module>\n"\
+ " <import-only-module>\n"\
+ " <name>ietf-yang-types</name>\n"\
+ " <revision>2013-07-15</revision>\n"\
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-types</namespace>\n"\
+ " </import-only-module>\n"\
+ " <import-only-module>\n"\
+ " <name>ietf-datastores</name>\n"\
+ " <revision>2018-02-14</revision>\n"\
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-datastores</namespace>\n"\
+ " </import-only-module>\n"
+
+#define DATA_YANG_SCHEMA_MODULE_STATE " </module-set>\n"\
+ " <schema>\n"\
+ " <name>complete</name>\n"\
+ " <module-set>complete</module-set>\n"\
+ " </schema>\n"\
+ " <content-id>9</content-id>\n"\
+ "</yang-library>\n"\
+ "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">\n"\
+ " <module-set-id>12</module-set-id>\n"\
+ " <module>\n"\
+ " <name>ietf-yang-metadata</name>\n"\
+ " <revision>2016-08-05</revision>\n"\
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-metadata</namespace>\n"\
+ " <conformance-type>import</conformance-type>\n"\
+ " </module>\n"\
+ " <module>\n"\
+ " <name>yang</name>\n"\
+ " <revision>2022-06-16</revision>\n"\
+ " <namespace>urn:ietf:params:xml:ns:yang:1</namespace>\n"\
+ " <conformance-type>implement</conformance-type>\n"\
+ " </module>\n"\
+ " <module>\n"\
+ " <name>ietf-inet-types</name>\n"\
+ " <revision>2013-07-15</revision>\n"\
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-inet-types</namespace>\n"\
+ " <conformance-type>import</conformance-type>\n"\
+ " </module>\n"\
+ " <module>\n"\
+ " <name>ietf-yang-types</name>\n"\
+ " <revision>2013-07-15</revision>\n"\
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-types</namespace>\n"\
+ " <conformance-type>import</conformance-type>\n"\
+ " </module>\n"\
+ " <module>\n"\
+ " <name>ietf-yang-library</name>\n"\
+ " <revision>2019-01-04</revision>\n"\
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>\n"\
+ " <conformance-type>implement</conformance-type>\n"\
+ " </module>\n"\
+ " <module>\n"\
+ " <name>ietf-datastores</name>\n"\
+ " <revision>2018-02-14</revision>\n"\
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-datastores</namespace>\n"\
+ " <conformance-type>import</conformance-type>\n"\
+ " </module>\n"
+
+ const char *yanglibrary_only =
+ DATA_YANG_LIBRARY_START
+ DATA_YANG_BASE_IMPORTS
+ DATA_YANG_SCHEMA_MODULE_STATE
+ "</modules-state>\n";
+
+ const char *with_netconf =
+ DATA_YANG_LIBRARY_START
+ " <module>\n"
+ " <name>ietf-netconf</name>\n"
+ " <revision>2011-06-01</revision>\n"
+ " <namespace>urn:ietf:params:xml:ns:netconf:base:1.0</namespace>\n"
+ " </module>\n"
+ DATA_YANG_BASE_IMPORTS
+ " <import-only-module>\n"
+ " <name>ietf-netconf-acm</name>\n"
+ " <revision>2018-02-14</revision>\n"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-netconf-acm</namespace>\n"
+ " </import-only-module>\n"
+ DATA_YANG_SCHEMA_MODULE_STATE
+ " <module>\n"
+ " <name>ietf-netconf</name>\n"
+ " <revision>2011-06-01</revision>\n"
+ " <namespace>urn:ietf:params:xml:ns:netconf:base:1.0</namespace>\n"
+ " <conformance-type>implement</conformance-type>\n"
+ " </module>\n"
+ " <module>\n"
+ " <name>ietf-netconf-acm</name>\n"
+ " <revision>2018-02-14</revision>\n"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-netconf-acm</namespace>\n"
+ " <conformance-type>import</conformance-type>\n"
+ " </module>\n"
+ "</modules-state>";
+
+ char *with_netconf_features = malloc(8096);
+
+ strcpy(with_netconf_features,
+ DATA_YANG_LIBRARY_START
+ " <module>\n"
+ " <name>ietf-netconf</name>\n"
+ " <revision>2011-06-01</revision>\n"
+ " <namespace>urn:ietf:params:xml:ns:netconf:base:1.0</namespace>\n"
+ " <feature>writable-running</feature>\n"
+ " <feature>candidate</feature>\n"
+ " <feature>confirmed-commit</feature>\n"
+ " <feature>rollback-on-error</feature>\n"
+ " <feature>validate</feature>\n"
+ " <feature>startup</feature>\n"
+ " <feature>url</feature>\n"
+ " <feature>xpath</feature>\n"
+ " </module>\n"
+ " <import-only-module>\n"
+ " <name>ietf-yang-metadata</name>\n"
+ " <revision>2016-08-05</revision>\n"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-metadata</namespace>\n"
+ " </import-only-module>\n"
+ " <import-only-module>\n"
+ " <name>ietf-inet-types</name>\n"
+ " <revision>2013-07-15</revision>\n"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-inet-types</namespace>\n"
+ " </import-only-module>\n"
+ " <import-only-module>\n"
+ " <name>ietf-yang-types</name>\n"
+ " <revision>2013-07-15</revision>\n"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-types</namespace>\n"
+ " </import-only-module>\n"
+ " <import-only-module>\n"
+ " <name>ietf-datastores</name>\n"
+ " <revision>2018-02-14</revision>\n"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-datastores</namespace>\n"
+ " </import-only-module>\n"
+ " <import-only-module>\n"
+ " <name>ietf-netconf-acm</name>\n"
+ " <revision>2018-02-14</revision>\n"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-netconf-acm</namespace>\n"
+ " </import-only-module>\n");
+ strcpy(with_netconf_features + strlen(with_netconf_features),
+ DATA_YANG_SCHEMA_MODULE_STATE
+ " <module>\n"
+ " <name>ietf-netconf</name>\n"
+ " <revision>2011-06-01</revision>\n"
+ " <namespace>urn:ietf:params:xml:ns:netconf:base:1.0</namespace>\n"
+ " <feature>writable-running</feature>\n"
+ " <feature>candidate</feature>\n"
+ " <feature>confirmed-commit</feature>\n"
+ " <feature>rollback-on-error</feature>\n"
+ " <feature>validate</feature>\n"
+ " <feature>startup</feature>\n"
+ " <feature>url</feature>\n"
+ " <feature>xpath</feature>\n"
+ " <conformance-type>implement</conformance-type>\n"
+ " </module>\n"
+ " <module>\n"
+ " <name>ietf-netconf-acm</name>\n"
+ " <revision>2018-02-14</revision>\n"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-netconf-acm</namespace>\n"
+ " <conformance-type>import</conformance-type>\n"
+ " </module>\n"
+ "</modules-state>");
+
+ const char *garbage_revision =
+ "<yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">\n"
+ " <module-set>\n"
+ " <name>complete</name>\n"
+ " <module>\n"
+ " <name>yang</name>\n"
+ " <revision>2022-06-16</revision>\n"
+ " <namespace>urn:ietf:params:xml:ns:yang:1</namespace>\n"
+ " </module>\n"
+ " <module>\n"
+ " <name>ietf-yang-library</name>\n"
+ " <revision>2019-01-01</revision>\n"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>\n"
+ " </module>\n"
+ DATA_YANG_BASE_IMPORTS
+ DATA_YANG_SCHEMA_MODULE_STATE
+ "</modules-state>\n";
+
+ const char *no_yanglibrary =
+ "<yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">\n"
+ " <module-set>\n"
+ " <name>complete</name>\n"
+ " <module>\n"
+ " <name>yang</name>\n"
+ " <revision>2022-06-16</revision>\n"
+ " <namespace>urn:ietf:params:xml:ns:yang:1</namespace>\n"
+ " </module>\n"
+ DATA_YANG_BASE_IMPORTS
+ DATA_YANG_SCHEMA_MODULE_STATE
+ "</modules-state>\n";
+
+ (void) state;
+ /* seperate context to avoid double free during teadown */
+ struct ly_ctx *ctx_test = NULL;
+
+ /* test invalid parameters */
+ assert_int_equal(LY_EINVAL, ly_ctx_new_ylpath(NULL, NULL, LYD_XML, 0, &ctx_test));
+ assert_int_equal(LY_EINVAL, ly_ctx_new_ylpath(NULL, TESTS_SRC, LYD_XML, 0, NULL));
+ assert_int_equal(LY_ESYS, ly_ctx_new_ylpath(NULL, TESTS_SRC "garbage", LYD_XML, 0, &ctx_test));
+
+ /* basic test with ietf-yang-library-only */
+ assert_int_equal(LY_SUCCESS, ly_ctx_new_ylmem(TESTS_SRC "/modules/yang/", yanglibrary_only, LYD_XML, 0, &ctx_test));
+ assert_non_null(ly_ctx_get_module(ctx_test, "ietf-yang-library", "2019-01-04"));
+ assert_null(ly_ctx_get_module(ctx_test, "ietf-netconf", "2011-06-01"));
+ ly_ctx_destroy(ctx_test);
+ ctx_test = NULL;
+
+ /* test loading module, should also import other module */
+ assert_int_equal(LY_SUCCESS, ly_ctx_new_ylmem(TESTS_SRC "/modules/yang/", with_netconf, LYD_XML, 0, &ctx_test));
+ assert_non_null(ly_ctx_get_module(ctx_test, "ietf-netconf", "2011-06-01"));
+ assert_int_equal(1, ly_ctx_get_module(ctx_test, "ietf-netconf", "2011-06-01")->implemented);
+ assert_non_null(ly_ctx_get_module(ctx_test, "ietf-netconf-acm", "2018-02-14"));
+ assert_int_equal(0, ly_ctx_get_module(ctx_test, "ietf-netconf-acm", "2018-02-14")->implemented);
+ assert_int_equal(LY_ENOT, lys_feature_value(ly_ctx_get_module(ctx_test, "ietf-netconf", "2011-06-01"), "url"));
+ ly_ctx_destroy(ctx_test);
+ ctx_test = NULL;
+
+ /* test loading module with feature if they are present */
+ assert_int_equal(LY_SUCCESS, ly_ctx_new_ylmem(TESTS_SRC "/modules/yang/", with_netconf_features, LYD_XML, 0, &ctx_test));
+ assert_non_null(ly_ctx_get_module(ctx_test, "ietf-netconf", "2011-06-01"));
+ assert_non_null(ly_ctx_get_module(ctx_test, "ietf-netconf-acm", "2018-02-14"));
+ assert_int_equal(LY_SUCCESS, lys_feature_value(ly_ctx_get_module(ctx_test, "ietf-netconf", "2011-06-01"), "url"));
+ ly_ctx_destroy(ctx_test);
+ ctx_test = NULL;
+
+ /* test with not matching revision */
+ assert_int_equal(LY_EINVAL, ly_ctx_new_ylmem(TESTS_SRC "/modules/yang/", garbage_revision, LYD_XML, 0, &ctx_test));
+
+ /* test data containing ietf-yang-library which conflicts with the option */
+ assert_int_equal(LY_EINVAL, ly_ctx_new_ylmem(TESTS_SRC "/modules/yang/", with_netconf_features, LYD_XML, LY_CTX_NO_YANGLIBRARY, &ctx_test));
+
+ /* test creating without ietf-yang-library */
+ assert_int_equal(LY_SUCCESS, ly_ctx_new_ylmem(TESTS_SRC "/modules/yang/", no_yanglibrary, LYD_XML, LY_CTX_NO_YANGLIBRARY, &ctx_test));
+ assert_int_equal(NULL, ly_ctx_get_module(ctx_test, "ietf-yang-library", "2019-01-04"));
+ ly_ctx_destroy(ctx_test);
+ free(with_netconf_features);
+}
+
+static LY_ERR
+check_node_priv_parsed_is_set(struct lysc_node *node, void *data, ly_bool *UNUSED(dfs_continue))
+{
+ const struct lysp_node *pnode;
+ const char ***iter;
+
+ pnode = (const struct lysp_node *)node->priv;
+ CHECK_POINTER(pnode, 1);
+ iter = (const char ***)data;
+ CHECK_POINTER(**iter, 1);
+ CHECK_STRING(pnode->name, **iter);
+ (*iter)++;
+
+ return LY_SUCCESS;
+}
+
+static LY_ERR
+check_node_priv_parsed_not_set(struct lysc_node *node, void *UNUSED(data), ly_bool *UNUSED(dfs_continue))
+{
+ CHECK_POINTER(node->priv, 0);
+ return LY_SUCCESS;
+}
+
+static void
+check_ext_instance_priv_parsed_is_set(struct lysc_ext_instance *ext)
+{
+ LY_ARRAY_COUNT_TYPE u, v;
+ struct lysc_ext_substmt *substmts;
+ struct lysc_node *cnode;
+ const char **iter;
+ const char *check[] = {
+ "tmp_cont", "lf", NULL
+ };
+
+ LY_ARRAY_FOR(ext, u) {
+ substmts = ext[u].substmts;
+ LY_ARRAY_FOR(substmts, v) {
+ if (substmts && substmts[v].storage && (substmts[v].stmt & LY_STMT_DATA_NODE_MASK)) {
+ cnode = *(struct lysc_node **)substmts[v].storage;
+ iter = check;
+ assert_int_equal(LY_SUCCESS, lysc_tree_dfs_full(cnode, check_node_priv_parsed_is_set, &iter));
+ }
+ }
+ }
+}
+
+static void
+check_ext_instance_priv_parsed_not_set(struct lysc_ext_instance *ext)
+{
+ LY_ARRAY_COUNT_TYPE u, v;
+ struct lysc_ext_substmt *substmts;
+ struct lysc_node *cnode;
+
+ LY_ARRAY_FOR(ext, u) {
+ substmts = ext[u].substmts;
+ LY_ARRAY_FOR(substmts, v) {
+ if (substmts && substmts[v].storage && (substmts[v].stmt & LY_STMT_DATA_NODE_MASK)) {
+ cnode = *(struct lysc_node **)substmts[v].storage;
+ if (cnode) {
+ CHECK_POINTER((struct lysp_node *)cnode->priv, 0);
+ }
+ }
+ }
+ }
+}
+
+/**
+ * @brief Testing of LY_CTX_SET_PRIV_PARSED.
+ */
+static void
+test_set_priv_parsed(void **state)
+{
+ struct lys_module *mod;
+ const char *schema_a;
+ const char **iter;
+ const char *check[] = {
+ "cont", "contnotif", "contx", "grpleaf", "augleaf", "l1",
+ "l1a", "l1b", "l1c", "foo1", "ll", "any", "l2",
+ "l2c", "l2cx", "ch", "cas", "casx", "oper",
+ "input", "inparam", "output", "outparam", "n1", NULL
+ };
+
+ /* each node must have a unique name. */
+ schema_a = "module a {\n"
+ " namespace urn:tests:a;\n"
+ " prefix a;yang-version 1.1;\n"
+ "\n"
+ " import ietf-restconf {\n"
+ " prefix rc;\n"
+ " revision-date 2017-01-26;\n"
+ " }\n"
+ "\n"
+ " rc:yang-data \"tmp\" {\n"
+ " container tmp_cont {\n"
+ " leaf lf {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " container cont {\n"
+ " notification contnotif;\n"
+ " leaf-list contx {\n"
+ " type string;\n"
+ " }\n"
+ " uses grp;\n"
+ " }\n"
+ " list l1 {\n"
+ " key \"l1a l1b\";\n"
+ " leaf l1a {\n"
+ " type string;\n"
+ " }\n"
+ " leaf l1b {\n"
+ " type string;\n"
+ " }\n"
+ " leaf l1c {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " feature f1;\n"
+ " feature f2;\n"
+ " leaf foo1 {\n"
+ " type uint16;\n"
+ " if-feature f1;\n"
+ " }\n"
+ " leaf foo2 {\n"
+ " type uint16;\n"
+ " }\n"
+ " leaf foo3 {\n"
+ " type uint16;\n"
+ " if-feature f2;\n"
+ " }\n"
+ " leaf-list ll {\n"
+ " type string;\n"
+ " }\n"
+ " anydata any {\n"
+ " config false;\n"
+ " }\n"
+ " list l2 {\n"
+ " config false;\n"
+ " container l2c {\n"
+ " leaf l2cx {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " choice ch {\n"
+ " case cas {\n"
+ " leaf casx {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " rpc oper {\n"
+ " input {\n"
+ " leaf inparam {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " output {\n"
+ " leaf outparam {\n"
+ " type int8;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " notification n1;\n"
+ " grouping grp {\n"
+ " leaf grpleaf {\n"
+ " type uint16;\n"
+ " }\n"
+ " }\n"
+ " augment /cont {\n"
+ " leaf augleaf {\n"
+ " type uint16;\n"
+ " }\n"
+ " }\n"
+ " deviation /a:foo2 {\n"
+ " deviate not-supported;\n"
+ " }\n"
+ "}\n";
+
+ /* use own context with extra flags */
+ ly_ctx_destroy(UTEST_LYCTX);
+ const char *feats[] = {"f1", NULL};
+
+ assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_SET_PRIV_PARSED, &UTEST_LYCTX));
+ assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_DIR_MODULES_YANG));
+ assert_non_null(ly_ctx_load_module(UTEST_LYCTX, "ietf-restconf", "2017-01-26", NULL));
+ UTEST_ADD_MODULE(schema_a, LYS_IN_YANG, feats, NULL);
+
+ print_message("[ ] create context\n");
+ mod = ly_ctx_get_module(UTEST_LYCTX, "a", NULL);
+ iter = check;
+ assert_int_equal(LY_SUCCESS, lysc_module_dfs_full(mod, check_node_priv_parsed_is_set, &iter));
+ check_ext_instance_priv_parsed_is_set(mod->compiled->exts);
+
+ print_message("[ ] unset option\n");
+ assert_int_equal(LY_SUCCESS, ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED));
+ mod = ly_ctx_get_module(UTEST_LYCTX, "a", NULL);
+ iter = check;
+ assert_int_equal(LY_SUCCESS, lysc_module_dfs_full(mod, check_node_priv_parsed_not_set, &iter));
+ check_ext_instance_priv_parsed_not_set(mod->compiled->exts);
+
+ print_message("[ ] set option\n");
+ assert_int_equal(LY_SUCCESS, ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED));
+ mod = ly_ctx_get_module(UTEST_LYCTX, "a", NULL);
+ iter = check;
+ assert_int_equal(LY_SUCCESS, lysc_module_dfs_full(mod, check_node_priv_parsed_is_set, &iter));
+ check_ext_instance_priv_parsed_is_set(mod->compiled->exts);
+}
+
+static void
+test_explicit_compile(void **state)
+{
+ uint32_t i;
+ struct lys_module *mod;
+ const char *schema_a = "module a {\n"
+ " namespace urn:tests:a;\n"
+ " prefix a;yang-version 1.1;\n"
+ " feature f1;\n"
+ " feature f2;\n"
+ " leaf foo1 {\n"
+ " type uint16;\n"
+ " if-feature f1;\n"
+ " }\n"
+ " leaf foo2 {\n"
+ " type uint16;\n"
+ " }\n"
+ " container cont {\n"
+ " leaf foo3 {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ "}\n";
+ const char *schema_b = "module b {\n"
+ " namespace urn:tests:b;\n"
+ " prefix b;yang-version 1.1;\n"
+ " import a {\n"
+ " prefix a;\n"
+ " }\n"
+ " augment /a:cont {\n"
+ " leaf augleaf {\n"
+ " type uint16;\n"
+ " }\n"
+ " }\n"
+ "}\n";
+ const char *schema_c = "module c {\n"
+ " namespace urn:tests:c;\n"
+ " prefix c;yang-version 1.1;\n"
+ " import a {\n"
+ " prefix a;\n"
+ " }\n"
+ " deviation /a:foo2 {\n"
+ " deviate not-supported;\n"
+ " }\n"
+ "}\n";
+
+ /* use own context with extra flags */
+ ly_ctx_destroy(UTEST_LYCTX);
+ const char *feats[] = {"f1", NULL};
+
+ assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_EXPLICIT_COMPILE, &UTEST_LYCTX));
+ UTEST_ADD_MODULE(schema_a, LYS_IN_YANG, NULL, &mod);
+ UTEST_ADD_MODULE(schema_b, LYS_IN_YANG, NULL, NULL);
+ UTEST_ADD_MODULE(schema_c, LYS_IN_YANG, NULL, NULL);
+ assert_int_equal(LY_SUCCESS, lys_set_implemented((struct lys_module *)mod, feats));
+
+ /* none of the modules should be compiled */
+ i = 0;
+ while ((mod = ly_ctx_get_module_iter(UTEST_LYCTX, &i))) {
+ assert_null(mod->compiled);
+ }
+
+ assert_int_equal(LY_SUCCESS, ly_ctx_compile(UTEST_LYCTX));
+
+ /* check internal modules */
+ mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "yang");
+ assert_non_null(mod);
+ mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "ietf-datastores");
+ assert_non_null(mod);
+ mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "ietf-yang-library");
+ assert_non_null(mod);
+
+ /* check test modules */
+ mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "a");
+ assert_non_null(mod);
+ mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "b");
+ assert_non_null(mod);
+ mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "c");
+ assert_non_null(mod);
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_searchdirs),
+ UTEST(test_options),
+ UTEST(test_models),
+ UTEST(test_imports),
+ UTEST(test_get_models),
+ UTEST(test_ylmem),
+ UTEST(test_set_priv_parsed),
+ UTEST(test_explicit_compile),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/basic/test_hash_table.c b/tests/utests/basic/test_hash_table.c
new file mode 100644
index 0000000..2ea34fa
--- /dev/null
+++ b/tests/utests/basic/test_hash_table.c
@@ -0,0 +1,262 @@
+/**
+ * @file test_hash_table.c
+ * @author: Radek Krejci <rkrejci@cesnet.cz>
+ * @brief unit tests for functions from hash_table.c
+ *
+ * Copyright (c) 2018 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
+ */
+#define _UTEST_MAIN_
+#include "utests.h"
+
+#include <stdlib.h>
+
+#include "hash_table.h"
+#include "ly_common.h"
+
+static void
+test_invalid_arguments(void **state)
+{
+ assert_int_equal(LY_EINVAL, lydict_insert(NULL, NULL, 0, NULL));
+ CHECK_LOG_LASTMSG("Invalid argument ctx (lydict_insert()).");
+
+ assert_int_equal(LY_EINVAL, lydict_insert_zc(NULL, NULL, NULL));
+ CHECK_LOG_LASTMSG("Invalid argument ctx (lydict_insert_zc()).");
+ assert_int_equal(LY_EINVAL, lydict_insert_zc(UTEST_LYCTX, NULL, NULL));
+ CHECK_LOG_CTX("Invalid argument str_p (lydict_insert_zc()).", NULL, 0);
+}
+
+static void
+test_dict_hit(void **state)
+{
+ const char *str1, *str2, *str3;
+
+ /* insert 2 strings, one of them repeatedly */
+ assert_int_equal(LY_SUCCESS, lydict_insert(UTEST_LYCTX, "test1", 0, &str1));
+ assert_non_null(str1);
+ /* via zerocopy we have to get the same pointer as provided */
+ assert_non_null(str2 = strdup("test2"));
+ assert_int_equal(LY_SUCCESS, lydict_insert_zc(UTEST_LYCTX, (char *)str2, &str3));
+ assert_ptr_equal(str2, str3);
+ /* here we get the same pointer as in case the string was inserted first time */
+ assert_int_equal(LY_SUCCESS, lydict_insert(UTEST_LYCTX, "test1", 0, &str2));
+ assert_non_null(str2);
+ assert_ptr_equal(str1, str2);
+
+ /* remove strings, but the repeatedly inserted only once */
+ lydict_remove(UTEST_LYCTX, "test1");
+ lydict_remove(UTEST_LYCTX, "test2");
+
+ /* destroy dictionary - should raise warning about data presence */
+ ly_ctx_destroy(UTEST_LYCTX);
+ UTEST_LYCTX = NULL;
+ CHECK_LOG_LASTMSG("String \"test1\" not freed from the dictionary, refcount 1.");
+
+#ifndef NDEBUG
+ /* cleanup */
+ free((char *)str1);
+#endif
+}
+
+static uint8_t
+ht_equal_clb(void *val1, void *val2, uint8_t mod, void *cb_data)
+{
+ int *v1, *v2;
+
+ (void)mod;
+ (void)cb_data;
+
+ v1 = (int *)val1;
+ v2 = (int *)val2;
+
+ return *v1 == *v2;
+}
+
+static void
+test_ht_basic(void **UNUSED(state))
+{
+ uint32_t i;
+ struct ly_ht *ht;
+
+ assert_non_null(ht = lyht_new(8, sizeof(int), ht_equal_clb, NULL, 0));
+
+ i = 2;
+ assert_int_equal(LY_ENOTFOUND, lyht_find(ht, &i, i, NULL));
+ assert_int_equal(LY_SUCCESS, lyht_insert(ht, &i, i, NULL));
+ assert_int_equal(LY_SUCCESS, lyht_find(ht, &i, i, NULL));
+ assert_int_equal(LY_SUCCESS, lyht_remove(ht, &i, i));
+ assert_int_equal(LY_ENOTFOUND, lyht_find(ht, &i, i, NULL));
+ assert_int_equal(LY_ENOTFOUND, lyht_remove(ht, &i, i));
+ CHECK_LOG_LASTMSG("Invalid argument hash (lyht_remove_with_resize_cb()).");
+
+ lyht_free(ht, NULL);
+}
+
+static void
+test_ht_resize(void **UNUSED(state))
+{
+ uint32_t i;
+ struct ly_ht *ht;
+
+ assert_non_null(ht = lyht_new(8, sizeof(int), ht_equal_clb, NULL, 1));
+ assert_int_equal(8, ht->size);
+
+ /* insert records into indexes 2-7 */
+ for (i = 2; i < 8; ++i) {
+ assert_int_equal(LY_SUCCESS, lyht_insert(ht, &i, i, NULL));
+ }
+ /* check that table resized */
+ assert_int_equal(16, ht->size);
+
+ /* check expected content of the table */
+ for (i = 0; i < 16; ++i) {
+ if ((i >= 2) && (i < 8)) {
+ /* inserted data on indexes 2-7 */
+ assert_int_not_equal(UINT32_MAX, ht->hlists[i].first);
+ assert_int_equal(LY_SUCCESS, lyht_find(ht, &i, i, NULL));
+ } else {
+ /* nothing otherwise */
+ assert_int_equal(UINT32_MAX, ht->hlists[i].first);
+ assert_int_equal(LY_ENOTFOUND, lyht_find(ht, &i, i, NULL));
+ }
+ }
+
+ /* removing not present data should fail */
+ for (i = 0; i < 2; ++i) {
+ assert_int_equal(LY_ENOTFOUND, lyht_remove(ht, &i, i));
+ CHECK_LOG_LASTMSG("Invalid argument hash (lyht_remove_with_resize_cb()).");
+ }
+ /* removing present data, resize should happened
+ * when we are below 25% of the table filled, so with 3 records left */
+ for ( ; i < 5; ++i) {
+ assert_int_equal(LY_SUCCESS, lyht_remove(ht, &i, i));
+ }
+ assert_int_equal(8, ht->size);
+
+ /* remove the rest */
+ for ( ; i < 8; ++i) {
+ assert_int_equal(LY_SUCCESS, lyht_remove(ht, &i, i));
+ }
+
+ for (i = 0; i < 8; ++i) {
+ assert_int_equal(LY_ENOTFOUND, lyht_find(ht, &i, i, NULL));
+ }
+
+ /* cleanup */
+ lyht_free(ht, NULL);
+}
+
+static void
+test_ht_collisions(void **UNUSED(state))
+{
+#define GET_REC_INT(rec) (*((uint32_t *)&(rec)->val))
+
+ uint32_t i;
+ struct ly_ht_rec *rec;
+ struct ly_ht *ht;
+ uint32_t rec_idx;
+ int count;
+
+ assert_non_null(ht = lyht_new(8, sizeof(int), ht_equal_clb, NULL, 1));
+
+ for (i = 2; i < 6; ++i) {
+ assert_int_equal(lyht_insert(ht, &i, 2, NULL), 0);
+ }
+
+ /* check all records */
+ for (i = 0; i < 8; ++i) {
+ if (i == 2) {
+ assert_int_not_equal(UINT32_MAX, ht->hlists[i].first);
+ } else {
+ assert_int_equal(UINT32_MAX, ht->hlists[i].first);
+ }
+ }
+ for (i = 0; i < 8; ++i) {
+ if ((i >= 2) && (i < 6)) {
+ assert_int_equal(LY_SUCCESS, lyht_find(ht, &i, 2, NULL));
+ } else {
+ assert_int_equal(LY_ENOTFOUND, lyht_find(ht, &i, 2, NULL));
+ }
+ }
+ rec_idx = ht->hlists[2].first;
+ count = 0;
+ while (rec_idx != UINT32_MAX) {
+ rec = lyht_get_rec(ht->recs, ht->rec_size, rec_idx);
+ rec_idx = rec->next;
+ assert_int_equal(rec->hash, 2);
+ count++;
+ }
+ assert_int_equal(count, 4);
+
+ i = 4;
+ assert_int_equal(lyht_remove(ht, &i, 2), 0);
+
+ rec = lyht_get_rec(ht->recs, ht->rec_size, i);
+
+ i = 2;
+ assert_int_equal(lyht_remove(ht, &i, 2), 0);
+
+ /* check all records */
+ for (i = 0; i < 8; ++i) {
+ if (i == 2) {
+ assert_int_not_equal(UINT32_MAX, ht->hlists[i].first);
+ } else {
+ assert_int_equal(UINT32_MAX, ht->hlists[i].first);
+ }
+ }
+ for (i = 0; i < 8; ++i) {
+ if ((i == 3) || (i == 5)) {
+ assert_int_equal(LY_SUCCESS, lyht_find(ht, &i, 2, NULL));
+ } else {
+ assert_int_equal(LY_ENOTFOUND, lyht_find(ht, &i, 2, NULL));
+ }
+ }
+ rec_idx = ht->hlists[2].first;
+ count = 0;
+ while (rec_idx != UINT32_MAX) {
+ rec = lyht_get_rec(ht->recs, ht->rec_size, rec_idx);
+ rec_idx = rec->next;
+ assert_int_equal(rec->hash, 2);
+ count++;
+ }
+ assert_int_equal(count, 2);
+
+ for (i = 0; i < 8; ++i) {
+ if ((i == 3) || (i == 5)) {
+ assert_int_equal(lyht_find(ht, &i, 2, NULL), LY_SUCCESS);
+ } else {
+ assert_int_equal(lyht_find(ht, &i, 2, NULL), LY_ENOTFOUND);
+ }
+ }
+
+ i = 3;
+ assert_int_equal(lyht_remove(ht, &i, 2), 0);
+ i = 5;
+ assert_int_equal(lyht_remove(ht, &i, 2), 0);
+
+ /* check all records */
+ for (i = 0; i < 8; ++i) {
+ assert_int_equal(UINT32_MAX, ht->hlists[i].first);
+ }
+
+ lyht_free(ht, NULL);
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_invalid_arguments),
+ UTEST(test_dict_hit),
+ UTEST(test_ht_basic),
+ UTEST(test_ht_resize),
+ UTEST(test_ht_collisions),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/basic/test_inout.c b/tests/utests/basic/test_inout.c
new file mode 100644
index 0000000..af8cc81
--- /dev/null
+++ b/tests/utests/basic/test_inout.c
@@ -0,0 +1,407 @@
+/**
+ * @file test_inout.c
+ * @author: Radek Krejci <rkrejci@cesnet.cz>
+ * @brief unit tests for input and output handlers functions
+ *
+ * Copyright (c) 2020 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
+ */
+#define _UTEST_MAIN_
+#include "utests.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "in.h"
+#include "log.h"
+#include "ly_common.h"
+#include "out.h"
+
+#define TEST_INPUT_FILE TESTS_BIN "/libyang_test_input"
+#define TEST_OUTPUT_FILE TESTS_BIN "/libyang_test_output"
+#define TEST_OUTPUT_FILE2 TESTS_BIN "/libyang_test_output2"
+
+static int
+setup_files(void **state)
+{
+ int fd;
+
+ UTEST_SETUP;
+
+ /* create input */
+ fd = open(TEST_INPUT_FILE, O_CREAT | O_WRONLY, 00600);
+ if (fd == -1) {
+ return 1;
+ }
+
+ /* write something */
+ if (write(fd, "data", 4) != 4) {
+ return 1;
+ }
+ close(fd);
+
+ /* create output */
+ fd = open(TEST_OUTPUT_FILE, O_CREAT | O_RDONLY, 00600);
+ if (fd == -1) {
+ return 1;
+ }
+ close(fd);
+
+ /* create output2 */
+ fd = open(TEST_OUTPUT_FILE2, O_CREAT | O_RDONLY, 00600);
+ if (fd == -1) {
+ return 1;
+ }
+ close(fd);
+
+ return 0;
+}
+
+static int
+teardown_files(void **state)
+{
+ unlink(TEST_INPUT_FILE);
+ unlink(TEST_OUTPUT_FILE);
+ unlink(TEST_OUTPUT_FILE2);
+
+ UTEST_TEARDOWN;
+ return 0;
+}
+
+static void
+test_input_mem(void **UNUSED(state))
+{
+ struct ly_in *in = NULL;
+ char *str1 = "a", *str2 = "b";
+
+ assert_int_equal(LY_EINVAL, ly_in_new_memory(NULL, NULL));
+ CHECK_LOG_LASTMSG("Invalid argument str (ly_in_new_memory()).");
+ assert_int_equal(LY_EINVAL, ly_in_new_memory(str1, NULL));
+ CHECK_LOG_LASTMSG("Invalid argument in (ly_in_new_memory()).");
+ assert_null(ly_in_memory(NULL, NULL));
+ CHECK_LOG_LASTMSG("Invalid argument in (ly_in_memory()).");
+
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str1, &in));
+ assert_int_equal(LY_IN_MEMORY, ly_in_type(in));
+ assert_ptr_equal(str1, ly_in_memory(in, str2));
+ assert_ptr_equal(str2, ly_in_memory(in, NULL));
+ assert_ptr_equal(str2, ly_in_memory(in, NULL));
+ ly_in_free(in, 0);
+}
+
+static void
+test_input_fd(void **UNUSED(state))
+{
+ struct ly_in *in = NULL;
+ int fd1, fd2;
+ struct stat statbuf;
+
+ assert_int_equal(LY_EINVAL, ly_in_new_fd(-1, NULL));
+ CHECK_LOG_LASTMSG("Invalid argument fd >= 0 (ly_in_new_fd()).");
+ assert_int_equal(-1, ly_in_fd(NULL, -1));
+ CHECK_LOG_LASTMSG("Invalid argument in (ly_in_fd()).");
+
+ assert_int_not_equal(-1, fd1 = open(TEST_INPUT_FILE, O_RDONLY));
+ assert_int_not_equal(-1, fd2 = open(TEST_INPUT_FILE, O_RDONLY));
+
+ assert_int_equal(LY_EINVAL, ly_in_new_fd(fd1, NULL));
+ CHECK_LOG_LASTMSG("Invalid argument in (ly_in_new_fd()).");
+
+ assert_int_equal(LY_SUCCESS, ly_in_new_fd(fd1, &in));
+ assert_int_equal(LY_IN_FD, ly_in_type(in));
+ assert_ptr_equal(fd1, ly_in_fd(in, fd2));
+ assert_ptr_equal(fd2, ly_in_fd(in, -1));
+ assert_ptr_equal(fd2, ly_in_fd(in, -1));
+ ly_in_free(in, 1);
+ /* fd1 is still open */
+ assert_int_equal(0, fstat(fd1, &statbuf));
+ close(fd1);
+#ifndef _WIN32
+ /* But fd2 was closed by ly_in_free(). This results in an "invalid handler" on Windows. */
+ errno = 0;
+ assert_int_equal(-1, fstat(fd2, &statbuf));
+ assert_int_equal(errno, EBADF);
+#endif
+}
+
+static void
+test_input_file(void **UNUSED(state))
+{
+ struct ly_in *in = NULL;
+ FILE *f1 = NULL, *f2 = NULL;
+
+ assert_int_equal(LY_EINVAL, ly_in_new_file(NULL, NULL));
+ assert_null(ly_in_file(NULL, NULL));
+
+ assert_non_null(f1 = fopen(TEST_INPUT_FILE, "rb"));
+ assert_non_null(f2 = fopen(TEST_INPUT_FILE, "rb"));
+
+ assert_int_equal(LY_EINVAL, ly_in_new_file(f1, NULL));
+
+ assert_int_equal(LY_SUCCESS, ly_in_new_file(f1, &in));
+ assert_int_equal(LY_IN_FILE, ly_in_type(in));
+ assert_ptr_equal(f1, ly_in_file(in, f2));
+ assert_ptr_equal(f2, ly_in_file(in, NULL));
+ assert_ptr_equal(f2, ly_in_file(in, NULL));
+ ly_in_free(in, 1);
+ /* f1 is still open */
+ assert_int_not_equal(-1, fileno(f1));
+ fclose(f1);
+ /* but f2 was closed by ly_in_free() */
+}
+
+static void
+test_input_filepath(void **UNUSED(state))
+{
+ struct ly_in *in = NULL;
+ const char *path1 = TEST_INPUT_FILE, *path2 = TEST_INPUT_FILE;
+
+ assert_int_equal(LY_EINVAL, ly_in_new_filepath(NULL, 0, NULL));
+ assert_int_equal(LY_EINVAL, ly_in_new_filepath(path1, 0, NULL));
+ assert_ptr_equal(((void *)-1), ly_in_filepath(NULL, NULL, 0));
+
+ assert_int_equal(LY_SUCCESS, ly_in_new_filepath(path1, 0, &in));
+ assert_int_equal(LY_IN_FILEPATH, ly_in_type(in));
+ assert_ptr_equal(NULL, ly_in_filepath(in, path2, 0));
+ assert_string_equal(path2, ly_in_filepath(in, NULL, 0));
+ ly_in_free(in, 0);
+}
+
+static void
+test_output_mem(void **UNUSED(state))
+{
+ struct ly_out *out = NULL;
+ char *buf1 = NULL, *buf2 = NULL;
+
+ /* manipulate with the handler */
+ assert_int_equal(LY_SUCCESS, ly_out_new_memory(&buf1, 0, &out));
+ assert_int_equal(LY_OUT_MEMORY, ly_out_type(out));
+ ly_write(out, "test", 4);
+ assert_ptr_equal(buf1, ly_out_memory(out, &buf2, 0));
+ assert_ptr_equal(buf2, ly_out_memory(out, NULL, 0));
+ assert_ptr_equal(buf2, ly_out_memory(out, &buf1, strlen(buf1)));
+ ly_out_free(out, NULL, 0);
+
+ assert_int_equal(LY_SUCCESS, ly_out_new_memory(&buf1, strlen(buf1), &out));
+ ly_out_free(out, NULL, 1);
+
+ /* writing data */
+
+ assert_int_equal(LY_SUCCESS, ly_out_new_memory(&buf1, 0, &out));
+ assert_int_equal(LY_SUCCESS, ly_print(out, "test %s", "print"));
+ assert_int_equal(10, ly_out_printed(out));
+ assert_string_equal("test print", buf1);
+ assert_int_equal(LY_SUCCESS, ly_out_reset(out));
+ assert_int_equal(LY_SUCCESS, ly_write(out, "rewrite", 8));
+ assert_int_equal(8, ly_out_printed(out));
+ assert_string_equal("rewrite", buf1);
+ ly_out_free(out, NULL, 1);
+}
+
+static void
+test_output_fd(void **UNUSED(state))
+{
+ struct ly_out *out = NULL;
+ int fd1, fd2;
+ char buf[31] = {0};
+
+ assert_int_not_equal(-1, fd1 = open(TEST_OUTPUT_FILE, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR));
+ assert_int_not_equal(-1, fd2 = open(TEST_OUTPUT_FILE, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR));
+
+ /* manipulate with the handler */
+ assert_int_equal(LY_SUCCESS, ly_out_new_fd(fd1, &out));
+ assert_int_equal(LY_OUT_FD, ly_out_type(out));
+ assert_ptr_equal(fd1, ly_out_fd(out, fd2));
+ assert_ptr_equal(fd2, ly_out_fd(out, -1));
+ assert_ptr_equal(fd2, ly_out_fd(out, fd1));
+ ly_out_free(out, NULL, 0);
+ assert_int_equal(0, close(fd2));
+ assert_int_equal(LY_SUCCESS, ly_out_new_fd(fd1, &out));
+ ly_out_free(out, NULL, 1);
+
+ /* writing data */
+ assert_int_not_equal(-1, fd1 = open(TEST_OUTPUT_FILE, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR));
+ assert_int_not_equal(-1, fd2 = open(TEST_OUTPUT_FILE, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR));
+ /* truncate file to start with no data */
+ assert_int_equal(0, ftruncate(fd1, 0));
+
+ assert_int_equal(LY_SUCCESS, ly_out_new_fd(fd1, &out));
+ assert_int_equal(LY_SUCCESS, ly_print(out, "test %s", "print"));
+ assert_int_equal(10, ly_out_printed(out));
+ ly_print_flush(out);
+ assert_int_equal(10, read(fd2, buf, 30));
+ assert_string_equal("test print", buf);
+ assert_int_equal(0, lseek(fd2, 0, SEEK_SET));
+ assert_int_equal(LY_SUCCESS, ly_out_reset(out));
+
+ assert_int_equal(LY_SUCCESS, ly_write(out, "rewrite", 8));
+ assert_int_equal(8, ly_out_printed(out));
+ ly_print_flush(out);
+ assert_int_equal(8, read(fd2, buf, 30));
+ assert_string_equal("rewrite", buf);
+
+ close(fd2);
+ ly_out_free(out, NULL, 1);
+}
+
+static void
+test_output_file(void **UNUSED(state))
+{
+ struct ly_out *out = NULL;
+ FILE *f1, *f2;
+ char buf[31] = {0};
+
+ assert_non_null(f1 = fopen(TEST_OUTPUT_FILE, "wb"));
+ assert_non_null(f2 = fopen(TEST_OUTPUT_FILE, "wb"));
+
+ /* manipulate with the handler */
+ assert_int_equal(LY_SUCCESS, ly_out_new_file(f1, &out));
+ assert_int_equal(LY_OUT_FILE, ly_out_type(out));
+ assert_ptr_equal(f1, ly_out_file(out, f2));
+ assert_ptr_equal(f2, ly_out_file(out, NULL));
+ assert_ptr_equal(f2, ly_out_file(out, f1));
+ ly_out_free(out, NULL, 0);
+ assert_int_equal(0, fclose(f2));
+ assert_int_equal(LY_SUCCESS, ly_out_new_file(f1, &out));
+ ly_out_free(out, NULL, 1);
+
+ /* writing data */
+ assert_non_null(f1 = fopen(TEST_OUTPUT_FILE, "wb"));
+ assert_non_null(f2 = fopen(TEST_OUTPUT_FILE, "rb"));
+
+ assert_int_equal(LY_SUCCESS, ly_out_new_file(f1, &out));
+ assert_int_equal(LY_SUCCESS, ly_print(out, "test %s", "print"));
+ assert_int_equal(10, ly_out_printed(out));
+ ly_print_flush(out);
+ assert_non_null(fgets(buf, 31, f2));
+ assert_string_equal("test print", buf);
+ assert_int_equal(0, fseek(f2, 0, SEEK_SET));
+ assert_int_equal(LY_SUCCESS, ly_out_reset(out));
+
+ assert_int_equal(LY_SUCCESS, ly_write(out, "rewrite", 8));
+ assert_int_equal(8, ly_out_printed(out));
+ ly_print_flush(out);
+ assert_non_null(fgets(buf, 31, f2));
+ assert_string_equal("rewrite", buf);
+
+ fclose(f2);
+ ly_out_free(out, NULL, 1);
+}
+
+static void
+test_output_filepath(void **UNUSED(state))
+{
+ struct ly_out *out = NULL;
+ FILE *f1;
+ char buf[31] = {0};
+ const char *fp1 = TEST_OUTPUT_FILE;
+ const char *fp2 = TEST_OUTPUT_FILE2;
+
+ /* manipulate with the handler */
+ assert_int_equal(LY_SUCCESS, ly_out_new_filepath(fp1, &out));
+ assert_int_equal(LY_OUT_FILEPATH, ly_out_type(out));
+ assert_ptr_equal(NULL, ly_out_filepath(out, fp2));
+ assert_string_equal(fp2, ly_out_filepath(out, NULL));
+ assert_ptr_equal(NULL, ly_out_filepath(out, fp1));
+ ly_out_free(out, NULL, 0);
+ assert_int_equal(LY_SUCCESS, ly_out_new_filepath(fp1, &out));
+ ly_out_free(out, NULL, 1);
+
+ /* writing data */
+ assert_non_null(f1 = fopen(fp1, "rb"));
+
+ assert_int_equal(LY_SUCCESS, ly_out_new_filepath(fp1, &out));
+ assert_int_equal(LY_SUCCESS, ly_print(out, "test %s", "print"));
+ assert_int_equal(10, ly_out_printed(out));
+ ly_print_flush(out);
+ assert_non_null(fgets(buf, 31, f1));
+ assert_string_equal("test print", buf);
+ assert_int_equal(0, fseek(f1, 0, SEEK_SET));
+ assert_int_equal(LY_SUCCESS, ly_out_reset(out));
+
+ assert_int_equal(LY_SUCCESS, ly_write(out, "rewrite", 8));
+ assert_int_equal(8, ly_out_printed(out));
+ ly_print_flush(out);
+ assert_non_null(fgets(buf, 31, f1));
+ assert_string_equal("rewrite", buf);
+
+ fclose(f1);
+ ly_out_free(out, NULL, 1);
+}
+
+static ssize_t
+write_clb(void *user_data, const void *buf, size_t count)
+{
+ return write((uintptr_t)user_data, buf, count);
+}
+
+void
+close_clb(void *arg)
+{
+ close((uintptr_t)arg);
+}
+
+static void
+test_output_clb(void **UNUSED(state))
+{
+ struct ly_out *out = NULL;
+ int fd1, fd2;
+ char buf[31] = {0};
+
+ assert_int_not_equal(-1, fd1 = open(TEST_OUTPUT_FILE, O_RDWR));
+ assert_int_not_equal(-1, fd2 = open(TEST_OUTPUT_FILE, O_RDWR));
+
+ /* manipulate with the handler */
+ assert_int_equal(LY_SUCCESS, ly_out_new_clb(write_clb, (void *)(intptr_t)fd1, &out));
+ assert_int_equal(LY_OUT_CALLBACK, ly_out_type(out));
+ assert_ptr_equal(fd1, ly_out_clb_arg(out, (void *)(intptr_t)fd2));
+ assert_ptr_equal(fd2, ly_out_clb_arg(out, NULL));
+ assert_ptr_equal(fd2, ly_out_clb_arg(out, (void *)(intptr_t)fd1));
+ assert_ptr_equal(write_clb, ly_out_clb(out, write_clb));
+ ly_out_free(out, NULL, 0);
+ assert_int_equal(0, close(fd2));
+ assert_int_equal(LY_SUCCESS, ly_out_new_clb(write_clb, (void *)(intptr_t)fd1, &out));
+ ly_out_free(out, close_clb, 0);
+
+ /* writing data */
+ assert_int_not_equal(-1, fd1 = open(TEST_OUTPUT_FILE, O_RDWR));
+ assert_int_not_equal(-1, fd2 = open(TEST_OUTPUT_FILE, O_RDWR));
+ /* truncate file to start with no data */
+ assert_int_equal(0, ftruncate(fd1, 0));
+
+ assert_int_equal(LY_SUCCESS, ly_out_new_clb(write_clb, (void *)(intptr_t)fd1, &out));
+ assert_int_equal(LY_SUCCESS, ly_print(out, "test %s", "print"));
+ assert_int_equal(10, ly_out_printed(out));
+ assert_int_equal(10, read(fd2, buf, 30));
+ assert_string_equal("test print", buf);
+
+ close(fd2);
+ ly_out_free(out, close_clb, 0);
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_input_mem),
+ UTEST(test_input_fd, setup_files, teardown_files),
+ UTEST(test_input_file, setup_files, teardown_files),
+ UTEST(test_input_filepath, setup_files, teardown_files),
+ UTEST(test_output_mem),
+ UTEST(test_output_fd, setup_files, teardown_files),
+ UTEST(test_output_file, setup_files, teardown_files),
+ UTEST(test_output_filepath, setup_files, teardown_files),
+ UTEST(test_output_clb, setup_files, teardown_files),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/basic/test_json.c b/tests/utests/basic/test_json.c
new file mode 100644
index 0000000..a760b38
--- /dev/null
+++ b/tests/utests/basic/test_json.c
@@ -0,0 +1,773 @@
+/**
+ * @file test_json.c
+ * @author: Radek Krejci <rkrejci@cesnet.cz>
+ * @brief unit tests for a generic JSON parser
+ *
+ * Copyright (c) 2020 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
+ */
+#define _UTEST_MAIN_
+#include "utests.h"
+
+#include "context.h"
+#include "in_internal.h"
+#include "json.h"
+static void
+test_general(void **state)
+{
+ struct lyjson_ctx *jsonctx;
+ struct ly_in *in;
+ const char *str;
+
+ /* empty */
+ str = "";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ CHECK_LOG_CTX("Empty JSON file.", NULL, 1);
+
+ str = " \n\t \n";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ CHECK_LOG_CTX("Empty JSON file.", NULL, 3);
+
+ /* constant values */
+ str = "true";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_TRUE, lyjson_ctx_status(jsonctx));
+
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx));
+ lyjson_ctx_free(jsonctx);
+
+ str = "false";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_FALSE, lyjson_ctx_status(jsonctx));
+
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx));
+ lyjson_ctx_free(jsonctx);
+
+ str = "null";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NULL, lyjson_ctx_status(jsonctx));
+
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx));
+ lyjson_ctx_free(jsonctx);
+
+ ly_in_free(in, 0);
+}
+
+static void
+test_number(void **state)
+{
+ struct lyjson_ctx *jsonctx;
+ struct ly_in *in;
+ const char *str;
+
+ /* simple value */
+ str = "11";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
+ assert_string_equal("11", jsonctx->value);
+ assert_int_equal(2, jsonctx->value_len);
+ assert_int_equal(0, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ /* fraction number */
+ str = "37.7668";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
+ assert_string_equal("37.7668", jsonctx->value);
+ assert_int_equal(7, jsonctx->value_len);
+ assert_int_equal(0, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ /* negative number */
+ str = "-122.3959";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
+ assert_string_equal("-122.3959", jsonctx->value);
+ assert_int_equal(9, jsonctx->value_len);
+ assert_int_equal(0, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ /* integer, positive exponent */
+ str = "550E3";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
+ assert_string_equal("550000", jsonctx->value);
+ assert_int_equal(6, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "-550E3";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
+ assert_string_equal("-550000", jsonctx->value);
+ assert_int_equal(7, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ /* integer, negative exponent */
+ str = "1E-1";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
+ assert_string_equal("0.1", jsonctx->value);
+ assert_int_equal(3, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "15E-1";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
+ assert_string_equal("1.5", jsonctx->value);
+ assert_int_equal(3, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "-15E-1";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
+ assert_string_equal("-1.5", jsonctx->value);
+ assert_int_equal(4, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "16E-2";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
+ assert_string_equal("0.16", jsonctx->value);
+ assert_int_equal(4, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "-16E-2";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
+ assert_string_equal("-0.16", jsonctx->value);
+ assert_int_equal(5, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "17E-3";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
+ assert_string_equal("0.017", jsonctx->value);
+ assert_int_equal(5, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "-17E-3";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
+ assert_string_equal("-0.017", jsonctx->value);
+ assert_int_equal(6, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "21000E-2";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
+ assert_string_equal("210", jsonctx->value);
+ assert_int_equal(3, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "21000E-4";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
+ assert_string_equal("2.1", jsonctx->value);
+ assert_int_equal(3, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "21000E-7";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
+ assert_string_equal("0.0021", jsonctx->value);
+ assert_int_equal(6, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ /* decimal number, positive exponent */
+ str = "5.087E1";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
+ assert_string_equal("50.87", jsonctx->value);
+ assert_int_equal(5, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "-5.087E1";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
+ assert_string_equal("-50.87", jsonctx->value);
+ assert_int_equal(6, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "5.087E5";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
+ assert_string_equal("508700", jsonctx->value);
+ assert_int_equal(6, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "59.1e+1";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
+ assert_string_equal("591", jsonctx->value);
+ assert_int_equal(3, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "0.005087E1";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
+ assert_string_equal("0.05087", jsonctx->value);
+ assert_int_equal(7, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "0.005087E2";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
+ assert_string_equal("0.5087", jsonctx->value);
+ assert_int_equal(6, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "0.005087E6";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
+ assert_string_equal("5087", jsonctx->value);
+ assert_int_equal(4, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "0.05087E6";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
+ assert_string_equal("50870", jsonctx->value);
+ assert_int_equal(5, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "0.005087E8";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
+ assert_string_equal("508700", jsonctx->value);
+ assert_int_equal(6, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ /* decimal number, negative exponent */
+ str = "35.94e-1";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
+ assert_string_equal("3.594", jsonctx->value);
+ assert_int_equal(5, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "-35.94e-1";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
+ assert_string_equal("-3.594", jsonctx->value);
+ assert_int_equal(6, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "35.94e-2";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
+ assert_string_equal("0.3594", jsonctx->value);
+ assert_int_equal(6, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "35.94e-3";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
+ assert_string_equal("0.03594", jsonctx->value);
+ assert_int_equal(7, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "0.3594e-1";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
+ assert_string_equal("0.03594", jsonctx->value);
+ assert_int_equal(7, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "0.03594e-1";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
+ assert_string_equal("0.003594", jsonctx->value);
+ assert_int_equal(8, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "0.003594e-1";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
+ assert_string_equal("0.0003594", jsonctx->value);
+ assert_int_equal(9, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "0.3594e-2";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
+ assert_string_equal("0.003594", jsonctx->value);
+ assert_int_equal(8, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "0.03594e-2";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
+ assert_string_equal("0.0003594", jsonctx->value);
+ assert_int_equal(9, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "0.003594e-2";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
+ assert_string_equal("0.00003594", jsonctx->value);
+ assert_int_equal(10, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ /* zero */
+ str = "0";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
+ assert_true(jsonctx->value[0] == '0');
+ assert_int_equal(1, jsonctx->value_len);
+ assert_int_equal(0, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "-0";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
+ assert_true(jsonctx->value[0] == '-');
+ assert_true(jsonctx->value[1] == '0');
+ assert_int_equal(2, jsonctx->value_len);
+ assert_int_equal(0, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "94E0";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
+ assert_true(jsonctx->value[0] == '9');
+ assert_true(jsonctx->value[1] == '4');
+ assert_int_equal(2, jsonctx->value_len);
+ assert_int_equal(0, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "0E2";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
+ assert_true(jsonctx->value[0] == '0');
+ assert_int_equal(1, jsonctx->value_len);
+ assert_int_equal(0, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "-0E2";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
+ assert_true(jsonctx->value[0] == '-');
+ assert_true(jsonctx->value[1] == '0');
+ assert_int_equal(2, jsonctx->value_len);
+ assert_int_equal(0, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "5.320e+2";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
+ assert_string_equal("532", jsonctx->value);
+ assert_int_equal(3, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ str = "5.320e-1";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
+ assert_string_equal("0.532", jsonctx->value);
+ assert_int_equal(5, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ /* various invalid inputs */
+ str = "-x";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ CHECK_LOG_CTX("Invalid character in JSON Number value (\"x\").", NULL, 1);
+
+ str = " -";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ CHECK_LOG_CTX("Unexpected end-of-input.", NULL, 1);
+
+ str = "--1";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ CHECK_LOG_CTX("Invalid character in JSON Number value (\"-\").", NULL, 1);
+
+ str = "+1";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ CHECK_LOG_CTX("Invalid character sequence \"+1\", expected a JSON value.", NULL, 1);
+
+ str = " 1.x ";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ CHECK_LOG_CTX("Invalid character in JSON Number value (\"x\").", NULL, 1);
+
+ str = "1.";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ CHECK_LOG_CTX("Unexpected end-of-input.", NULL, 1);
+
+ str = " 1eo ";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ CHECK_LOG_CTX("Invalid character in JSON Number value (\"o\").", NULL, 1);
+
+ str = "1e";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ CHECK_LOG_CTX("Unexpected end-of-input.", NULL, 1);
+
+ str = "1E1000";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ CHECK_LOG_CTX("Number encoded as a string exceeded the LY_NUMBER_MAXLEN limit.", NULL, 1);
+
+ str = "1e9999999999999999999";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ CHECK_LOG_CTX("Exponent out-of-bounds in a JSON Number value (1e9999999999999999999).", NULL, 1);
+
+ str = "1.1e66000";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ CHECK_LOG_CTX("Exponent out-of-bounds in a JSON Number value (1.1e66000).", NULL, 1);
+
+ str = "1.1e-66000";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ CHECK_LOG_CTX("Exponent out-of-bounds in a JSON Number value (1.1e-66000).", NULL, 1);
+
+ ly_in_free(in, 0);
+}
+
+/* now string is tested in file ./tests/utests/types/string.c */
+static void
+test_string(void **state)
+{
+ struct lyjson_ctx *jsonctx;
+ struct ly_in *in = NULL;
+ const char *str;
+
+ str = "";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+
+ /* unterminated string */
+ str = "\"unterminated string";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ CHECK_LOG_CTX("Missing quotation-mark at the end of a JSON string.", NULL, 1);
+ CHECK_LOG_CTX("Unexpected end-of-input.", NULL, 1);
+
+ ly_in_free(in, 0);
+}
+
+static void
+test_object(void **state)
+{
+ struct lyjson_ctx *jsonctx;
+ struct ly_in *in;
+ const char *str;
+
+ /* empty */
+ str = " { } ";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_OBJECT, lyjson_ctx_status(jsonctx));
+
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_OBJECT_CLOSED, lyjson_ctx_status(jsonctx));
+
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx));
+ lyjson_ctx_free(jsonctx);
+
+ /* simple value */
+ str = "{\"name\" : \"Radek\"}";
+ assert_non_null(ly_in_memory(in, str));
+
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_OBJECT, lyjson_ctx_status(jsonctx));
+
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_OBJECT_NAME, lyjson_ctx_status(jsonctx));
+ assert_ptr_equal(&str[2], jsonctx->value);
+ assert_int_equal(4, jsonctx->value_len);
+ assert_int_equal(0, jsonctx->dynamic);
+
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_STRING, lyjson_ctx_status(jsonctx));
+ assert_string_equal("Radek\"}", jsonctx->value);
+ assert_int_equal(5, jsonctx->value_len);
+ assert_int_equal(0, jsonctx->dynamic);
+
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_OBJECT_CLOSED, lyjson_ctx_status(jsonctx));
+
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx));
+ lyjson_ctx_free(jsonctx);
+
+ /* two values */
+ str = "{\"smart\" : true,\"handsom\":false}";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_OBJECT, lyjson_ctx_status(jsonctx));
+
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_OBJECT_NAME, lyjson_ctx_status(jsonctx));
+ assert_string_equal("smart\" : true,\"handsom\":false}", jsonctx->value);
+ assert_int_equal(5, jsonctx->value_len);
+ assert_int_equal(0, jsonctx->dynamic);
+
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_TRUE, lyjson_ctx_status(jsonctx));
+
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_OBJECT_NEXT, lyjson_ctx_status(jsonctx));
+
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_OBJECT_NAME, lyjson_ctx_status(jsonctx));
+ assert_string_equal("handsom\":false}", jsonctx->value);
+ assert_int_equal(7, jsonctx->value_len);
+ assert_int_equal(0, jsonctx->dynamic);
+
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_FALSE, lyjson_ctx_status(jsonctx));
+
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_OBJECT_CLOSED, lyjson_ctx_status(jsonctx));
+
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx));
+ lyjson_ctx_free(jsonctx);
+
+ /* inherited objects */
+ str = "{\"person\" : {\"name\":\"Radek\"}}";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_OBJECT, lyjson_ctx_status(jsonctx));
+
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_OBJECT_NAME, lyjson_ctx_status(jsonctx));
+ assert_string_equal("person\" : {\"name\":\"Radek\"}}", jsonctx->value);
+ assert_int_equal(6, jsonctx->value_len);
+ assert_int_equal(0, jsonctx->dynamic);
+
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_OBJECT, lyjson_ctx_status(jsonctx));
+
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_OBJECT_NAME, lyjson_ctx_status(jsonctx));
+ assert_string_equal("name\":\"Radek\"}}", jsonctx->value);
+ assert_int_equal(4, jsonctx->value_len);
+ assert_int_equal(0, jsonctx->dynamic);
+
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_STRING, lyjson_ctx_status(jsonctx));
+ assert_string_equal("Radek\"}}", jsonctx->value);
+ assert_int_equal(5, jsonctx->value_len);
+ assert_int_equal(0, jsonctx->dynamic);
+
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_OBJECT_CLOSED, lyjson_ctx_status(jsonctx));
+
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_OBJECT_CLOSED, lyjson_ctx_status(jsonctx));
+
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx));
+ lyjson_ctx_free(jsonctx);
+
+ /* unquoted string */
+ str = "{ unquoted : \"data\"}";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_OBJECT, lyjson_ctx_status(jsonctx));
+
+ assert_int_equal(LY_EVALID, lyjson_ctx_next(jsonctx, NULL));
+ CHECK_LOG_CTX("Invalid character sequence \"unquoted : \"data\"}\", expected a JSON object name.", NULL, 1);
+ lyjson_ctx_free(jsonctx);
+
+ ly_in_free(in, 0);
+}
+
+static void
+test_array(void **state)
+{
+ struct lyjson_ctx *jsonctx;
+ struct ly_in *in;
+ const char *str;
+
+ /* empty */
+ str = " [ ] ";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_ARRAY, lyjson_ctx_status(jsonctx));
+
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_ARRAY_CLOSED, lyjson_ctx_status(jsonctx));
+
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx));
+ lyjson_ctx_free(jsonctx);
+
+ /* simple value */
+ str = "[ null]";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_ARRAY, lyjson_ctx_status(jsonctx));
+ assert_null(jsonctx->value);
+ assert_int_equal(0, jsonctx->value_len);
+ assert_int_equal(0, jsonctx->dynamic);
+
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_NULL, lyjson_ctx_status(jsonctx));
+
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_ARRAY_CLOSED, lyjson_ctx_status(jsonctx));
+
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx));
+ lyjson_ctx_free(jsonctx);
+
+ /* two values */
+ str = "[{\"a\":null},\"x\"]";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_ARRAY, lyjson_ctx_status(jsonctx));
+ assert_null(jsonctx->value);
+ assert_int_equal(0, jsonctx->value_len);
+ assert_int_equal(0, jsonctx->dynamic);
+
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_OBJECT, lyjson_ctx_status(jsonctx));
+
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_OBJECT_NAME, lyjson_ctx_status(jsonctx));
+ assert_string_equal("a\":null},\"x\"]", jsonctx->value);
+ assert_int_equal(1, jsonctx->value_len);
+ assert_int_equal(0, jsonctx->dynamic);
+
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_NULL, lyjson_ctx_status(jsonctx));
+
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_OBJECT_CLOSED, lyjson_ctx_status(jsonctx));
+
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_ARRAY_NEXT, lyjson_ctx_status(jsonctx));
+
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_STRING, lyjson_ctx_status(jsonctx));
+ assert_string_equal("x\"]", jsonctx->value);
+ assert_int_equal(1, jsonctx->value_len);
+ assert_int_equal(0, jsonctx->dynamic);
+
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_ARRAY_CLOSED, lyjson_ctx_status(jsonctx));
+
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx));
+ lyjson_ctx_free(jsonctx);
+
+ /* new line is allowed only as escaped character in JSON */
+ str = "[ , null]";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LY_EVALID, lyjson_ctx_next(jsonctx, NULL));
+ CHECK_LOG_CTX("Invalid character sequence \", null]\", expected a JSON value.", NULL, 1);
+ lyjson_ctx_free(jsonctx);
+
+ ly_in_free(in, 0);
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_general),
+ UTEST(test_number),
+ UTEST(test_string),
+ UTEST(test_object),
+ UTEST(test_array),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/basic/test_plugins.c b/tests/utests/basic/test_plugins.c
new file mode 100644
index 0000000..df7523a
--- /dev/null
+++ b/tests/utests/basic/test_plugins.c
@@ -0,0 +1,153 @@
+/**
+ * @file test_plugins.c
+ * @author: Radek Krejci <rkrejci@cesnet.cz>
+ * @brief unit tests for functions from set.c
+ *
+ * Copyright (c) 2018 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
+ */
+#define _UTEST_MAIN_
+#include "utests.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "ly_config.h"
+#include "plugins.h"
+#include "plugins_internal.h"
+
+const char *simple = "module libyang-plugins-simple {"
+ " namespace urn:libyang:tests:plugins:simple;"
+ " prefix s;"
+ " typedef note { type string; }"
+ " extension hint { argument value; }"
+ " leaf test {"
+ " type s:note {length 255;}"
+ " s:hint \"some hint here\";"
+ " }"
+ "}";
+
+static void
+test_add_invalid(void **state)
+{
+ (void)state;
+ assert_int_equal(LY_ESYS, lyplg_add(TESTS_BIN "/plugins/plugin_does_not_exist" LYPLG_SUFFIX));
+}
+
+static void
+test_add_simple(void **state)
+{
+ struct lys_module *mod;
+ struct lysc_node_leaf *leaf;
+ struct lyplg_ext_record *record_e;
+ struct lyplg_type *plugin_t;
+
+ assert_int_equal(LY_SUCCESS, lyplg_add(TESTS_BIN "/plugins/plugin_simple" LYPLG_SUFFIX));
+
+ UTEST_ADD_MODULE(simple, LYS_IN_YANG, NULL, &mod);
+
+ leaf = (struct lysc_node_leaf *)mod->compiled->data;
+ assert_int_equal(LYS_LEAF, leaf->nodetype);
+
+ assert_non_null(plugin_t = lyplg_type_plugin_find(NULL, "libyang-plugins-simple", NULL, "note"));
+ assert_string_equal("ly2 simple test v1", plugin_t->id);
+ assert_ptr_equal(leaf->type->plugin, plugin_t);
+
+ assert_int_equal(1, LY_ARRAY_COUNT(leaf->exts));
+ assert_non_null(record_e = lyplg_ext_record_find(NULL, "libyang-plugins-simple", NULL, "hint"));
+ assert_string_equal("ly2 simple test v1", record_e->plugin.id);
+ assert_ptr_equal(leaf->exts[0].def->plugin, &record_e->plugin);
+
+ /* the second loading of the same plugin - still success */
+ assert_int_equal(LY_SUCCESS, lyplg_add(TESTS_BIN "/plugins/plugin_simple" LYPLG_SUFFIX));
+}
+
+static void
+test_not_implemented(void **state)
+{
+ struct lys_module *mod;
+ struct lyd_node *tree;
+ const char *schema = "module libyang-plugins-unknown {"
+ " namespace urn:libyang:tests:plugins:unknown;"
+ " prefix u;"
+ " extension myext;"
+ " typedef mytype { type string;}"
+ " leaf test {"
+ " u:myext;"
+ " type mytype;"
+ " }"
+ "}";
+ const char *data = "<test xmlns=\"urn:libyang:tests:plugins:unknown\">xxx</test>";
+ char *printed = NULL;
+
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+
+ assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG_COMPILED, 0));
+ free(printed);
+
+ assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, 0, LYD_VALIDATE_PRESENT, &tree));
+ CHECK_LOG_CTX(NULL, NULL, 0);
+
+ lyd_free_all(tree);
+}
+
+static LY_ERR
+parse_clb(struct lysp_ctx *UNUSED(pctx), struct lysp_ext_instance *ext)
+{
+ struct lysp_node_leaf *leaf;
+
+ leaf = (struct lysp_node_leaf *)ext->parent;
+ leaf->flags |= LYS_STATUS_OBSLT;
+ return LY_SUCCESS;
+}
+
+struct lyplg_ext_record memory_recs[] = {
+ {
+ .module = "libyang-plugins-simple",
+ .revision = NULL,
+ .name = "hint",
+
+ .plugin.id = "memory-plugin-v1",
+ .plugin.parse = parse_clb,
+ .plugin.compile = NULL,
+ .plugin.printer_info = NULL,
+ .plugin.node = NULL,
+ .plugin.snode = NULL,
+ .plugin.validate = NULL,
+ .plugin.pfree = NULL,
+ .plugin.cfree = NULL
+ },
+ {0} /* terminating zeroed item */
+};
+
+static void
+test_simple_from_memory(void **state)
+{
+ struct lys_module *mod;
+ struct lysc_node_leaf *leaf;
+
+ lyplg_add_extension_plugin(UTEST_LYCTX, LYPLG_EXT_API_VERSION, memory_recs);
+ UTEST_ADD_MODULE(simple, LYS_IN_YANG, NULL, &mod);
+
+ leaf = (struct lysc_node_leaf *)mod->compiled->data;
+ assert_int_equal(LYS_LEAF, leaf->nodetype);
+ assert_true(leaf->flags & LYS_STATUS_OBSLT);
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_add_invalid),
+ UTEST(test_add_simple),
+ UTEST(test_not_implemented),
+ UTEST(test_simple_from_memory),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/basic/test_set.c b/tests/utests/basic/test_set.c
new file mode 100644
index 0000000..04ea6bc
--- /dev/null
+++ b/tests/utests/basic/test_set.c
@@ -0,0 +1,276 @@
+/**
+ * @file test_set.c
+ * @author: Radek Krejci <rkrejci@cesnet.cz>
+ * @brief unit tests for functions from set.c
+ *
+ * Copyright (c) 2018 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
+ */
+#define _POSIX_C_SOURCE 200809L /* strdup */
+#define _UTEST_MAIN_
+#include "utests.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "set.h"
+
+static void
+test_basics(void **UNUSED(state))
+{
+ struct ly_set *set;
+ char *str;
+ unsigned int u;
+ void *ptr;
+ uint32_t index;
+
+ /* creation - everything is empty */
+ assert_int_equal(LY_SUCCESS, ly_set_new(&set));
+ assert_non_null(set);
+ assert_int_equal(0, set->count);
+ assert_int_equal(0, set->size);
+ assert_null(set->objs);
+
+ /* add a testing object */
+ str = strdup("test string");
+ assert_non_null(str);
+
+ assert_int_equal(LY_SUCCESS, ly_set_add(set, str, 0, NULL));
+ assert_int_not_equal(0, set->size);
+ assert_int_equal(1, set->count);
+ assert_non_null(set->objs);
+ assert_non_null(set->objs[0]);
+
+ /* check the presence of the testing data */
+ assert_int_equal(1, ly_set_contains(set, str, &index));
+ assert_int_equal(0, index);
+ assert_int_equal(0, ly_set_contains(set, str - 1, NULL));
+
+ /* remove data, but keep the set */
+ u = set->size;
+ ptr = set->objs;
+ ly_set_clean(set, free);
+ assert_int_equal(0, set->count);
+ assert_int_equal(u, set->size);
+ assert_ptr_equal(ptr, set->objs);
+
+ /* remove buffer, but keep the set object */
+ ly_set_erase(set, NULL);
+ assert_int_equal(0, set->count);
+ assert_int_equal(0, set->size);
+ assert_ptr_equal(NULL, set->objs);
+
+ /* final cleanup */
+ ly_set_free(set, NULL);
+}
+
+static void
+test_inval(void **UNUSED(state))
+{
+ struct ly_set set = {0};
+
+ assert_int_equal(LY_EINVAL, ly_set_dup(NULL, NULL, NULL));
+ CHECK_LOG_LASTMSG("Invalid argument set (ly_set_dup()).");
+
+ assert_int_equal(LY_EINVAL, ly_set_add(NULL, NULL, 0, NULL));
+ CHECK_LOG_LASTMSG("Invalid argument set (ly_set_add()).");
+
+ assert_int_equal(LY_EINVAL, ly_set_merge(NULL, NULL, 0, NULL));
+ CHECK_LOG_LASTMSG("Invalid argument trg (ly_set_merge()).");
+ assert_int_equal(LY_SUCCESS, ly_set_merge(&set, NULL, 0, NULL));
+
+ assert_int_equal(LY_EINVAL, ly_set_rm_index(NULL, 0, NULL));
+ CHECK_LOG_LASTMSG("Invalid argument set (ly_set_rm_index()).");
+ assert_int_equal(LY_EINVAL, ly_set_rm_index(&set, 1, NULL));
+ CHECK_LOG_LASTMSG("Invalid argument index (ly_set_rm_index()).");
+
+ assert_int_equal(LY_EINVAL, ly_set_rm(NULL, NULL, NULL));
+ CHECK_LOG_LASTMSG("Invalid argument set (ly_set_rm()).");
+ assert_int_equal(LY_EINVAL, ly_set_rm(&set, NULL, NULL));
+ CHECK_LOG_LASTMSG("Invalid argument object (ly_set_rm()).");
+ assert_int_equal(LY_EINVAL, ly_set_rm(&set, &set, NULL));
+ CHECK_LOG_LASTMSG("Invalid argument object (ly_set_rm()).");
+}
+
+static void
+test_duplication(void **UNUSED(state))
+{
+ struct ly_set *orig, *new;
+ char *str;
+ uint32_t index;
+
+ assert_int_equal(LY_SUCCESS, ly_set_new(&orig));
+ assert_non_null(orig);
+
+ /* add a testing object */
+ str = strdup("test string");
+ assert_non_null(str);
+ assert_int_equal(LY_SUCCESS, ly_set_add(orig, str, 0, &index));
+ assert_int_equal(0, index);
+
+ /* duplicate the set - without duplicator, so the new set will point to the same string */
+ assert_int_equal(LY_SUCCESS, ly_set_dup(orig, NULL, &new));
+ assert_non_null(new);
+ assert_ptr_not_equal(orig, new);
+ assert_int_equal(orig->count, new->count);
+ assert_ptr_equal(orig->objs[0], new->objs[0]);
+
+ ly_set_free(new, NULL);
+
+ /* duplicate the set - with duplicator, so the new set will point to a different buffer with the same content */
+ assert_int_equal(LY_SUCCESS, ly_set_dup(orig, (void *(*)(const void *))strdup, &new));
+ assert_non_null(new);
+ assert_ptr_not_equal(orig, new);
+ assert_int_equal(orig->count, new->count);
+ assert_ptr_not_equal(orig->objs[0], new->objs[0]);
+ assert_string_equal(orig->objs[0], new->objs[0]);
+
+ /* cleanup */
+ ly_set_free(new, free);
+ ly_set_free(orig, free);
+}
+
+static void
+test_add(void **UNUSED(state))
+{
+ uint32_t u, index;
+ char *str = "test string";
+ struct ly_set set;
+
+ memset(&set, 0, sizeof set);
+
+ /* add a testing object */
+ assert_int_equal(LY_SUCCESS, ly_set_add(&set, str, 0, &index));
+ assert_int_equal(0, index);
+
+ /* test avoiding data duplicities */
+ assert_int_equal(LY_SUCCESS, ly_set_add(&set, str, 0, &index));
+ assert_int_equal(0, index);
+ assert_int_equal(1, set.count);
+ assert_int_equal(LY_SUCCESS, ly_set_add(&set, str, 1, &index));
+ assert_int_equal(1, index);
+ assert_int_equal(2, set.count);
+
+ /* test array resizing */
+ u = set.size;
+ for (uint32_t expected_index = 2; expected_index <= u; ++expected_index) {
+ assert_int_equal(LY_SUCCESS, ly_set_add(&set, str, 1, &index));
+ assert_int_equal(expected_index, index);
+ }
+ assert_true(u != set.size);
+
+ /* cleanup */
+ ly_set_erase(&set, NULL);
+}
+
+static void
+test_merge(void **UNUSED(state))
+{
+ char *str1, *str2;
+ struct ly_set one, two;
+
+ memset(&one, 0, sizeof one);
+ memset(&two, 0, sizeof two);
+
+ str1 = strdup("string1");
+ str2 = strdup("string2");
+
+ /* fill first set
+ * - str1 is the same as in two, so it must not be freed! */
+ assert_int_equal(LY_SUCCESS, ly_set_add(&one, str1, 0, NULL));
+
+ /* fill second set */
+ assert_int_equal(LY_SUCCESS, ly_set_add(&two, str1, 0, NULL));
+ assert_int_equal(LY_SUCCESS, ly_set_add(&two, str2, 0, NULL));
+
+ /* merge with checking duplicities - only one item is added into one;
+ * also without duplicating data, so it must not be freed at the end */
+ assert_int_equal(LY_SUCCESS, ly_set_merge(&one, &two, 0, NULL));
+ assert_int_equal(2, one.count);
+ assert_ptr_equal(one.objs[1], two.objs[1]);
+
+ /* clean and re-fill one (now duplicating str1, to allow testing duplicator) */
+ ly_set_clean(&one, NULL);
+ assert_int_equal(LY_SUCCESS, ly_set_add(&one, strdup(str1), 0, NULL));
+
+ /* merge without checking duplicities - two items are added into one;
+ * here also with duplicator */
+ assert_int_equal(LY_SUCCESS, ly_set_merge(&one, &two, 1, (void *(*)(const void *))strdup));
+ assert_int_equal(3, one.count);
+ assert_ptr_not_equal(one.objs[1], two.objs[0]);
+ assert_string_equal(one.objs[1], two.objs[0]);
+
+ /* cleanup */
+ ly_set_erase(&one, free);
+ ly_set_erase(&two, free);
+}
+
+static void
+test_rm(void **UNUSED(state))
+{
+ char *str1, *str2, *str3;
+ struct ly_set set;
+
+ memset(&set, 0, sizeof set);
+
+ /* fill the set */
+ assert_int_equal(LY_SUCCESS, ly_set_add(&set, "string1", 0, NULL));
+ assert_int_equal(LY_SUCCESS, ly_set_add(&set, strdup("string2"), 0, NULL));
+ assert_int_equal(LY_SUCCESS, ly_set_add(&set, "string3", 0, NULL));
+
+ /* remove by index ... */
+ /* ... in the middle ... */
+ assert_int_equal(LY_SUCCESS, ly_set_rm_index(&set, 1, free));
+ assert_int_equal(2, set.count);
+ assert_string_not_equal("string2", set.objs[0]);
+ assert_string_not_equal("string2", set.objs[1]);
+ /* ... last .. */
+ assert_int_equal(LY_SUCCESS, ly_set_rm_index(&set, 1, NULL));
+ assert_int_equal(1, set.count);
+ assert_string_not_equal("string3", set.objs[0]);
+ /* ... first .. */
+ assert_int_equal(LY_SUCCESS, ly_set_rm_index(&set, 0, NULL));
+ assert_int_equal(0, set.count);
+
+ /* fill the set */
+ assert_int_equal(LY_SUCCESS, ly_set_add(&set, str1 = "string1", 0, NULL));
+ assert_int_equal(LY_SUCCESS, ly_set_add(&set, str2 = "string2", 0, NULL));
+ assert_int_equal(LY_SUCCESS, ly_set_add(&set, str3 = strdup("string3"), 0, NULL));
+
+ /* remove by pointer ... */
+ /* ... in the middle ... */
+ assert_int_equal(LY_SUCCESS, ly_set_rm(&set, str2, NULL));
+ assert_int_equal(2, set.count);
+ assert_string_not_equal("string2", set.objs[0]);
+ assert_string_not_equal("string2", set.objs[1]);
+ /* ... last (with destructor) .. */
+ assert_int_equal(LY_SUCCESS, ly_set_rm(&set, str3, free));
+ assert_int_equal(1, set.count);
+ assert_string_not_equal("string3", set.objs[0]);
+ /* ... first .. */
+ assert_int_equal(LY_SUCCESS, ly_set_rm(&set, str1, NULL));
+ assert_int_equal(0, set.count);
+
+ /* cleanup */
+ ly_set_erase(&set, NULL);
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_basics),
+ UTEST(test_duplication),
+ UTEST(test_add),
+ UTEST(test_merge),
+ UTEST(test_rm),
+ UTEST(test_inval),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/basic/test_xml.c b/tests/utests/basic/test_xml.c
new file mode 100644
index 0000000..25aed03
--- /dev/null
+++ b/tests/utests/basic/test_xml.c
@@ -0,0 +1,689 @@
+/**
+ * @file test_xml.c
+ * @author: Radek Krejci <rkrejci@cesnet.cz>
+ * @brief unit tests for functions from xml.c
+ *
+ * Copyright (c) 2018 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
+ */
+#define _UTEST_MAIN_
+#include "utests.h"
+
+#define _POSIX_C_SOURCE 200809L /* strdup */
+
+#include <string.h>
+
+#include "context.h"
+#include "in_internal.h"
+#include "xml.h"
+
+LY_ERR lyxml_ns_add(struct lyxml_ctx *xmlctx, const char *prefix, size_t prefix_len, char *uri);
+
+static void
+test_element(void **state)
+{
+ struct lyxml_ctx *xmlctx;
+ struct ly_in *in;
+ const char *str;
+
+ /* empty */
+ str = "";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx));
+ assert_int_equal(LYXML_END, xmlctx->status);
+ lyxml_ctx_free(xmlctx);
+ ly_in_free(in, 0);
+
+ /* end element */
+ str = "</element>";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_EVALID, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx));
+ CHECK_LOG_CTX("Stray closing element tag (\"element\").", NULL, 1);
+ ly_in_free(in, 0);
+
+ /* no element */
+ str = "no data present";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_EVALID, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx));
+ CHECK_LOG_CTX("Invalid character sequence \"no data present\", expected element tag start ('<').", NULL, 1);
+ ly_in_free(in, 0);
+
+ /* not supported DOCTYPE */
+ str = "<!DOCTYPE greeting SYSTEM \"hello.dtd\"><greeting/>";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_EVALID, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx));
+ CHECK_LOG_CTX("Document Type Declaration not supported.", NULL, 1);
+ ly_in_free(in, 0);
+
+ /* invalid XML */
+ str = "<!NONSENSE/>";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_EVALID, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx));
+ CHECK_LOG_CTX("Unknown XML section \"<!NONSENSE/>\".", NULL, 1);
+ ly_in_free(in, 0);
+
+ /* namespace ambiguity */
+ str = "<element xmlns=\"urn1\" xmlns=\"urn2\"/>";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_EVALID, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx));
+ CHECK_LOG_CTX("Duplicate default XML namespaces \"urn1\" and \"urn2\".", NULL, 1);
+ ly_in_free(in, 0);
+
+ /* prefix duplicate */
+ str = "<element xmlns:a=\"urn1\" xmlns:a=\"urn2\"/>";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_EVALID, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx));
+ CHECK_LOG_CTX("Duplicate XML NS prefix \"a\" used for namespaces \"urn1\" and \"urn2\".", NULL, 1);
+ ly_in_free(in, 0);
+
+ /* unqualified element */
+ str = " < element/>";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx));
+ assert_int_equal(LYXML_ELEMENT, xmlctx->status);
+ assert_null(xmlctx->prefix);
+ assert_true(!strncmp("element", xmlctx->name, xmlctx->name_len));
+ assert_int_equal(1, xmlctx->elements.count);
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ELEM_CONTENT, xmlctx->status);
+ assert_true(!strncmp("", xmlctx->value, xmlctx->value_len));
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ELEM_CLOSE, xmlctx->status);
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_END, xmlctx->status);
+ lyxml_ctx_free(xmlctx);
+ ly_in_free(in, 0);
+
+ /* element with attribute */
+ str = " < element attr=\'x\'/>";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx));
+ assert_int_equal(LYXML_ELEMENT, xmlctx->status);
+ assert_true(!strncmp("element", xmlctx->name, xmlctx->name_len));
+ assert_null(xmlctx->prefix);
+ assert_int_equal(1, xmlctx->elements.count);
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ATTRIBUTE, xmlctx->status);
+ assert_true(!strncmp("attr", xmlctx->name, xmlctx->name_len));
+ assert_null(xmlctx->prefix);
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ATTR_CONTENT, xmlctx->status);
+ assert_int_equal(1, xmlctx->elements.count);
+ assert_true(!strncmp("x", xmlctx->value, xmlctx->value_len));
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ELEM_CONTENT, xmlctx->status);
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ELEM_CLOSE, xmlctx->status);
+ assert_int_equal(0, xmlctx->elements.count);
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_END, xmlctx->status);
+ lyxml_ctx_free(xmlctx);
+ ly_in_free(in, 0);
+
+ /* headers and comments */
+ str = "<?xml version=\"1.0\"?> <!-- comment --> <?TEST xxx?> <element/>";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx));
+ assert_int_equal(LYXML_ELEMENT, xmlctx->status);
+ assert_true(!strncmp("element", xmlctx->name, xmlctx->name_len));
+ assert_null(xmlctx->prefix);
+ assert_int_equal(1, xmlctx->elements.count);
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ELEM_CONTENT, xmlctx->status);
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ELEM_CLOSE, xmlctx->status);
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_END, xmlctx->status);
+ lyxml_ctx_free(xmlctx);
+ ly_in_free(in, 0);
+
+ /* separate opening and closing tags, neamespaced parsed internally */
+ str = "<element xmlns=\"urn\"></element>";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx));
+ assert_int_equal(LYXML_ELEMENT, xmlctx->status);
+ assert_true(!strncmp("element", xmlctx->name, xmlctx->name_len));
+ assert_null(xmlctx->prefix);
+ assert_int_equal(1, xmlctx->elements.count);
+ assert_int_equal(1, xmlctx->ns.count);
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ELEM_CONTENT, xmlctx->status);
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ELEM_CLOSE, xmlctx->status);
+ assert_int_equal(0, xmlctx->elements.count);
+ assert_int_equal(0, xmlctx->ns.count);
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_END, xmlctx->status);
+ lyxml_ctx_free(xmlctx);
+ ly_in_free(in, 0);
+
+ /* qualified element */
+ str = " < yin:element/>";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx));
+ assert_int_equal(LYXML_ELEMENT, xmlctx->status);
+ assert_true(!strncmp("element", xmlctx->name, xmlctx->name_len));
+ assert_true(!strncmp("yin", xmlctx->prefix, xmlctx->prefix_len));
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ELEM_CONTENT, xmlctx->status);
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ELEM_CLOSE, xmlctx->status);
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_END, xmlctx->status);
+ lyxml_ctx_free(xmlctx);
+ ly_in_free(in, 0);
+
+ /* non-matching closing tag */
+ str = "<yin:element xmlns=\"urn\"></element>";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx));
+ assert_int_equal(LYXML_ELEMENT, xmlctx->status);
+ assert_true(!strncmp("element", xmlctx->name, xmlctx->name_len));
+ assert_true(!strncmp("yin", xmlctx->prefix, xmlctx->prefix_len));
+ assert_int_equal(1, xmlctx->elements.count);
+ assert_int_equal(1, xmlctx->ns.count);
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ELEM_CONTENT, xmlctx->status);
+
+ assert_int_equal(LY_EVALID, lyxml_ctx_next(xmlctx));
+ CHECK_LOG_CTX("Opening (\"yin:element\") and closing (\"element\") elements tag mismatch.", NULL, 1);
+ lyxml_ctx_free(xmlctx);
+ ly_in_free(in, 0);
+
+ /* invalid closing tag */
+ str = "<yin:element xmlns=\"urn\"></yin:element/>";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx));
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LY_EVALID, lyxml_ctx_next(xmlctx));
+ CHECK_LOG_CTX("Invalid character sequence \"/>\", expected element tag termination ('>').", NULL, 1);
+ lyxml_ctx_free(xmlctx);
+ ly_in_free(in, 0);
+
+ /* UTF8 characters */
+ str = "<𠜎€𠜎Øn:𠜎€𠜎Øn/>";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx));
+ assert_true(!strncmp("𠜎€𠜎Øn", xmlctx->name, xmlctx->name_len));
+ assert_true(!strncmp("𠜎€𠜎Øn", xmlctx->prefix, xmlctx->prefix_len));
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ELEM_CONTENT, xmlctx->status);
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ELEM_CLOSE, xmlctx->status);
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_END, xmlctx->status);
+ lyxml_ctx_free(xmlctx);
+ ly_in_free(in, 0);
+
+ /* invalid UTF-8 characters */
+ str = "<¢:element>";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_EVALID, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx));
+ CHECK_LOG_CTX("Identifier \"¢:element>\" starts with an invalid character.", NULL, 1);
+ ly_in_free(in, 0);
+
+ str = "<yin:c⁐element>";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx));
+ assert_int_equal(LY_EVALID, lyxml_ctx_next(xmlctx));
+ CHECK_LOG_CTX("Invalid character sequence \"⁐element>\", expected element tag end ('>' or '/>') or an attribute.", NULL, 1);
+ lyxml_ctx_free(xmlctx);
+ ly_in_free(in, 0);
+
+ /* mixed content */
+ str = "<a>text <b>x</b></a>";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx));
+ assert_int_equal(LYXML_ELEMENT, xmlctx->status);
+ assert_true(!strncmp("a", xmlctx->name, xmlctx->name_len));
+ assert_null(xmlctx->prefix);
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ELEM_CONTENT, xmlctx->status);
+ assert_true(!strncmp("text ", xmlctx->value, xmlctx->value_len));
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ELEMENT, xmlctx->status);
+ assert_true(!strncmp("b", xmlctx->name, xmlctx->name_len));
+ assert_null(xmlctx->prefix);
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ELEM_CONTENT, xmlctx->status);
+ assert_true(!strncmp("x", xmlctx->value, xmlctx->value_len));
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ELEM_CLOSE, xmlctx->status);
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ELEM_CLOSE, xmlctx->status);
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_END, xmlctx->status);
+ lyxml_ctx_free(xmlctx);
+ ly_in_free(in, 0);
+
+ /* tag mismatch */
+ str = "<a>text</b>";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx));
+ assert_int_equal(LYXML_ELEMENT, xmlctx->status);
+ assert_true(!strncmp("a", xmlctx->name, xmlctx->name_len));
+ assert_null(xmlctx->prefix);
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ELEM_CONTENT, xmlctx->status);
+ assert_true(!strncmp("text", xmlctx->value, xmlctx->value_len));
+
+ assert_int_equal(LY_EVALID, lyxml_ctx_next(xmlctx));
+ CHECK_LOG_CTX("Opening (\"a\") and closing (\"b\") elements tag mismatch.", NULL, 1);
+ lyxml_ctx_free(xmlctx);
+ ly_in_free(in, 0);
+}
+
+static void
+test_attribute(void **state)
+{
+ const char *str;
+ struct lyxml_ctx *xmlctx;
+ struct ly_in *in;
+ struct lyxml_ns *ns;
+
+ /* not an attribute */
+ str = "<e unknown/>";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_EVALID, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx));
+ CHECK_LOG_CTX("Invalid character sequence \"/>\", expected '='.", NULL, 1);
+ ly_in_free(in, 0);
+
+ str = "<e xxx=/>";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_EVALID, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx));
+ CHECK_LOG_CTX("Invalid character sequence \"/>\", expected either single or double quotation mark.", NULL, 1);
+ ly_in_free(in, 0);
+
+ str = "<e xxx\n = yyy/>";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_EVALID, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx));
+ CHECK_LOG_CTX("Invalid character sequence \"yyy/>\", expected either single or double quotation mark.", NULL, 2);
+ ly_in_free(in, 0);
+
+ /* valid attribute */
+ str = "<e attr=\"val\"";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx));
+ assert_int_equal(LYXML_ELEMENT, xmlctx->status);
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ATTRIBUTE, xmlctx->status);
+ assert_true(!strncmp("attr", xmlctx->name, xmlctx->name_len));
+ assert_null(xmlctx->prefix);
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ATTR_CONTENT, xmlctx->status);
+ assert_true(!strncmp("val", xmlctx->value, xmlctx->value_len));
+ assert_int_equal(xmlctx->ws_only, 0);
+ assert_int_equal(xmlctx->dynamic, 0);
+ lyxml_ctx_free(xmlctx);
+ ly_in_free(in, 0);
+
+ /* valid namespace with prefix */
+ str = "<e xmlns:nc\n = \'urn\'/>";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx));
+ assert_int_equal(LYXML_ELEMENT, xmlctx->status);
+ assert_int_equal(1, xmlctx->ns.count);
+ ns = (struct lyxml_ns *)xmlctx->ns.objs[0];
+ assert_string_equal(ns->prefix, "nc");
+ assert_string_equal(ns->uri, "urn");
+ lyxml_ctx_free(xmlctx);
+ ly_in_free(in, 0);
+}
+
+static void
+test_text(void **state)
+{
+ const char *str;
+ struct lyxml_ctx *xmlctx;
+ struct ly_in *in;
+
+ /* empty attribute value */
+ str = "<e a=\"\"";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx));
+ assert_int_equal(LYXML_ELEMENT, xmlctx->status);
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ATTRIBUTE, xmlctx->status);
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ATTR_CONTENT, xmlctx->status);
+ assert_true(!strncmp("", xmlctx->value, xmlctx->value_len));
+ assert_int_equal(xmlctx->ws_only, 1);
+ assert_int_equal(xmlctx->dynamic, 0);
+ ly_in_free(in, 0);
+
+ /* empty value but in single quotes */
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory("=\'\'", &in));
+ xmlctx->in = in;
+ ly_log_location(NULL, NULL, NULL, in);
+ xmlctx->status = LYXML_ATTRIBUTE;
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ATTR_CONTENT, xmlctx->status);
+ assert_true(!strncmp("", xmlctx->value, xmlctx->value_len));
+ assert_int_equal(xmlctx->ws_only, 1);
+ assert_int_equal(xmlctx->dynamic, 0);
+ ly_in_free(in, 0);
+
+ /* empty element content - only formating before defining child */
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(">\n <y>", &in));
+ xmlctx->in = in;
+ ly_log_location(NULL, NULL, NULL, in);
+ xmlctx->status = LYXML_ELEMENT;
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ELEM_CONTENT, xmlctx->status);
+ assert_true(!strncmp("\n ", xmlctx->value, xmlctx->value_len));
+ assert_int_equal(xmlctx->ws_only, 1);
+ assert_int_equal(xmlctx->dynamic, 0);
+ ly_in_free(in, 0);
+
+ /* empty element content is invalid - missing content terminating character < */
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory("", &in));
+ xmlctx->in = in;
+ ly_log_location(NULL, NULL, NULL, in);
+ xmlctx->status = LYXML_ELEM_CONTENT;
+ assert_int_equal(LY_EVALID, lyxml_ctx_next(xmlctx));
+ CHECK_LOG_CTX("Unexpected end-of-input.", NULL, 1);
+ ly_in_free(in, 0);
+
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory("xxx", &in));
+ xmlctx->in = in;
+ ly_log_location(NULL, NULL, NULL, in);
+ xmlctx->status = LYXML_ELEM_CONTENT;
+ assert_int_equal(LY_EVALID, lyxml_ctx_next(xmlctx));
+ CHECK_LOG_CTX("Invalid character sequence \"xxx\", expected element tag start ('<').", NULL, 1);
+ ly_in_free(in, 0);
+
+ lyxml_ctx_free(xmlctx);
+ ly_log_location_revert(0, 0, 0, 4);
+
+ /* valid strings */
+ str = "<a>€𠜎Øn \n&lt;&amp;&quot;&apos;&gt; &#82;&#x4f;&#x4B;</a>";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx));
+ assert_int_equal(LYXML_ELEMENT, xmlctx->status);
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ELEM_CONTENT, xmlctx->status);
+ assert_true(!strncmp("€𠜎Øn \n<&\"\'> ROK", xmlctx->value, xmlctx->value_len));
+ assert_int_equal(xmlctx->ws_only, 0);
+ assert_int_equal(xmlctx->dynamic, 1);
+ free((char *)xmlctx->value);
+ ly_in_free(in, 0);
+
+ /* test using n-bytes UTF8 hexadecimal code points */
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory("=\'&#x0024;&#x00A2;&#x20ac;&#x10348;\'", &in));
+ xmlctx->in = in;
+ ly_log_location(NULL, NULL, NULL, in);
+ xmlctx->status = LYXML_ATTRIBUTE;
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ATTR_CONTENT, xmlctx->status);
+ assert_true(!strncmp("$¢€𐍈", xmlctx->value, xmlctx->value_len));
+ assert_int_equal(xmlctx->ws_only, 0);
+ assert_int_equal(xmlctx->dynamic, 1);
+ ly_in_free(in, 0);
+
+ /* CDATA value */
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory("> <![CDATA[ special non-escaped chars <>&\"' ]]> </a>", &in));
+ xmlctx->in = in;
+ ly_log_location(NULL, NULL, NULL, in);
+ xmlctx->status = LYXML_ATTR_CONTENT;
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ELEM_CONTENT, xmlctx->status);
+ assert_true(!strncmp(" special non-escaped chars <>&\"' ", xmlctx->value, xmlctx->value_len));
+ assert_int_equal(xmlctx->ws_only, 0);
+ assert_int_equal(xmlctx->dynamic, 1);
+ free((char *)xmlctx->value);
+ ly_in_free(in, 0);
+
+ /* invalid characters in string */
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory("=\'&#x52\'", &in));
+ xmlctx->in = in;
+ ly_log_location(NULL, NULL, NULL, in);
+ xmlctx->status = LYXML_ATTRIBUTE;
+ assert_int_equal(LY_EVALID, lyxml_ctx_next(xmlctx));
+ CHECK_LOG_CTX("Invalid character sequence \"'\", expected ;.", NULL, 1);
+ ly_in_free(in, 0);
+
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory("=\"&#82\"", &in));
+ xmlctx->in = in;
+ ly_log_location(NULL, NULL, NULL, in);
+ xmlctx->status = LYXML_ATTRIBUTE;
+ assert_int_equal(LY_EVALID, lyxml_ctx_next(xmlctx));
+ CHECK_LOG_CTX("Invalid character sequence \"\"\", expected ;.", NULL, 1);
+ ly_in_free(in, 0);
+
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory("=\"&nonsense;\"", &in));
+ xmlctx->in = in;
+ ly_log_location(NULL, NULL, NULL, in);
+ xmlctx->status = LYXML_ATTRIBUTE;
+ assert_int_equal(LY_EVALID, lyxml_ctx_next(xmlctx));
+ CHECK_LOG_CTX("Entity reference \"&nonsense;\" not supported, only predefined references allowed.", NULL, 1);
+ ly_in_free(in, 0);
+
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(">&#o122;", &in));
+ xmlctx->in = in;
+ ly_log_location(NULL, NULL, NULL, in);
+ xmlctx->status = LYXML_ELEMENT;
+ assert_int_equal(LY_EVALID, lyxml_ctx_next(xmlctx));
+ CHECK_LOG_CTX("Invalid character reference \"&#o122;\".", NULL, 1);
+ ly_in_free(in, 0);
+
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory("=\'&#x06;\'", &in));
+ xmlctx->in = in;
+ ly_log_location(NULL, NULL, NULL, in);
+ xmlctx->status = LYXML_ATTRIBUTE;
+ assert_int_equal(LY_EVALID, lyxml_ctx_next(xmlctx));
+ CHECK_LOG_CTX("Invalid character reference \"&#x06;\'\" (0x00000006).", NULL, 1);
+ ly_in_free(in, 0);
+
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory("=\'&#xfdd0;\'", &in));
+ xmlctx->in = in;
+ ly_log_location(NULL, NULL, NULL, in);
+ xmlctx->status = LYXML_ATTRIBUTE;
+ assert_int_equal(LY_EVALID, lyxml_ctx_next(xmlctx));
+ CHECK_LOG_CTX("Invalid character reference \"&#xfdd0;\'\" (0x0000fdd0).", NULL, 1);
+ ly_in_free(in, 0);
+
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory("=\'&#xffff;\'", &in));
+ xmlctx->in = in;
+ ly_log_location(NULL, NULL, NULL, in);
+ xmlctx->status = LYXML_ATTRIBUTE;
+ assert_int_equal(LY_EVALID, lyxml_ctx_next(xmlctx));
+ CHECK_LOG_CTX("Invalid character reference \"&#xffff;\'\" (0x0000ffff).", NULL, 1);
+ ly_in_free(in, 0);
+
+ lyxml_ctx_free(xmlctx);
+ ly_log_location_revert(0, 0, 0, 9);
+}
+
+static void
+test_ns(void **state)
+{
+ const char *str;
+ struct lyxml_ctx *xmlctx;
+ struct ly_in *in;
+ const struct lyxml_ns *ns;
+
+ /* opening element1 */
+ str = "<element1/>";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx));
+
+ /* processing namespace definitions */
+ assert_int_equal(LY_SUCCESS, lyxml_ns_add(xmlctx, NULL, 0, strdup("urn:default")));
+ assert_int_equal(LY_SUCCESS, lyxml_ns_add(xmlctx, "nc", 2, strdup("urn:nc1")));
+ /* simulate adding open element2 into context */
+ xmlctx->elements.count++;
+ /* processing namespace definitions */
+ assert_int_equal(LY_SUCCESS, lyxml_ns_add(xmlctx, "nc", 2, strdup("urn:nc2")));
+ assert_int_equal(3, xmlctx->ns.count);
+ assert_int_not_equal(0, xmlctx->ns.size);
+
+ ns = lyxml_ns_get(&xmlctx->ns, NULL, 0);
+ assert_non_null(ns);
+ assert_null(ns->prefix);
+ assert_string_equal("urn:default", ns->uri);
+
+ ns = lyxml_ns_get(&xmlctx->ns, "nc", 2);
+ assert_non_null(ns);
+ assert_string_equal("nc", ns->prefix);
+ assert_string_equal("urn:nc2", ns->uri);
+
+ /* simulate closing element2 */
+ xmlctx->elements.count--;
+ lyxml_ns_rm(xmlctx);
+ assert_int_equal(2, xmlctx->ns.count);
+
+ ns = lyxml_ns_get(&xmlctx->ns, "nc", 2);
+ assert_non_null(ns);
+ assert_string_equal("nc", ns->prefix);
+ assert_string_equal("urn:nc1", ns->uri);
+
+ /* close element1 */
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(0, xmlctx->ns.count);
+
+ assert_null(lyxml_ns_get(&xmlctx->ns, "nc", 2));
+ assert_null(lyxml_ns_get(&xmlctx->ns, NULL, 0));
+
+ lyxml_ctx_free(xmlctx);
+ ly_in_free(in, 0);
+}
+
+static void
+test_ns2(void **state)
+{
+ const char *str;
+ struct lyxml_ctx *xmlctx;
+ struct ly_in *in;
+
+ /* opening element1 */
+ str = "<element1/>";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx));
+
+ /* default namespace defined in parent element1 */
+ assert_int_equal(LY_SUCCESS, lyxml_ns_add(xmlctx, NULL, 0, strdup("urn:default")));
+ assert_int_equal(1, xmlctx->ns.count);
+ /* going into child element1 */
+ /* simulate adding open element1 into context */
+ xmlctx->elements.count++;
+ /* no namespace defined, going out (first, simulate closing of so far open element) */
+ xmlctx->elements.count--;
+ lyxml_ns_rm(xmlctx);
+ assert_int_equal(1, xmlctx->ns.count);
+
+ /* nothing else, going out of the parent element1 */
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(0, xmlctx->ns.count);
+
+ lyxml_ctx_free(xmlctx);
+ ly_in_free(in, 0);
+}
+
+static void
+test_simple_xml(void **state)
+{
+ struct lyxml_ctx *xmlctx;
+ struct ly_in *in;
+ const char *test_input = "<elem1 attr1=\"value\"> <elem2 attr2=\"value\" /> </elem1>";
+
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(test_input, &in));
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx));
+ assert_int_equal(LYXML_ELEMENT, xmlctx->status);
+ assert_string_equal(xmlctx->in->current, "attr1=\"value\"> <elem2 attr2=\"value\" /> </elem1>");
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ATTRIBUTE, xmlctx->status);
+ assert_string_equal(xmlctx->in->current, "=\"value\"> <elem2 attr2=\"value\" /> </elem1>");
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ATTR_CONTENT, xmlctx->status);
+ assert_string_equal(xmlctx->in->current, "> <elem2 attr2=\"value\" /> </elem1>");
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ELEM_CONTENT, xmlctx->status);
+ assert_string_equal(xmlctx->in->current, "<elem2 attr2=\"value\" /> </elem1>");
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ELEMENT, xmlctx->status);
+ assert_string_equal(xmlctx->in->current, "attr2=\"value\" /> </elem1>");
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ATTRIBUTE, xmlctx->status);
+ assert_string_equal(xmlctx->in->current, "=\"value\" /> </elem1>");
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ATTR_CONTENT, xmlctx->status);
+ assert_string_equal(xmlctx->in->current, " /> </elem1>");
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ELEM_CONTENT, xmlctx->status);
+ assert_string_equal(xmlctx->in->current, "/> </elem1>");
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ELEM_CLOSE, xmlctx->status);
+ assert_string_equal(xmlctx->in->current, " </elem1>");
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_ELEM_CLOSE, xmlctx->status);
+ assert_string_equal(xmlctx->in->current, "");
+
+ assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx));
+ assert_int_equal(LYXML_END, xmlctx->status);
+ assert_string_equal(xmlctx->in->current, "");
+
+ lyxml_ctx_free(xmlctx);
+ ly_in_free(in, 0);
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_element),
+ UTEST(test_attribute),
+ UTEST(test_text),
+ UTEST(test_ns),
+ UTEST(test_ns2),
+ UTEST(test_simple_xml),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/basic/test_xpath.c b/tests/utests/basic/test_xpath.c
new file mode 100644
index 0000000..7a2785b
--- /dev/null
+++ b/tests/utests/basic/test_xpath.c
@@ -0,0 +1,1263 @@
+/**
+ * @file test_xpath.c
+ * @author: Michal Vasko <mvasko@cesnet.cz>
+ * @brief unit tests for XPath evaluation
+ *
+ * Copyright (c) 2020 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
+ */
+#define _UTEST_MAIN_
+#include "utests.h"
+
+#include <string.h>
+
+#include "context.h"
+#include "parser_data.h"
+#include "set.h"
+#include "tests_config.h"
+#include "tree_data.h"
+#include "tree_schema.h"
+
+const char *schema_a =
+ "module a {\n"
+ " namespace urn:tests:a;\n"
+ " prefix a;\n"
+ " yang-version 1.1;\n"
+ "\n"
+ " identity id_a;\n"
+ " identity id_b {\n"
+ " base id_a;\n"
+ " }\n"
+ " identity id_c {\n"
+ " base id_b;\n"
+ " }\n"
+ "\n"
+ " list l1 {\n"
+ " key \"a b\";\n"
+ " leaf a {\n"
+ " type string;\n"
+ " }\n"
+ " leaf b {\n"
+ " type string;\n"
+ " }\n"
+ " leaf c {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " leaf foo {\n"
+ " type string;\n"
+ " }\n"
+ " leaf foo2 {\n"
+ " type uint8;\n"
+ " }\n"
+ " leaf foo3 {\n"
+ " type identityref {\n"
+ " base id_a;\n"
+ " }\n"
+ " }\n"
+ " leaf foo4 {\n"
+ " type decimal64 {\n"
+ " fraction-digits 5;\n"
+ " }\n"
+ " }\n"
+ " container c {\n"
+ " leaf x {\n"
+ " type string;\n"
+ " }\n"
+ " list ll {\n"
+ " key \"a\";\n"
+ " leaf a {\n"
+ " type string;\n"
+ " }\n"
+ " list ll {\n"
+ " key \"a\";\n"
+ " leaf a {\n"
+ " type string;\n"
+ " }\n"
+ " leaf b {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " leaf-list ll2 {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ "\n"
+ " rpc r {\n"
+ " input {\n"
+ " leaf l {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " output {\n"
+ " leaf l {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "}";
+
+static int
+setup(void **state)
+{
+ UTEST_SETUP;
+
+ UTEST_ADD_MODULE(schema_a, LYS_IN_YANG, NULL, NULL);
+ lys_parse_path(UTEST_LYCTX, TESTS_DIR_MODULES_YANG "/ietf-interfaces@2014-05-08.yang", LYS_IN_YANG, NULL);
+
+ return 0;
+}
+
+static void
+test_predicate(void **state)
+{
+ const char *data;
+ struct lyd_node *tree;
+ struct ly_set *set;
+
+ data =
+ "<foo2 xmlns=\"urn:tests:a\">50</foo2>"
+ "<l1 xmlns=\"urn:tests:a\">"
+ " <a>a1</a>"
+ " <b>b1</b>"
+ " <c>c1</c>"
+ "</l1>"
+ "<l1 xmlns=\"urn:tests:a\">"
+ " <a>a2</a>"
+ " <b>b2</b>"
+ "</l1>"
+ "<l1 xmlns=\"urn:tests:a\">"
+ " <a>a3</a>"
+ " <b>b3</b>"
+ "</l1>"
+ "<l1 xmlns=\"urn:tests:a\">"
+ " <a>a4</a>"
+ " <b>b4</b>"
+ " <c>c4</c>"
+ "</l1>"
+ "<l1 xmlns=\"urn:tests:a\">"
+ " <a>a5</a>"
+ " <b>b5</b>"
+ " <c>c5</c>"
+ "</l1>"
+ "<c xmlns=\"urn:tests:a\">"
+ " <x>key2</x>"
+ " <ll>"
+ " <a>key1</a>"
+ " <ll>"
+ " <a>key11</a>"
+ " <b>val11</b>"
+ " </ll>"
+ " <ll>"
+ " <a>key12</a>"
+ " <b>val12</b>"
+ " </ll>"
+ " <ll>"
+ " <a>key13</a>"
+ " <b>val13</b>"
+ " </ll>"
+ " </ll>"
+ " <ll>"
+ " <a>key2</a>"
+ " <ll>"
+ " <a>key21</a>"
+ " <b>val21</b>"
+ " </ll>"
+ " <ll>"
+ " <a>key22</a>"
+ " <b>val22</b>"
+ " </ll>"
+ " </ll>"
+ " <ll>"
+ " <a>key3</a>"
+ " <ll>"
+ " <a>key31</a>"
+ " <b>val31</b>"
+ " </ll>"
+ " <ll>"
+ " <a>key32</a>"
+ " <b>val32</b>"
+ " </ll>"
+ " </ll>"
+ "</c>";
+ assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree));
+ assert_non_null(tree);
+
+ /* predicate after number */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/foo2[4[3 = 3]]", &set));
+ assert_int_equal(0, set->count);
+ ly_set_free(set, NULL);
+
+ /* reverse axis */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/c/child::ll[2]/preceding::ll[3]", &set));
+ assert_int_equal(1, set->count);
+ assert_string_equal("key11", lyd_get_value(lyd_child(set->dnodes[0])));
+ ly_set_free(set, NULL);
+
+ /* special predicate evaluated using hashes */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:l1[a=concat('a', '1')][b=substring('ab1',2)]", &set));
+ assert_int_equal(1, set->count);
+ ly_set_free(set, NULL);
+
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:c/ll[a=../x]", &set));
+ assert_int_equal(1, set->count);
+ ly_set_free(set, NULL);
+
+ /* cannot use hashes */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:c/ll[a=substring(ll/a,1,4)]", &set));
+ assert_int_equal(3, set->count);
+ ly_set_free(set, NULL);
+
+ /* nested predicate */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:c/a:ll[a:a=string(/a:l1[a:a='foo']/a:a)]/a:a", &set));
+ assert_int_equal(0, set->count);
+ ly_set_free(set, NULL);
+
+ lyd_free_all(tree);
+}
+
+static void
+test_union(void **state)
+{
+ const char *data;
+ struct lyd_node *tree;
+ struct ly_set *set;
+
+ data =
+ "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>a1</a>\n"
+ " <b>b1</b>\n"
+ " <c>c1</c>\n"
+ "</l1>\n"
+ "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>a2</a>\n"
+ " <b>b2</b>\n"
+ "</l1>"
+ "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>a3</a>\n"
+ " <b>b3</b>\n"
+ " <c>c3</c>\n"
+ "</l1>";
+ assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree));
+ assert_non_null(tree);
+
+ /* Predicate for operand. */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/l1[c[../a = 'a1'] | c]/a", &set));
+ ly_set_free(set, NULL);
+
+ lyd_free_all(tree);
+}
+
+static void
+test_invalid(void **state)
+{
+ const char *data =
+ "<foo2 xmlns=\"urn:tests:a\">50</foo2>";
+ struct lyd_node *tree;
+ struct ly_set *set;
+
+ assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree));
+ assert_non_null(tree);
+
+ assert_int_equal(LY_EVALID, lyd_find_xpath(tree, "/a:foo2[.=]", &set));
+ assert_null(set);
+ CHECK_LOG_CTX("Unexpected XPath token \"]\" (\"]\").", NULL, 0);
+
+ assert_int_equal(LY_EVALID, lyd_find_xpath(tree, "/a:", &set));
+ assert_null(set);
+ CHECK_LOG_CTX("Invalid character 'a'[2] of expression '/a:'.", NULL, 0);
+
+ lyd_free_all(tree);
+}
+
+static void
+test_hash(void **state)
+{
+ const char *data =
+ "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>a1</a>\n"
+ " <b>b1</b>\n"
+ " <c>c1</c>\n"
+ "</l1>\n"
+ "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>a2</a>\n"
+ " <b>b2</b>\n"
+ "</l1>\n"
+ "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>a3</a>\n"
+ " <b>b3</b>\n"
+ " <c>c3</c>\n"
+ "</l1>\n"
+ "<foo xmlns=\"urn:tests:a\">foo value</foo>\n"
+ "<c xmlns=\"urn:tests:a\">\n"
+ " <x>val</x>\n"
+ " <ll>\n"
+ " <a>val_a</a>\n"
+ " <ll>\n"
+ " <a>val_a</a>\n"
+ " <b>val</b>\n"
+ " </ll>\n"
+ " <ll>\n"
+ " <a>val_b</a>\n"
+ " </ll>\n"
+ " </ll>\n"
+ " <ll>\n"
+ " <a>val_b</a>\n"
+ " <ll>\n"
+ " <a>val_a</a>\n"
+ " </ll>\n"
+ " <ll>\n"
+ " <a>val_b</a>\n"
+ " <b>val</b>\n"
+ " </ll>\n"
+ " </ll>\n"
+ " <ll>\n"
+ " <a>val_c</a>\n"
+ " <ll>\n"
+ " <a>val_a</a>\n"
+ " </ll>\n"
+ " <ll>\n"
+ " <a>val_b</a>\n"
+ " </ll>\n"
+ " </ll>\n"
+ " <ll2>one</ll2>\n"
+ " <ll2>two</ll2>\n"
+ " <ll2>three</ll2>\n"
+ " <ll2>four</ll2>\n"
+ "</c>";
+ struct lyd_node *tree, *node;
+ struct ly_set *set;
+
+ assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree));
+ assert_non_null(tree);
+
+ /* top-level, so hash table is not ultimately used but instances can be compared based on hashes */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:l1[a='a3'][b='b3']", &set));
+ assert_int_equal(1, set->count);
+
+ node = set->objs[0];
+ assert_string_equal(node->schema->name, "l1");
+ node = lyd_child(node);
+ assert_string_equal(node->schema->name, "a");
+ assert_string_equal(lyd_get_value(node), "a3");
+
+ ly_set_free(set, NULL);
+
+ /* hashes should be used for both searches (well, there are not enough nested ll instances, so technically not true) */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:c/ll[a='val_b']/ll[a='val_b']", &set));
+ assert_int_equal(1, set->count);
+
+ node = set->objs[0];
+ assert_string_equal(node->schema->name, "ll");
+ node = lyd_child(node);
+ assert_string_equal(node->schema->name, "a");
+ assert_string_equal(lyd_get_value(node), "val_b");
+ node = node->next;
+ assert_string_equal(node->schema->name, "b");
+ assert_null(node->next);
+
+ ly_set_free(set, NULL);
+
+ /* hashes are not used */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:c//ll[a='val_b']", &set));
+ assert_int_equal(4, set->count);
+
+ ly_set_free(set, NULL);
+
+ /* hashes used even for leaf-lists */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:c/ll2[. = 'three']", &set));
+ assert_int_equal(1, set->count);
+
+ node = set->objs[0];
+ assert_string_equal(node->schema->name, "ll2");
+ assert_string_equal(lyd_get_value(node), "three");
+
+ ly_set_free(set, NULL);
+
+ /* not found using hashes */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:c/ll[a='val_d']", &set));
+ assert_int_equal(0, set->count);
+
+ ly_set_free(set, NULL);
+
+ /* white-spaces are also ok */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:c/ll[ \na = 'val_c' ]", &set));
+ assert_int_equal(1, set->count);
+
+ ly_set_free(set, NULL);
+
+ lyd_free_all(tree);
+}
+
+static void
+test_rpc(void **state)
+{
+ const char *data =
+ "<r xmlns=\"urn:tests:a\">\n"
+ " <l>val</l>\n"
+ "</r>";
+ struct ly_in *in;
+ struct lyd_node *tree;
+ struct ly_set *set;
+
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+ assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_XML, LYD_TYPE_REPLY_YANG, &tree, NULL));
+ ly_in_free(in, 0);
+ assert_non_null(tree);
+
+ /* name collision input/output, hashes are not used */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:r/l", &set));
+ assert_int_equal(1, set->count);
+
+ ly_set_free(set, NULL);
+
+ lyd_free_all(tree);
+}
+
+static void
+test_toplevel(void **state)
+{
+ const char *schema_b =
+ "module b {\n"
+ " namespace urn:tests:b;\n"
+ " prefix b;\n"
+ " yang-version 1.1;\n"
+ "\n"
+ " list l2 {\n"
+ " key \"a\";\n"
+ " leaf a {\n"
+ " type uint16;\n"
+ " }\n"
+ " leaf b {\n"
+ " type uint16;\n"
+ " }\n"
+ " }\n"
+ "}";
+ const char *data =
+ "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>a1</a>\n"
+ " <b>b1</b>\n"
+ " <c>c1</c>\n"
+ "</l1>\n"
+ "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>a2</a>\n"
+ " <b>b2</b>\n"
+ "</l1>\n"
+ "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>a3</a>\n"
+ " <b>b3</b>\n"
+ " <c>c3</c>\n"
+ "</l1>\n"
+ "<foo xmlns=\"urn:tests:a\">foo value</foo>\n"
+ "<l2 xmlns=\"urn:tests:b\">\n"
+ " <a>1</a>\n"
+ " <b>1</b>\n"
+ "</l2>\n"
+ "<l2 xmlns=\"urn:tests:b\">\n"
+ " <a>2</a>\n"
+ " <b>1</b>\n"
+ "</l2>\n"
+ "<l2 xmlns=\"urn:tests:b\">\n"
+ " <a>3</a>\n"
+ " <b>1</b>\n"
+ "</l2>";
+ struct lyd_node *tree;
+ struct ly_set *set;
+
+ UTEST_ADD_MODULE(schema_b, LYS_IN_YANG, NULL, NULL);
+
+ assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree));
+ assert_non_null(tree);
+
+ /* all top-level nodes from one module (default container as well) */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:*", &set));
+ assert_int_equal(5, set->count);
+
+ ly_set_free(set, NULL);
+
+ /* all top-level nodes from all modules */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/*", &set));
+ assert_int_equal(8, set->count);
+
+ ly_set_free(set, NULL);
+
+ /* all nodes from one module */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "//a:*", &set));
+ assert_int_equal(13, set->count);
+
+ ly_set_free(set, NULL);
+
+ /* all nodes from all modules */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "//*", &set));
+ assert_int_equal(22, set->count);
+
+ ly_set_free(set, NULL);
+
+ /* all nodes from all modules #2 */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "//.", &set));
+ assert_int_equal(22, set->count);
+
+ ly_set_free(set, NULL);
+
+ lyd_free_all(tree);
+}
+
+static void
+test_atomize(void **state)
+{
+ struct ly_set *set;
+ const struct lys_module *mod;
+
+ mod = ly_ctx_get_module_latest(UTEST_LYCTX, "a");
+ assert_non_null(mod);
+
+ /* some random paths just making sure the API function works */
+ assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/a:*", 0, &set));
+ assert_int_equal(7, set->count);
+ ly_set_free(set, NULL);
+
+ /* all nodes from all modules (including internal, which can change easily, so check just the test modules) */
+ assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "//.", 0, &set));
+ assert_in_range(set->count, 17, UINT32_MAX);
+ ly_set_free(set, NULL);
+
+ assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/a:c/ll[a='val1']/ll[a='val2']/b", 0, &set));
+ assert_int_equal(6, set->count);
+ ly_set_free(set, NULL);
+
+ assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/ietf-interfaces:interfaces/*", 0, &set));
+ assert_int_equal(2, set->count);
+ ly_set_free(set, NULL);
+
+ assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/*", 0, &set));
+ assert_int_equal(14, set->count);
+ ly_set_free(set, NULL);
+
+ /*
+ * axes
+ */
+
+ /* ancestor */
+ assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "//ll[a and b]/a/ancestor::node()", 0, &set));
+ assert_int_equal(6, set->count);
+ ly_set_free(set, NULL);
+
+ /* ancestor-or-self */
+ assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "//ll[a and b]/ancestor-or-self::ll", 0, &set));
+ assert_int_equal(5, set->count);
+ ly_set_free(set, NULL);
+
+ /* attribute */
+ assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/l1/attribute::key", 0, &set));
+ assert_int_equal(1, set->count);
+ ly_set_free(set, NULL);
+
+ /* child */
+ assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/child::l1/child::a", 0, &set));
+ assert_int_equal(2, set->count);
+ ly_set_free(set, NULL);
+
+ /* descendant */
+ assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/descendant::c/descendant::b", 0, &set));
+ assert_int_equal(3, set->count);
+ ly_set_free(set, NULL);
+
+ /* descendant-or-self */
+ assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/a:*/descendant-or-self::c", 0, &set));
+ assert_int_equal(8, set->count);
+ ly_set_free(set, NULL);
+
+ /* following */
+ assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/c/x/following::a", 0, &set));
+ assert_int_equal(4, set->count);
+ ly_set_free(set, NULL);
+
+ /* following-sibling */
+ assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/c/x/following-sibling::ll", 0, &set));
+ assert_int_equal(3, set->count);
+ ly_set_free(set, NULL);
+
+ /* parent */
+ assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/child::a:*/c/parent::l1", 0, &set));
+ assert_int_equal(8, set->count);
+ ly_set_free(set, NULL);
+
+ assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/child::a:c//..", 0, &set));
+ assert_int_equal(11, set->count);
+ ly_set_free(set, NULL);
+
+ /* preceding */
+ assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/c/preceding::a", 0, &set));
+ assert_int_equal(2, set->count);
+ ly_set_free(set, NULL);
+
+ /* preceding-sibling */
+ assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/c/ll/preceding-sibling::node()", 0, &set));
+ assert_int_equal(3, set->count);
+ ly_set_free(set, NULL);
+
+ /* self */
+ assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/c/self::c/ll/ll/b/self::b", 0, &set));
+ assert_int_equal(4, set->count);
+ ly_set_free(set, NULL);
+}
+
+static void
+test_canonize(void **state)
+{
+ const char *data =
+ "<foo2 xmlns=\"urn:tests:a\">50</foo2>"
+ "<foo3 xmlns=\"urn:tests:a\" xmlns:a=\"urn:tests:a\">a:id_b</foo3>"
+ "<foo4 xmlns=\"urn:tests:a\">250.5</foo4>";
+ struct lyd_node *tree;
+ struct ly_set *set;
+
+ assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree));
+ assert_non_null(tree);
+
+ /* integer */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:foo2[.='050']", &set));
+ assert_int_equal(1, set->count);
+ ly_set_free(set, NULL);
+
+ /* identityref */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:foo3[.='id_b']", &set));
+ assert_int_equal(1, set->count);
+ ly_set_free(set, NULL);
+
+ /* decimal64 */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:foo4[.='0250.500']", &set));
+ assert_int_equal(1, set->count);
+ ly_set_free(set, NULL);
+
+ lyd_free_all(tree);
+}
+
+static void
+test_derived_from(void **state)
+{
+ const char *data =
+ "<foo3 xmlns=\"urn:tests:a\">id_c</foo3>";
+ struct lyd_node *tree;
+ struct ly_set *set;
+
+ assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree));
+ assert_non_null(tree);
+
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:foo3[derived-from(., 'a:id_b')]", &set));
+ assert_int_equal(1, set->count);
+ ly_set_free(set, NULL);
+
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:foo3[derived-from(., 'a:id_a')]", &set));
+ assert_int_equal(1, set->count);
+ ly_set_free(set, NULL);
+
+ lyd_free_all(tree);
+}
+
+static void
+test_augment(void **state)
+{
+ const char *schema_b =
+ "module b {\n"
+ " namespace urn:tests:b;\n"
+ " prefix b;\n"
+ " yang-version 1.1;\n"
+ "\n"
+ " import a {\n"
+ " prefix a;\n"
+ " }\n"
+ "\n"
+ " augment /a:c {\n"
+ " leaf a {\n"
+ " type uint16;\n"
+ " }\n"
+ " }\n"
+ "}";
+ const char *data =
+ "<c xmlns=\"urn:tests:a\">\n"
+ " <x>value</x>\n"
+ " <ll>\n"
+ " <a>key</a>\n"
+ " </ll>\n"
+ " <a xmlns=\"urn:tests:b\">25</a>\n"
+ " <ll2>c1</ll2>\n"
+ "</c>";
+ struct lyd_node *tree;
+ struct ly_set *set;
+
+ UTEST_ADD_MODULE(schema_b, LYS_IN_YANG, NULL, NULL);
+
+ assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree));
+ assert_non_null(tree);
+
+ /* get all children ignoring their module */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:c/*", &set));
+ assert_int_equal(4, set->count);
+
+ ly_set_free(set, NULL);
+
+ lyd_free_all(tree);
+}
+
+static void
+test_variables(void **state)
+{
+ struct lyd_node *tree, *node;
+ struct ly_set *set;
+ const char *data;
+ struct lyxp_var *vars = NULL;
+
+#define LOCAL_SETUP(DATA, TREE) \
+ assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, DATA, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &TREE)); \
+ assert_non_null(TREE);
+
+#define SET_NODE(NODE, SET, INDEX) \
+ assert_non_null(SET); \
+ assert_true(INDEX < SET->count); \
+ NODE = SET->objs[INDEX];
+
+#define LOCAL_TEARDOWN(SET, TREE, VARS) \
+ ly_set_free(SET, NULL); \
+ lyd_free_all(TREE); \
+ lyxp_vars_free(VARS); \
+ vars = NULL;
+
+ /* Eval variable to number. */
+ data =
+ "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>a1</a>\n"
+ " <b>b1</b>\n"
+ " <c>c1</c>\n"
+ "</l1>"
+ "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>a2</a>\n"
+ " <b>b2</b>\n"
+ " <c>c2</c>\n"
+ "</l1>";
+ LOCAL_SETUP(data, tree);
+ assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "2"));
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/l1[$var]/a", vars, &set));
+ SET_NODE(node, set, 0);
+ assert_string_equal(lyd_get_value(node), "a2");
+ LOCAL_TEARDOWN(set, tree, vars);
+
+ /* Eval variable to string. */
+ data =
+ "<foo xmlns=\"urn:tests:a\">mstr</foo>";
+ LOCAL_SETUP(data, tree);
+ assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "\"mstr\""));
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/foo[text() = $var]", vars, &set));
+ SET_NODE(node, set, 0);
+ assert_string_equal(lyd_get_value(node), "mstr");
+ LOCAL_TEARDOWN(set, tree, vars);
+
+ /* Eval variable to set of nodes. */
+ data =
+ "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>a1</a>\n"
+ " <b>b1</b>\n"
+ "</l1>"
+ "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>a2</a>\n"
+ " <b>b2</b>\n"
+ " <c>c2</c>\n"
+ "</l1>";
+ LOCAL_SETUP(data, tree);
+ assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "c"));
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/l1[$var]/a", vars, &set));
+ SET_NODE(node, set, 0);
+ assert_string_equal(lyd_get_value(node), "a2");
+ LOCAL_TEARDOWN(set, tree, vars);
+
+ /* Variable in union expr. */
+ data =
+ "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>a1</a>\n"
+ " <b>b1</b>\n"
+ " <c>c1</c>\n"
+ "</l1>"
+ "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>a2</a>\n"
+ " <b>b2</b>\n"
+ " <c>c2</c>\n"
+ "</l1>"
+ "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>a3</a>\n"
+ " <b>b3</b>\n"
+ " <c>c3</c>\n"
+ "</l1>";
+ LOCAL_SETUP(data, tree);
+ assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "c[../a = 'a3']"));
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/l1[c[../a = 'a1'] | $var]/a", vars, &set));
+ SET_NODE(node, set, 0);
+ assert_string_equal(lyd_get_value(node), "a1");
+ SET_NODE(node, set, 1);
+ assert_string_equal(lyd_get_value(node), "a3");
+ assert_int_equal(set->count, 2);
+ LOCAL_TEARDOWN(set, tree, vars);
+
+ /* Predicate after variable. */
+ data =
+ "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>a1</a>\n"
+ " <b>b1</b>\n"
+ " <c>c1</c>\n"
+ "</l1>"
+ "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>a2</a>\n"
+ " <b>b2</b>\n"
+ " <c>c2</c>\n"
+ "</l1>";
+ LOCAL_SETUP(data, tree);
+ assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "c"));
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/l1[$var[../a = 'a1']]/a", vars, &set));
+ SET_NODE(node, set, 0);
+ assert_string_equal(lyd_get_value(node), "a1");
+ LOCAL_TEARDOWN(set, tree, vars);
+
+ /* Variable in variable. */
+ data =
+ "<foo xmlns=\"urn:tests:a\">mstr</foo>";
+ LOCAL_SETUP(data, tree);
+ assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var1", "$var2"));
+ assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var2", "\"mstr\""));
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/foo[text() = $var]", vars, &set));
+ SET_NODE(node, set, 0);
+ assert_string_equal(lyd_get_value(node), "mstr");
+ LOCAL_TEARDOWN(set, tree, vars);
+
+ /* Compare two variables. */
+ data =
+ "<foo xmlns=\"urn:tests:a\">mstr</foo>";
+ LOCAL_SETUP(data, tree);
+ assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var1", "\"str\""));
+ assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var2", "\"str\""));
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/foo[$var1 = $var2]", vars, &set));
+ SET_NODE(node, set, 0);
+ assert_string_equal(lyd_get_value(node), "mstr");
+ LOCAL_TEARDOWN(set, tree, vars);
+
+ /* Arithmetic operation with variable. */
+ data =
+ "<foo2 xmlns=\"urn:tests:a\">4</foo2>";
+ LOCAL_SETUP(data, tree);
+ assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var1", "2"));
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/foo2[.= ($var1 * 2)]", vars, &set));
+ SET_NODE(node, set, 0);
+ assert_string_equal(lyd_get_value(node), "4");
+ LOCAL_TEARDOWN(set, tree, vars);
+
+ /* Variable as function parameter. */
+ data =
+ "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>a1</a>\n"
+ " <b>b1</b>\n"
+ " <c>c1</c>\n"
+ "</l1>"
+ "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>a2</a>\n"
+ " <b>b2</b>\n"
+ "</l1>";
+ LOCAL_SETUP(data, tree);
+ assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "./c"));
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/l1[count($var) = 1]/a", vars, &set));
+ SET_NODE(node, set, 0);
+ assert_string_equal(lyd_get_value(node), "a1");
+ LOCAL_TEARDOWN(set, tree, vars);
+
+ /* Variable in path expr. */
+ /* NOTE: The variable can only be at the beginning of the expression path. */
+ data =
+ "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>a1</a>\n"
+ " <b>b1</b>\n"
+ " <c>c1</c>\n"
+ "</l1>"
+ "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>a2</a>\n"
+ " <b>b2</b>\n"
+ "</l1>";
+ LOCAL_SETUP(data, tree);
+ assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "/l1"));
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/l1[$var/a]", vars, &set));
+ assert_int_equal(set->count, 2);
+ LOCAL_TEARDOWN(set, tree, vars);
+
+ /* Variable as function. */
+ data =
+ "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>a1</a>\n"
+ " <b>b1</b>\n"
+ " <c>c1</c>\n"
+ "</l1>"
+ "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>a2</a>\n"
+ " <b>b2</b>\n"
+ "</l1>";
+ LOCAL_SETUP(data, tree);
+ assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "position()"));
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/l1[$var = 2]/a", vars, &set));
+ SET_NODE(node, set, 0);
+ assert_string_equal(lyd_get_value(node), "a2");
+ LOCAL_TEARDOWN(set, tree, vars);
+
+ /* Dynamic change of value. */
+ data =
+ "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>a1</a>\n"
+ " <b>b1</b>\n"
+ " <c>c1</c>\n"
+ "</l1>"
+ "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>a2</a>\n"
+ " <b>b2</b>\n"
+ " <c>c2</c>\n"
+ "</l1>";
+ LOCAL_SETUP(data, tree);
+ assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "1"));
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/l1[$var]/a", vars, &set));
+ SET_NODE(node, set, 0);
+ assert_string_equal(lyd_get_value(node), "a1");
+ ly_set_free(set, NULL);
+ assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "2"));
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/l1[$var]/a", vars, &set));
+ SET_NODE(node, set, 0);
+ assert_string_equal(lyd_get_value(node), "a2");
+ LOCAL_TEARDOWN(set, tree, vars);
+
+ /* Variable not defined. */
+ data =
+ "<foo xmlns=\"urn:tests:a\">mstr</foo>";
+ LOCAL_SETUP(data, tree);
+ assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var1", "\"mstr\""));
+ assert_int_equal(LY_ENOTFOUND, lyd_find_xpath2(tree, "/foo[text() = $var55]", vars, &set));
+ CHECK_LOG_CTX("Variable \"var55\" not defined.", NULL, 0);
+ LOCAL_TEARDOWN(set, tree, vars);
+
+ /* Syntax error in value. */
+ data =
+ "<foo xmlns=\"urn:tests:a\">mstr</foo>";
+ LOCAL_SETUP(data, tree);
+ assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "\""));
+ assert_int_equal(LY_EVALID, lyd_find_xpath2(tree, "/foo[$var]", vars, &set));
+ CHECK_LOG_CTX("Unterminated string delimited with \" (\").", "/a:foo", 0);
+ LOCAL_TEARDOWN(set, tree, vars);
+
+ /* Prefix is not supported. */
+ data =
+ "<foo xmlns=\"urn:tests:a\">mstr</foo>";
+ LOCAL_SETUP(data, tree);
+ assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "\""));
+ assert_int_equal(LY_EVALID, lyd_find_xpath2(tree, "/foo[$pref:var]", vars, &set));
+ CHECK_LOG_CTX("Variable with prefix is not supported.", NULL, 0);
+ LOCAL_TEARDOWN(set, tree, vars);
+
+#undef LOCAL_SETUP
+#undef LOCAL_TEARDOWN
+}
+
+static void
+test_axes(void **state)
+{
+ const char *data;
+ struct lyd_node *tree;
+ struct ly_set *set;
+
+ data =
+ "<l1 xmlns=\"urn:tests:a\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\">\n"
+ " <a>a1</a>\n"
+ " <b yang:operation=\"replace\">b1</b>\n"
+ " <c yang:operation=\"none\">c1</c>\n"
+ "</l1>\n"
+ "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>a2</a>\n"
+ " <b>b2</b>\n"
+ "</l1>"
+ "<l1 xmlns=\"urn:tests:a\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\">\n"
+ " <a yang:operation=\"none\" yang:key=\"[no-key='no-value']\">a3</a>\n"
+ " <b>b3</b>\n"
+ " <c yang:value=\"no-val\">c3</c>\n"
+ "</l1>"
+ "<c xmlns=\"urn:tests:a\">"
+ " <x>val</x>"
+ " <ll>"
+ " <a>key1</a>"
+ " <ll>"
+ " <a>key11</a>"
+ " <b>val11</b>"
+ " </ll>"
+ " <ll>"
+ " <a>key12</a>"
+ " <b>val12</b>"
+ " </ll>"
+ " <ll>"
+ " <a>key13</a>"
+ " <b>val13</b>"
+ " </ll>"
+ " </ll>"
+ " <ll>"
+ " <a>key2</a>"
+ " <ll>"
+ " <a>key21</a>"
+ " <b>val21</b>"
+ " </ll>"
+ " <ll>"
+ " <a>key22</a>"
+ " <b>val22</b>"
+ " </ll>"
+ " </ll>"
+ "</c>";
+ assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree));
+ assert_non_null(tree);
+
+ /* ancestor */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "//ll[a and b]/a/ancestor::node()", &set));
+ assert_int_equal(8, set->count);
+ ly_set_free(set, NULL);
+
+ /* ancestor-or-self */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "//ll[a and b]/ancestor-or-self::ll", &set));
+ assert_int_equal(7, set->count);
+ ly_set_free(set, NULL);
+
+ /* attribute */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/l1/@operation", &set));
+ assert_int_equal(0, set->count);
+ ly_set_free(set, NULL);
+
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/l1/attribute::key", &set));
+ assert_int_equal(0, set->count);
+ ly_set_free(set, NULL);
+
+ /* child */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/child::l1/child::a", &set));
+ assert_int_equal(3, set->count);
+ ly_set_free(set, NULL);
+
+ /* descendant */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/descendant::c/descendant::b", &set));
+ assert_int_equal(5, set->count);
+ ly_set_free(set, NULL);
+
+ /* descendant-or-self */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "//c", &set));
+ assert_int_equal(3, set->count);
+ ly_set_free(set, NULL);
+
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/descendant-or-self::node()/c", &set));
+ assert_int_equal(3, set->count);
+ ly_set_free(set, NULL);
+
+ /* following */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/c/x/following::a", &set));
+ assert_int_equal(7, set->count);
+ ly_set_free(set, NULL);
+
+ /* following-sibling */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/c/x/following-sibling::ll", &set));
+ assert_int_equal(2, set->count);
+ ly_set_free(set, NULL);
+
+ /* parent */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/child::*/c/parent::l1", &set));
+ assert_int_equal(2, set->count);
+ ly_set_free(set, NULL);
+
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/child::c//..", &set));
+ assert_int_equal(8, set->count);
+ ly_set_free(set, NULL);
+
+ /* preceding */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/c/preceding::a", &set));
+ assert_int_equal(3, set->count);
+ ly_set_free(set, NULL);
+
+ /* preceding-sibling */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/c/ll/preceding-sibling::node()", &set));
+ assert_int_equal(2, set->count);
+ ly_set_free(set, NULL);
+
+ /* self */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/c/self::c/ll/ll/b/self::b", &set));
+ assert_int_equal(5, set->count);
+ ly_set_free(set, NULL);
+
+ lyd_free_all(tree);
+}
+
+static void
+test_trim(void **state)
+{
+ const char *data;
+ char *str1;
+ struct lyd_node *tree;
+
+ data =
+ "<l1 xmlns=\"urn:tests:a\">"
+ " <a>a1</a>"
+ " <b>b1</b>"
+ " <c>c1</c>"
+ "</l1>"
+ "<l1 xmlns=\"urn:tests:a\">"
+ " <a>a2</a>"
+ " <b>b2</b>"
+ "</l1>"
+ "<l1 xmlns=\"urn:tests:a\">"
+ " <a>a3</a>"
+ " <b>b3</b>"
+ "</l1>"
+ "<l1 xmlns=\"urn:tests:a\">"
+ " <a>a4</a>"
+ " <b>b4</b>"
+ " <c>c4</c>"
+ "</l1>"
+ "<l1 xmlns=\"urn:tests:a\">"
+ " <a>a5</a>"
+ " <b>b5</b>"
+ " <c>c5</c>"
+ "</l1>"
+ "<foo2 xmlns=\"urn:tests:a\">50</foo2>"
+ "<c xmlns=\"urn:tests:a\">"
+ " <x>key2</x>"
+ " <ll>"
+ " <a>key1</a>"
+ " <ll>"
+ " <a>key11</a>"
+ " <b>val11</b>"
+ " </ll>"
+ " <ll>"
+ " <a>key12</a>"
+ " <b>val12</b>"
+ " </ll>"
+ " <ll>"
+ " <a>key13</a>"
+ " <b>val13</b>"
+ " </ll>"
+ " </ll>"
+ " <ll>"
+ " <a>key2</a>"
+ " <ll>"
+ " <a>key21</a>"
+ " <b>val21</b>"
+ " </ll>"
+ " <ll>"
+ " <a>key22</a>"
+ " <b>val22</b>"
+ " </ll>"
+ " </ll>"
+ " <ll>"
+ " <a>key3</a>"
+ " <ll>"
+ " <a>key31</a>"
+ " <b>val31</b>"
+ " </ll>"
+ " <ll>"
+ " <a>key32</a>"
+ " <b>val32</b>"
+ " </ll>"
+ " </ll>"
+ "</c>";
+
+ /* trim #1 */
+ assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree));
+ assert_non_null(tree);
+
+ assert_int_equal(LY_SUCCESS, lyd_trim_xpath(&tree, "/a:c/ll/ll[a='key11']", NULL));
+ lyd_print_mem(&str1, tree, LYD_XML, LYD_PRINT_WITHSIBLINGS);
+ assert_string_equal(str1,
+ "<c xmlns=\"urn:tests:a\">\n"
+ " <ll>\n"
+ " <a>key1</a>\n"
+ " <ll>\n"
+ " <a>key11</a>\n"
+ " <b>val11</b>\n"
+ " </ll>\n"
+ " </ll>\n"
+ "</c>\n");
+
+ free(str1);
+ lyd_free_all(tree);
+
+ /* trim #2 */
+ assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree));
+ assert_non_null(tree);
+
+ assert_int_equal(LY_SUCCESS, lyd_trim_xpath(&tree, "/a:c/ll/ll[contains(.,'2')]", NULL));
+ lyd_print_mem(&str1, tree, LYD_XML, LYD_PRINT_WITHSIBLINGS);
+ assert_string_equal(str1,
+ "<c xmlns=\"urn:tests:a\">\n"
+ " <ll>\n"
+ " <a>key1</a>\n"
+ " <ll>\n"
+ " <a>key12</a>\n"
+ " <b>val12</b>\n"
+ " </ll>\n"
+ " </ll>\n"
+ " <ll>\n"
+ " <a>key2</a>\n"
+ " <ll>\n"
+ " <a>key21</a>\n"
+ " <b>val21</b>\n"
+ " </ll>\n"
+ " <ll>\n"
+ " <a>key22</a>\n"
+ " <b>val22</b>\n"
+ " </ll>\n"
+ " </ll>\n"
+ " <ll>\n"
+ " <a>key3</a>\n"
+ " <ll>\n"
+ " <a>key32</a>\n"
+ " <b>val32</b>\n"
+ " </ll>\n"
+ " </ll>\n"
+ "</c>\n");
+
+ free(str1);
+ lyd_free_all(tree);
+
+ /* trim #3 */
+ assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree));
+ assert_non_null(tree);
+
+ assert_int_equal(LY_SUCCESS, lyd_trim_xpath(&tree, "/l1[4]//.", NULL));
+ lyd_print_mem(&str1, tree, LYD_XML, LYD_PRINT_WITHSIBLINGS);
+ assert_string_equal(str1,
+ "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>a4</a>\n"
+ " <b>b4</b>\n"
+ " <c>c4</c>\n"
+ "</l1>\n");
+
+ free(str1);
+ lyd_free_all(tree);
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_predicate, setup),
+ UTEST(test_union, setup),
+ UTEST(test_invalid, setup),
+ UTEST(test_hash, setup),
+ UTEST(test_rpc, setup),
+ UTEST(test_toplevel, setup),
+ UTEST(test_atomize, setup),
+ UTEST(test_canonize, setup),
+ UTEST(test_derived_from, setup),
+ UTEST(test_augment, setup),
+ UTEST(test_variables, setup),
+ UTEST(test_axes, setup),
+ UTEST(test_trim, setup),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/basic/test_yanglib.c b/tests/utests/basic/test_yanglib.c
new file mode 100644
index 0000000..0349157
--- /dev/null
+++ b/tests/utests/basic/test_yanglib.c
@@ -0,0 +1,144 @@
+/**
+ * @file test_yanglib.c
+ * @author: Michal Vasko <mvasko@cesnet.cz>
+ * @brief unit tests for ietf-yang-library data
+ *
+ * Copyright (c) 2020 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
+ */
+#define _UTEST_MAIN_
+#include "utests.h"
+
+#include <string.h>
+
+#include "context.h"
+#include "in.h"
+#include "log.h"
+#include "set.h"
+#include "tests_config.h"
+#include "tree_data.h"
+#include "tree_schema.h"
+
+const char *schema_a =
+ "module a {\n"
+ " namespace urn:tests:a;\n"
+ " prefix a;\n"
+ " yang-version 1.1;\n"
+ "\n"
+ " include a_sub;\n"
+ "\n"
+ " list l2 {\n"
+ " key \"a\";\n"
+ " leaf a {\n"
+ " type uint16;\n"
+ " }\n"
+ " leaf b {\n"
+ " type uint16;\n"
+ " }\n"
+ " }\n"
+ "}";
+const char *schema_b =
+ "module b {\n"
+ " namespace urn:tests:b;\n"
+ " prefix b;\n"
+ " yang-version 1.1;\n"
+ "\n"
+ " import a {\n"
+ " prefix a;\n"
+ " }\n"
+ "\n"
+ " deviation /a:l2 {\n"
+ " deviate add {\n"
+ " max-elements 40;\n"
+ " }\n"
+ " }\n"
+ "\n"
+ " leaf foo {\n"
+ " type string;\n"
+ " }\n"
+ "}";
+
+static LY_ERR
+test_imp_clb(const char *mod_name, const char *mod_rev, const char *submod_name, const char *sub_rev, void *user_data,
+ LYS_INFORMAT *format, const char **module_data, void (**free_module_data)(void *model_data, void *user_data))
+{
+ const char *schema_a_sub =
+ "submodule a_sub {\n"
+ " belongs-to a {\n"
+ " prefix a;\n"
+ " }\n"
+ " yang-version 1.1;\n"
+ "\n"
+ " feature feat1;\n"
+ "\n"
+ " list l3 {\n"
+ " key \"a\";\n"
+ " leaf a {\n"
+ " type uint16;\n"
+ " }\n"
+ " leaf b {\n"
+ " type uint16;\n"
+ " }\n"
+ " }\n"
+ "}\n";
+
+ assert_string_equal(mod_name, "a");
+ assert_null(mod_rev);
+ if (!submod_name) {
+ return LY_ENOTFOUND;
+ }
+ assert_string_equal(submod_name, "a_sub");
+ assert_null(sub_rev);
+ assert_null(user_data);
+
+ *format = LYS_IN_YANG;
+ *module_data = schema_a_sub;
+ *free_module_data = NULL;
+ return LY_SUCCESS;
+}
+
+static void
+test_yanglib(void **state)
+{
+ const char *feats[] = {"feat1", NULL};
+ struct lyd_node *tree;
+ struct ly_set *set;
+ LY_ERR ret;
+
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, NULL);
+ UTEST_ADD_MODULE(schema_a, LYS_IN_YANG, feats, NULL);
+ UTEST_ADD_MODULE(schema_b, LYS_IN_YANG, NULL, NULL);
+
+ assert_int_equal(LY_SUCCESS, ly_ctx_get_yanglib_data(UTEST_LYCTX, &tree, "<<%u>>", ly_ctx_get_change_count(UTEST_LYCTX)));
+ lyd_free_all(tree);
+ assert_int_equal(LY_SUCCESS, ly_ctx_get_yanglib_data(UTEST_LYCTX, &tree, "%u", -10));
+ lyd_free_all(tree);
+ assert_int_equal(LY_SUCCESS, ly_ctx_get_yanglib_data(UTEST_LYCTX, &tree, ""));
+ lyd_free_all(tree);
+ assert_int_equal(LY_SUCCESS, ly_ctx_get_yanglib_data(UTEST_LYCTX, &tree, "%u", ly_ctx_get_change_count(UTEST_LYCTX)));
+
+ /* make sure there is "a" with a submodule and deviation */
+ ret = lyd_find_xpath(tree, "/ietf-yang-library:yang-library/module-set/module[name='a'][submodule/name='a_sub']"
+ "[feature='feat1'][deviation='b']", &set);
+ assert_int_equal(ret, LY_SUCCESS);
+
+ assert_int_equal(set->count, 1);
+ ly_set_free(set, NULL);
+
+ lyd_free_all(tree);
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_yanglib),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}