utests data REFACTOR put repetitive commands into macros
diff --git a/tests/utests/basic/test_common.c b/tests/utests/basic/test_common.c
new file mode 100644
index 0000000..41a1a2f
--- /dev/null
+++ b/tests/utests/basic/test_common.c
@@ -0,0 +1,274 @@
+/*
+ * @file set.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 "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] = 0x04;
+ assert_int_equal(LY_EINVAL, ly_getutf8(&str, &c, &len));
+ buf[0] = 0x80;
+ assert_int_equal(LY_EINVAL, ly_getutf8(&str, &c, &len));
+
+ buf[0] = 0xc0;
+ buf[1] = 0x00;
+ assert_int_equal(LY_EINVAL, ly_getutf8(&str, &c, &len));
+ buf[1] = 0x80;
+ assert_int_equal(LY_EINVAL, ly_getutf8(&str, &c, &len));
+
+ buf[0] = 0xe0;
+ buf[1] = 0x00;
+ buf[2] = 0x80;
+ assert_int_equal(LY_EINVAL, ly_getutf8(&str, &c, &len));
+ buf[1] = 0x80;
+ assert_int_equal(LY_EINVAL, ly_getutf8(&str, &c, &len));
+
+ buf[0] = 0xf0;
+ buf[1] = 0x00;
+ buf[2] = 0x80;
+ buf[3] = 0x80;
+ assert_int_equal(LY_EINVAL, ly_getutf8(&str, &c, &len));
+ buf[1] = 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.");
+}
+
+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),
+ };
+
+ 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..de535f0
--- /dev/null
+++ b/tests/utests/basic/test_context.c
@@ -0,0 +1,450 @@
+/*
+ * @file set.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 "common.h"
+#include "context.h"
+#include "in.h"
+#include "schema_compile.h"
+#include "tests/config.h"
+#include "tree_schema_internal.h"
+#include "utests.h"
+
+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("Invalid argument ctx (ly_ctx_set_searchdir()).", NULL);
+ assert_null(ly_ctx_get_searchdirs(NULL));
+ CHECK_LOG("Invalid argument ctx (ly_ctx_get_searchdirs()).", NULL);
+ assert_int_equal(LY_EINVAL, ly_ctx_unset_searchdir(NULL, NULL));
+ CHECK_LOG("Invalid argument ctx (ly_ctx_unset_searchdir()).", NULL);
+
+ /* 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);
+ /* not executable */
+ assert_int_equal(LY_EINVAL, ly_ctx_set_searchdir(UTEST_LYCTX, __FILE__));
+ CHECK_LOG_CTX("Unable to fully access search directory \""__FILE__ "\" (Permission denied).", NULL);
+ /* 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);
+
+ /* 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);
+ /* 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, NULL);
+
+ /* test searchdir list in ly_ctx_new() */
+ assert_int_equal(LY_EINVAL, ly_ctx_new("/nonexistingfile", 0, &UTEST_LYCTX));
+ CHECK_LOG("Unable to use search directory \"/nonexistingfile\" (No such file or directory).", NULL);
+ assert_int_equal(LY_SUCCESS, ly_ctx_new(TESTS_SRC ":"TESTS_BIN ":"TESTS_BIN ":"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]);
+}
+
+static void
+test_options(void **state)
+{
+ /* use own context with extra flags */
+ ly_ctx_destroy(UTEST_LYCTX, NULL);
+
+ 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("Invalid argument ctx (ly_ctx_get_options()).", NULL);
+
+ assert_int_equal(LY_EINVAL, ly_ctx_set_options(NULL, 0));
+ CHECK_LOG("Invalid argument ctx (ly_ctx_set_options()).", NULL);
+ assert_int_equal(LY_EINVAL, ly_ctx_unset_options(NULL, 0));
+ CHECK_LOG("Invalid argument ctx (ly_ctx_unset_options()).", NULL);
+
+ /* option not allowed to be changed */
+ assert_int_equal(LY_EINVAL, ly_ctx_set_options(UTEST_LYCTX, LY_CTX_NO_YANGLIBRARY));
+ CHECK_LOG_CTX("Invalid argument option (ly_ctx_set_options()).", NULL);
+ assert_int_equal(LY_EINVAL, ly_ctx_set_options(UTEST_LYCTX, LY_CTX_NO_YANGLIBRARY));
+ CHECK_LOG_CTX("Invalid argument option (ly_ctx_set_options()).", NULL);
+
+ /* 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);
+
+ 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);
+
+ 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, NULL);
+
+ /* invalid arguments */
+ assert_int_equal(0, ly_ctx_get_module_set_id(NULL));
+ CHECK_LOG("Invalid argument ctx (ly_ctx_get_module_set_id()).", NULL);
+
+ assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_DISABLE_SEARCHDIRS, &UTEST_LYCTX));
+ assert_int_equal(UTEST_LYCTX->module_set_id, ly_ctx_get_module_set_id(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_create_module(UTEST_LYCTX, in, 4, 1, NULL, NULL, NULL, &unres, &mod1));
+ lys_compile_unres_glob_erase(UTEST_LYCTX, &unres);
+ ly_in_free(in, 0);
+ CHECK_LOG_CTX("Invalid schema input format.", NULL);
+
+ /* 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_create_module(UTEST_LYCTX, in, LYS_IN_YANG, 1, NULL, NULL, NULL, &unres, &mod1));
+ lys_compile_unres_glob_erase(UTEST_LYCTX, &unres);
+ ly_in_free(in, 0);
+ CHECK_LOG_CTX("Name collision between module and submodule of name \"y\".", "Line number 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_create_module(UTEST_LYCTX, in, LYS_IN_YANG, 1, NULL, NULL, NULL, &unres, &mod1));
+ assert_int_equal(LY_SUCCESS, lys_compile_unres_glob(UTEST_LYCTX, &unres));
+ lys_compile_unres_glob_erase(UTEST_LYCTX, &unres);
+ 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_create_module(UTEST_LYCTX, in, LYS_IN_YANG, 1, NULL, NULL, NULL, &unres, &mod1));
+ lys_compile_unres_glob_erase(UTEST_LYCTX, &unres);
+ ly_in_free(in, 0);
+ CHECK_LOG_CTX("Name collision between module and submodule of name \"y\".", "Line number 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_create_module(UTEST_LYCTX, in, LYS_IN_YANG, 1, NULL, NULL, NULL, &unres, &mod1));
+ lys_compile_unres_glob_revert(UTEST_LYCTX, &unres);
+ lys_compile_unres_glob_erase(UTEST_LYCTX, &unres);
+ ly_in_free(in, 0);
+ CHECK_LOG_CTX("Including \"y\" submodule into \"b\" failed.", NULL,
+ "Name collision between submodules of name \"y\".", "Line number 1.");
+
+ /* selecting correct revision of the submodules */
+ ly_ctx_reset_latests(UTEST_LYCTX);
+ 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_create_module(UTEST_LYCTX, in, LYS_IN_YANG, 0, NULL, NULL, NULL, &unres, &mod2));
+ lys_compile_unres_glob_erase(UTEST_LYCTX, &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_create_module(UTEST_LYCTX, in, LYS_IN_YANG, 0, NULL, NULL, NULL, &unres, &mod1));
+ ly_in_free(in, 0);
+ mod1->implemented = 1;
+ assert_int_equal(LY_SUCCESS, lys_compile(mod1, 0, &unres));
+ assert_int_equal(LY_SUCCESS, lys_compile_unres_glob(UTEST_LYCTX, &unres));
+ lys_compile_unres_glob_erase(UTEST_LYCTX, &unres);
+ assert_non_null(mod1->compiled);
+ assert_non_null(mod1->parsed);
+
+#if 0
+ /* TODO in case we are able to remove the parsed schema, here we will test how it will handle missing import parsed schema */
+
+ 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));
+ /* mod1->parsed is necessary to compile mod2 because of possible groupings, typedefs, ... */
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, NULL, NULL);
+ assert_int_equal(LY_ENOTFOUND, lys_create_module(UTEST_LYCTX, in, LYS_IN_YANG, 1, NULL, NULL, &mod2));
+ /*logbuf_assert("Unable to reload \"w\" module to import it into \"z\", source data not found.");*/
+ CHECK_LOG_CTX("Recompilation of module \"w\" failed.", NULL);
+ assert_null(mod2);
+ ly_in_free(in, 0);
+#endif
+
+ 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_create_module(UTEST_LYCTX, in, LYS_IN_YANG, 1, NULL, NULL, NULL, &unres, &mod2));
+ assert_int_equal(LY_SUCCESS, lys_compile_unres_glob(UTEST_LYCTX, &unres));
+ lys_compile_unres_glob_erase(UTEST_LYCTX, &unres);
+ 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)
+{
+ const struct lys_module *mod1, *mod2, *import;
+
+ /* use own context with extra flags */
+ ly_ctx_destroy(UTEST_LYCTX, NULL);
+
+ /* import callback provides newer revision of module 'a' than present in context, so when importing 'a', the newer revision
+ * from the callback should be loaded into the context and used as an import */
+ assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_DISABLE_SEARCHDIRS, &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_int_equal(1, 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));
+ import = mod2->parsed->imports[0].module;
+ assert_int_equal(2, import->latest_revision);
+ assert_int_equal(0, mod1->latest_revision);
+ assert_ptr_not_equal(mod1, import);
+ assert_string_equal("2019-09-17", import->revision);
+ assert_int_equal(0, import->implemented);
+ assert_non_null(ly_ctx_get_module(UTEST_LYCTX, "a", "2019-09-16"));
+ ly_ctx_destroy(UTEST_LYCTX, NULL);
+
+ /* import callback provides older revision of module 'a' than present in context, so when importing a, the newer revision
+ * already present in the context should be selected and the callback's revision should not be loaded into the context */
+ assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_DISABLE_SEARCHDIRS, &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-18;}",
+ LYS_IN_YANG, &mod1));
+ assert_int_equal(1, 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));
+ import = mod2->parsed->imports[0].module;
+ assert_ptr_equal(mod1, import);
+ assert_int_equal(2, import->latest_revision);
+ assert_int_equal(1, import->implemented);
+ assert_string_equal("2019-09-18", import->revision);
+ assert_null(ly_ctx_get_module(UTEST_LYCTX, "a", "2019-09-17"));
+}
+
+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-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("Invalid argument ctx (ly_ctx_get_module()).", NULL);
+ assert_ptr_equal(NULL, ly_ctx_get_module(UTEST_LYCTX, NULL, NULL));
+ CHECK_LOG_CTX("Invalid argument name (ly_ctx_get_module()).", NULL);
+ assert_ptr_equal(NULL, ly_ctx_get_module_ns(NULL, NULL, NULL));
+ CHECK_LOG("Invalid argument ctx (ly_ctx_get_module_ns()).", NULL);
+ 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);
+ 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_create_module(UTEST_LYCTX, in1, LYS_IN_YANG, 1, NULL, NULL, NULL, &unres, &mod));
+ assert_int_equal(LY_SUCCESS, lys_compile_unres_glob(UTEST_LYCTX, &unres));
+ lys_compile_unres_glob_erase(UTEST_LYCTX, &unres);
+ /* invalid attempts - implementing module of the same name and inserting the same module */
+ assert_int_equal(LY_EDENIED, lys_create_module(UTEST_LYCTX, in2, LYS_IN_YANG, 1, NULL, NULL, NULL, &unres, NULL));
+ CHECK_LOG_CTX("Module \"a@2018-10-23\" is already implemented in the context.", NULL);
+ lys_compile_unres_glob_erase(UTEST_LYCTX, &unres);
+ ly_in_reset(in1);
+ /* it is already there, fine */
+ assert_int_equal(LY_SUCCESS, lys_create_module(UTEST_LYCTX, in1, LYS_IN_YANG, 0, NULL, NULL, NULL, &unres, NULL));
+ /* insert the second module only as imported, not implemented */
+ lys_compile_unres_glob_erase(UTEST_LYCTX, &unres);
+ ly_in_reset(in2);
+ assert_int_equal(LY_SUCCESS, lys_create_module(UTEST_LYCTX, in2, LYS_IN_YANG, 0, NULL, NULL, NULL, &unres, &mod2));
+ lys_compile_unres_glob_erase(UTEST_LYCTX, &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_create_module(UTEST_LYCTX, in0, LYS_IN_YANG, 0, NULL, NULL, NULL, &unres, &mod));
+ lys_compile_unres_glob_erase(UTEST_LYCTX, &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_create_module(UTEST_LYCTX, in1, LYS_IN_YANG, 1, NULL, NULL, NULL, &unres, &mod));
+ CHECK_LOG_CTX("Input data contains submodule which cannot be parsed directly without its main module.", NULL);
+ lys_compile_unres_glob_erase(UTEST_LYCTX, &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(9, index);
+
+ /* cleanup */
+ ly_in_free(in0, 0);
+ ly_in_free(in1, 0);
+ ly_in_free(in2, 0);
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_searchdirs),
+ UTEST(test_options),
+ UTEST(test_models),
+ UTEST(test_imports),
+ UTEST(test_get_models),
+ };
+
+ 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..aa6ac3d
--- /dev/null
+++ b/tests/utests/basic/test_hash_table.c
@@ -0,0 +1,271 @@
+/*
+ * @file 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 "common.h"
+#include "hash_table.h"
+
+struct ht_rec *lyht_get_rec(unsigned char *recs, uint16_t rec_size, uint32_t idx);
+
+static void
+test_invalid_arguments(void **state)
+{
+ assert_int_equal(LY_EINVAL, lydict_insert(NULL, NULL, 0, NULL));
+ CHECK_LOG("Invalid argument ctx (lydict_insert()).", NULL);
+
+ assert_int_equal(LY_EINVAL, lydict_insert_zc(NULL, NULL, NULL));
+ CHECK_LOG("Invalid argument ctx (lydict_insert_zc()).", NULL);
+ assert_int_equal(LY_EINVAL, lydict_insert_zc(UTEST_LYCTX, NULL, NULL));
+ CHECK_LOG_CTX("Invalid argument value (lydict_insert_zc()).", NULL);
+}
+
+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, NULL);
+ UTEST_LYCTX = NULL;
+ CHECK_LOG("String \"test1\" not freed from the dictionary, refcount 1", NULL);
+
+#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 **state)
+{
+ uint32_t i;
+ struct hash_table *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("Invalid argument hash (lyht_remove_with_resize_cb()).", NULL);
+
+ lyht_free(ht);
+}
+
+static void
+test_ht_resize(void **state)
+{
+ uint32_t i;
+ struct ht_rec *rec;
+ struct hash_table *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 */
+ rec = lyht_get_rec(ht->recs, ht->rec_size, i);
+ assert_int_equal(1, rec->hits);
+ assert_int_equal(i, rec->hash);
+ } else {
+ /* nothing otherwise */
+ rec = lyht_get_rec(ht->recs, ht->rec_size, i);
+ assert_int_equal(0, rec->hits);
+ }
+ }
+
+ /* removing not present data should fail */
+ for (i = 0; i < 2; ++i) {
+ UTEST_LOG_CLEAN;
+ assert_int_equal(LY_ENOTFOUND, lyht_remove(ht, &i, i));
+ CHECK_LOG("Invalid argument hash (lyht_remove_with_resize_cb()).", NULL);
+ }
+ /* 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);
+}
+
+static void
+test_ht_collisions(void **UNUSED(state))
+{
+#define GET_REC_INT(rec) (*((uint32_t *)&(rec)->val))
+
+ uint32_t i;
+ struct ht_rec *rec;
+ struct hash_table *ht;
+
+ 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 < 2; ++i) {
+ rec = lyht_get_rec(ht->recs, ht->rec_size, i);
+ assert_int_equal(rec->hits, 0);
+ }
+ rec = lyht_get_rec(ht->recs, ht->rec_size, i);
+ assert_int_equal(rec->hits, 4);
+ assert_int_equal(GET_REC_INT(rec), i);
+ ++i;
+ for ( ; i < 6; ++i) {
+ rec = lyht_get_rec(ht->recs, ht->rec_size, i);
+ assert_int_equal(rec->hits, 1);
+ assert_int_equal(GET_REC_INT(rec), i);
+ }
+ for ( ; i < 8; ++i) {
+ rec = lyht_get_rec(ht->recs, ht->rec_size, i);
+ assert_int_equal(rec->hits, 0);
+ }
+
+ i = 4;
+ assert_int_equal(lyht_remove(ht, &i, 2), 0);
+
+ rec = lyht_get_rec(ht->recs, ht->rec_size, i);
+ assert_int_equal(rec->hits, -1);
+
+ i = 2;
+ assert_int_equal(lyht_remove(ht, &i, 2), 0);
+
+ /* check all records */
+ for (i = 0; i < 2; ++i) {
+ rec = lyht_get_rec(ht->recs, ht->rec_size, i);
+ assert_int_equal(rec->hits, 0);
+ }
+ rec = lyht_get_rec(ht->recs, ht->rec_size, i);
+ assert_int_equal(rec->hits, 2);
+ assert_int_equal(GET_REC_INT(rec), 5);
+ ++i;
+ rec = lyht_get_rec(ht->recs, ht->rec_size, i);
+ assert_int_equal(rec->hits, 1);
+ assert_int_equal(GET_REC_INT(rec), 3);
+ ++i;
+ for ( ; i < 6; ++i) {
+ rec = lyht_get_rec(ht->recs, ht->rec_size, i);
+ assert_int_equal(rec->hits, -1);
+ }
+ for ( ; i < 8; ++i) {
+ rec = lyht_get_rec(ht->recs, ht->rec_size, i);
+ assert_int_equal(rec->hits, 0);
+ }
+
+ for (i = 0; i < 3; ++i) {
+ assert_int_equal(lyht_find(ht, &i, 2, NULL), LY_ENOTFOUND);
+ }
+ assert_int_equal(lyht_find(ht, &i, 2, NULL), LY_SUCCESS);
+ ++i;
+ assert_int_equal(lyht_find(ht, &i, 2, NULL), LY_ENOTFOUND);
+ ++i;
+ assert_int_equal(lyht_find(ht, &i, 2, NULL), LY_SUCCESS);
+ ++i;
+ for ( ; i < 8; ++i) {
+ 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 < 2; ++i) {
+ rec = lyht_get_rec(ht->recs, ht->rec_size, i);
+ assert_int_equal(rec->hits, 0);
+ }
+ for ( ; i < 6; ++i) {
+ rec = lyht_get_rec(ht->recs, ht->rec_size, i);
+ assert_int_equal(rec->hits, -1);
+ }
+ for ( ; i < 8; ++i) {
+ rec = lyht_get_rec(ht->recs, ht->rec_size, i);
+ assert_int_equal(rec->hits, 0);
+ }
+
+ lyht_free(ht);
+}
+
+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..f0f7968
--- /dev/null
+++ b/tests/utests/basic/test_inout.c
@@ -0,0 +1,339 @@
+/**
+ * @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 "common.h"
+#include "in.h"
+#include "log.h"
+#include "out.h"
+
+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));
+ assert_int_equal(LY_EINVAL, ly_in_new_memory(str1, NULL));
+ assert_null(ly_in_memory(NULL, NULL));
+
+ 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));
+ assert_int_equal(-1, ly_in_fd(NULL, -1));
+
+ assert_int_not_equal(-1, fd1 = open(__FILE__, O_RDONLY));
+ assert_int_not_equal(-1, fd2 = open(__FILE__, O_RDONLY));
+
+ assert_int_equal(LY_EINVAL, ly_in_new_fd(fd1, NULL));
+
+ 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);
+ /* but fd2 was closed by ly_in_free() */
+ errno = 0;
+ assert_int_equal(-1, fstat(fd2, &statbuf));
+ assert_int_equal(errno, EBADF);
+}
+
+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_int_not_equal(-1, f1 = fopen(__FILE__, "r"));
+ assert_int_not_equal(-1, f2 = fopen(__FILE__, "r"));
+
+ 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 = __FILE__, *path2 = __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};
+ const char *filepath = "/tmp/libyang_test_output";
+
+ assert_int_not_equal(-1, fd1 = open(filepath, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR));
+ assert_int_not_equal(-1, fd2 = open(filepath, 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(filepath, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR));
+ assert_int_not_equal(-1, fd2 = open(filepath, 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};
+ const char *filepath = "/tmp/libyang_test_output";
+
+ assert_int_not_equal(-1, f1 = fopen(filepath, "w"));
+ assert_int_not_equal(-1, f2 = fopen(filepath, "w"));
+
+ /* 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_int_not_equal(-1, f1 = fopen(filepath, "w"));
+ assert_int_not_equal(-1, f2 = fopen(filepath, "r"));
+
+ 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 = "/tmp/libyang_test_output";
+ const char *fp2 = "/tmp/libyang_test_output2";
+
+ /* 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_int_not_equal(-1, f1 = fopen(fp1, "r"));
+
+ 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 void
+test_output_clb(void **UNUSED(state))
+{
+ struct ly_out *out = NULL;
+ int fd1, fd2;
+ char buf[31] = {0};
+ const char *filepath = "/tmp/libyang_test_output";
+
+ assert_int_not_equal(-1, fd1 = open(filepath, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR));
+ assert_int_not_equal(-1, fd2 = open(filepath, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR));
+
+ /* manipulate with the handler */
+ assert_int_equal(LY_SUCCESS, ly_out_new_clb((void *)write, (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, ly_out_clb(out, (void *)write));
+ ly_out_free(out, NULL, 0);
+ assert_int_equal(0, close(fd2));
+ assert_int_equal(LY_SUCCESS, ly_out_new_clb((void *)write, (void *)(intptr_t)fd1, &out));
+ ly_out_free(out, (void *)close, 0);
+
+ /* writing data */
+ assert_int_not_equal(-1, fd1 = open(filepath, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR));
+ assert_int_not_equal(-1, fd2 = open(filepath, 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_clb((void *)write, (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, (void *)close, 0);
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_input_mem),
+ UTEST(test_input_fd),
+ UTEST(test_input_file),
+ UTEST(test_input_filepath),
+ UTEST(test_output_mem),
+ UTEST(test_output_fd),
+ UTEST(test_output_file),
+ UTEST(test_output_filepath),
+ UTEST(test_output_clb),
+ };
+
+ 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..cf47400
--- /dev/null
+++ b/tests/utests/basic/test_json.c
@@ -0,0 +1,483 @@
+/*
+ * @file 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"
+#include "utests.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_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx, 0));
+ lyjson_ctx_free(jsonctx);
+
+ str = " \n\t \n";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx, 0));
+ lyjson_ctx_free(jsonctx);
+
+ /* 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, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx, 0));
+ 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, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx, 0));
+ 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, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx, 0));
+ 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, 0));
+ 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, 0));
+ 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, 0));
+ assert_string_equal("-122.3959", jsonctx->value);
+ assert_int_equal(9, jsonctx->value_len);
+ assert_int_equal(0, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ /* exp number */
+ str = "1E10";
+ 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, 0));
+ assert_string_equal("10000000000", jsonctx->value);
+ assert_int_equal(11, 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, 0));
+ 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-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, 0));
+ assert_string_equal("0.015", jsonctx->value);
+ assert_int_equal(5, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ /* exp fraction number */
+ str = "1.1e3";
+ 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, 0));
+ assert_string_equal("1100", jsonctx->value);
+ assert_int_equal(4, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ /* negative exp fraction number */
+ str = "1.1e-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, 0));
+ assert_string_equal("0.0011", jsonctx->value);
+ assert_int_equal(6, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ /* exp negative fraction number */
+ str = "-0.11e3";
+ 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, 0));
+ assert_string_equal("-110", jsonctx->value);
+ assert_int_equal(4, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ /* negative exp negative fraction number */
+ str = "-3.14e-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, 0));
+ assert_string_equal("-0.00314", jsonctx->value);
+ assert_int_equal(8, 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\").", "Line number 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.", "Line number 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 (\"-\").", "Line number 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.", "Line number 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\").", "Line number 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.", "Line number 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\").", "Line number 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.", "Line number 1.");
+
+ ly_in_free(in, 0);
+}
+
+static void
+test_string(void **state)
+{
+ struct lyjson_ctx *jsonctx;
+ struct ly_in *in;
+ const char *str;
+
+ /* simple string */
+ str = "\"hello\"";
+ 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_STRING, lyjson_ctx_status(jsonctx, 0));
+ assert_ptr_equal(&str[1], jsonctx->value);
+ assert_int_equal(5, jsonctx->value_len);
+ assert_int_equal(0, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ /* 4-byte utf8 character */
+ str = "\"\\t๐ \"";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_STRING, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("\t๐ ", jsonctx->value);
+ assert_int_equal(5, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ /* valid escape sequences - note that here it mixes valid JSON string characters (RFC 7159, sec. 7) and
+ * valid characters in YANG string type (RFC 7950, sec. 9.4). Since the latter is a subset of JSON string,
+ * the YANG string type's restrictions apply to the JSON escape sequences */
+ str = "\"\\\" \\\\ \\r \\/ \\n \\t \\u20ac\"";
+ assert_non_null(ly_in_memory(in, str));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_STRING, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("\" \\ \r / \n \t €", jsonctx->value);
+ assert_int_equal(15, jsonctx->value_len);
+ assert_int_equal(1, jsonctx->dynamic);
+ lyjson_ctx_free(jsonctx);
+
+ /* backspace and form feed are valid JSON escape sequences, but the control characters they represents are not allowed values for YANG string type */
+ str = "\"\\b\"";
+ 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 reference \"\\b\" (0x00000008).", "Line number 1.");
+
+ str = "\"\\f\"";
+ 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 reference \"\\f\" (0x0000000c).", "Line number 1.");
+
+ /* 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.", "Line number 1.");
+
+ /* invalid escape sequence */
+ str = "\"char \\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 escape sequence \\x.", "Line number 1.");
+
+ /* new line is allowed only as escaped character in JSON */
+ str = "\"\n\"";
+ 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 string \"\n\" (0x0000000a).", "Line number 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_EMPTY, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx, 0));
+ 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, 0));
+ assert_ptr_equal(&str[2], jsonctx->value);
+ assert_int_equal(4, jsonctx->value_len);
+ assert_int_equal(0, jsonctx->dynamic);
+ assert_string_equal("\"Radek\"}", jsonctx->in->current);
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_STRING, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("Radek\"}", jsonctx->value);
+ assert_int_equal(5, jsonctx->value_len);
+ assert_int_equal(0, jsonctx->dynamic);
+ assert_string_equal("}", jsonctx->in->current);
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_OBJECT_CLOSED, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx, 0));
+ 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, 0));
+ assert_string_equal("smart\" : true,\"handsom\":false}", jsonctx->value);
+ assert_int_equal(5, jsonctx->value_len);
+ assert_int_equal(0, jsonctx->dynamic);
+ assert_string_equal("true,\"handsom\":false}", jsonctx->in->current);
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_TRUE, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal(",\"handsom\":false}", jsonctx->in->current);
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_OBJECT, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("handsom\":false}", jsonctx->value);
+ assert_int_equal(7, jsonctx->value_len);
+ assert_int_equal(0, jsonctx->dynamic);
+ assert_string_equal("false}", jsonctx->in->current);
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_FALSE, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("}", jsonctx->in->current);
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_OBJECT_CLOSED, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx, 0));
+ 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, 0));
+ assert_string_equal("person\" : {\"name\":\"Radek\"}}", jsonctx->value);
+ assert_int_equal(6, jsonctx->value_len);
+ assert_int_equal(0, jsonctx->dynamic);
+ assert_string_equal("{\"name\":\"Radek\"}}", jsonctx->in->current);
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_OBJECT, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("name\":\"Radek\"}}", jsonctx->value);
+ assert_int_equal(4, jsonctx->value_len);
+ assert_int_equal(0, jsonctx->dynamic);
+ assert_string_equal("\"Radek\"}}", jsonctx->in->current);
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_STRING, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("Radek\"}}", jsonctx->value);
+ assert_int_equal(5, jsonctx->value_len);
+ assert_int_equal(0, jsonctx->dynamic);
+ assert_string_equal("}}", jsonctx->in->current);
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_OBJECT_CLOSED, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("}", jsonctx->in->current);
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_OBJECT_CLOSED, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx, 0));
+ lyjson_ctx_free(jsonctx);
+
+ /* new line is allowed only as escaped character in JSON */
+ str = "{ unquoted : \"data\"}";
+ 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 \"unquoted : \"data\"}\", expected a JSON object's member.", "Line number 1.");
+
+ 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_EMPTY, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx, 0));
+ 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, 0));
+ assert_null(jsonctx->value);
+ assert_int_equal(0, jsonctx->value_len);
+ assert_int_equal(0, jsonctx->dynamic);
+ assert_string_equal("null]", jsonctx->in->current);
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_NULL, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("]", jsonctx->in->current);
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_ARRAY_CLOSED, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx, 0));
+ 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, 0));
+ assert_null(jsonctx->value);
+ assert_int_equal(0, jsonctx->value_len);
+ assert_int_equal(0, jsonctx->dynamic);
+ assert_string_equal("{\"a\":null},\"x\"]", jsonctx->in->current);
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_OBJECT, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("a\":null},\"x\"]", jsonctx->value);
+ assert_int_equal(1, jsonctx->value_len);
+ assert_int_equal(0, jsonctx->dynamic);
+ assert_string_equal("null},\"x\"]", jsonctx->in->current);
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_NULL, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("},\"x\"]", jsonctx->in->current);
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_OBJECT_CLOSED, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal(",\"x\"]", jsonctx->in->current);
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_STRING, lyjson_ctx_status(jsonctx, 0));
+ assert_string_equal("x\"]", jsonctx->value);
+ assert_int_equal(1, jsonctx->value_len);
+ assert_int_equal(0, jsonctx->dynamic);
+ assert_string_equal("]", jsonctx->in->current);
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_ARRAY_CLOSED, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx, 0));
+ 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.", "Line number 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_set.c b/tests/utests/basic/test_set.c
new file mode 100644
index 0000000..c4ef521
--- /dev/null
+++ b/tests/utests/basic/test_set.c
@@ -0,0 +1,287 @@
+/*
+ * @file 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_not_equal(0, 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 **state)
+{
+ struct ly_set set;
+
+ memset(&set, 0, sizeof set);
+
+ ly_set_clean(NULL, NULL);
+ CHECK_LOG(NULL, NULL);
+
+ ly_set_erase(NULL, NULL);
+ CHECK_LOG(NULL, NULL);
+
+ ly_set_free(NULL, NULL);
+ CHECK_LOG(NULL, NULL);
+
+ assert_int_equal(LY_EINVAL, ly_set_dup(NULL, NULL, NULL));
+ CHECK_LOG("Invalid argument set (ly_set_dup()).", NULL);
+
+ assert_int_equal(LY_EINVAL, ly_set_add(NULL, NULL, 0, NULL));
+ CHECK_LOG("Invalid argument set (ly_set_add()).", NULL);
+
+ assert_int_equal(LY_EINVAL, ly_set_merge(NULL, NULL, 0, NULL));
+ CHECK_LOG("Invalid argument trg (ly_set_merge()).", NULL);
+ 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("Invalid argument set (ly_set_rm_index()).", NULL);
+ assert_int_equal(LY_EINVAL, ly_set_rm_index(&set, 1, NULL));
+ CHECK_LOG("Invalid argument index (ly_set_rm_index()).", NULL);
+
+ assert_int_equal(LY_EINVAL, ly_set_rm(NULL, NULL, NULL));
+ CHECK_LOG("Invalid argument set (ly_set_rm()).", NULL);
+ assert_int_equal(LY_EINVAL, ly_set_rm(&set, NULL, NULL));
+ CHECK_LOG("Invalid argument object (ly_set_rm()).", NULL);
+ assert_int_equal(LY_EINVAL, ly_set_rm(&set, &set, NULL));
+ CHECK_LOG("Invalid argument object (ly_set_rm()).", NULL);
+}
+
+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 *(*)(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 *(*)(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..abf0bf1
--- /dev/null
+++ b/tests/utests/basic/test_xml.c
@@ -0,0 +1,651 @@
+/*
+ * @file 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);
+LY_ERR lyxml_ns_rm(struct lyxml_ctx *xmlctx);
+
+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\").", "Line number 1.");
+ ly_in_free(in, 0);
+
+ /* no element */
+ UTEST_LOG_CLEAN;
+ 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 ('<').", "Line number 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.", "Line number 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/>\".", "Line number 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 --> <![CDATA[<greeting>Hello, world!</greeting>]]> <?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.", "Line number 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 ('>').", "Line number 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.", "Line number 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.", "Line number 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.", "Line number 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 '='.", "Line number 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.", "Line number 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.", "Line number 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->name, xmlctx->name_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;
+ 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;
+ 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;
+ xmlctx->status = LYXML_ELEM_CONTENT;
+ assert_int_equal(LY_EVALID, lyxml_ctx_next(xmlctx));
+ CHECK_LOG_CTX("Unexpected end-of-input.", "Line number 2.");
+ ly_in_free(in, 0);
+
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory("xxx", &in));
+ xmlctx->in = 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 ('<').", "Line number 2.");
+ ly_in_free(in, 0);
+
+ lyxml_ctx_free(xmlctx);
+
+ /* valid strings */
+ str = "<a>€๐ Øn \n<&"'> ROK</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("=\'$¢€𐍈\'", &in));
+ xmlctx->in = 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);
+ free((char *)xmlctx->value);
+ ly_in_free(in, 0);
+
+ /* invalid characters in string */
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory("=\'R\'", &in));
+ xmlctx->in = in;
+ xmlctx->status = LYXML_ATTRIBUTE;
+ assert_int_equal(LY_EVALID, lyxml_ctx_next(xmlctx));
+ CHECK_LOG_CTX("Invalid character sequence \"'\", expected ;.", "Line number 2.");
+ ly_in_free(in, 0);
+
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory("=\"R\"", &in));
+ xmlctx->in = in;
+ xmlctx->status = LYXML_ATTRIBUTE;
+ assert_int_equal(LY_EVALID, lyxml_ctx_next(xmlctx));
+ CHECK_LOG_CTX("Invalid character sequence \"\"\", expected ;.", "Line number 2.");
+ ly_in_free(in, 0);
+
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory("=\"&nonsense;\"", &in));
+ xmlctx->in = 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.", "Line number 2.");
+ ly_in_free(in, 0);
+
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(">&#o122;", &in));
+ xmlctx->in = in;
+ xmlctx->status = LYXML_ELEMENT;
+ assert_int_equal(LY_EVALID, lyxml_ctx_next(xmlctx));
+ CHECK_LOG_CTX("Invalid character reference \"&#o122;\".", "Line number 2.");
+ ly_in_free(in, 0);
+
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory("=\'\'", &in));
+ xmlctx->in = in;
+ xmlctx->status = LYXML_ATTRIBUTE;
+ assert_int_equal(LY_EVALID, lyxml_ctx_next(xmlctx));
+ CHECK_LOG_CTX("Invalid character reference \"\'\" (0x00000006).", "Line number 2.");
+ ly_in_free(in, 0);
+
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory("=\'\'", &in));
+ xmlctx->in = in;
+ xmlctx->status = LYXML_ATTRIBUTE;
+ assert_int_equal(LY_EVALID, lyxml_ctx_next(xmlctx));
+ CHECK_LOG_CTX("Invalid character reference \"\'\" (0x0000fdd0).", "Line number 2.");
+ ly_in_free(in, 0);
+
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory("=\'\'", &in));
+ xmlctx->in = in;
+ xmlctx->status = LYXML_ATTRIBUTE;
+ assert_int_equal(LY_EVALID, lyxml_ctx_next(xmlctx));
+ CHECK_LOG_CTX("Invalid character reference \"\'\" (0x0000ffff).", "Line number 2.");
+ ly_in_free(in, 0);
+
+ lyxml_ctx_free(xmlctx);
+}
+
+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..712b854
--- /dev/null
+++ b/tests/utests/basic/test_xpath.c
@@ -0,0 +1,351 @@
+/*
+ * @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"
+ " 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"
+ " 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"
+ "}";
+
+static int
+setup(void **state)
+{
+ UTEST_SETUP;
+
+ UTEST_ADD_MODULE(schema_a, LYS_IN_YANG, NULL, NULL);
+
+ return 0;
+}
+
+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_CANON_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_CANON_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_CANON_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_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(mod->compiled->data, "/a:*", 0, &set));
+ assert_int_equal(4, 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(mod->compiled->data, "//.", 0, &set));
+ assert_in_range(set->count, 16, UINT32_MAX);
+
+ ly_set_free(set, NULL);
+
+ assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(mod->compiled->data->next->next, "/a:c/ll[a='val1']/ll[a='val2']/b",
+ 0, &set));
+ assert_int_equal(7, set->count);
+
+ ly_set_free(set, NULL);
+}
+
+static void
+test_canonize(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_SUCCESS, lyd_find_xpath(tree, "/a:foo2[.='050']", &set));
+ assert_int_equal(1, set->count);
+ ly_set_free(set, NULL);
+
+ /* TODO more use-cases once there are some type plugins that have canonical values */
+
+ lyd_free_all(tree);
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_hash, setup),
+ UTEST(test_toplevel, setup),
+ UTEST(test_atomize, setup),
+ UTEST(test_canonize, 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..563fbb3
--- /dev/null
+++ b/tests/utests/basic/test_yanglib.c
@@ -0,0 +1,138 @@
+/**
+ * @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));
+
+ /* 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);
+}