diff options
Diffstat (limited to 'tests/utests/basic')
-rw-r--r-- | tests/utests/basic/test_common.c | 416 | ||||
-rw-r--r-- | tests/utests/basic/test_context.c | 1087 | ||||
-rw-r--r-- | tests/utests/basic/test_hash_table.c | 271 | ||||
-rw-r--r-- | tests/utests/basic/test_inout.c | 401 | ||||
-rw-r--r-- | tests/utests/basic/test_json.c | 794 | ||||
-rw-r--r-- | tests/utests/basic/test_plugins.c | 109 | ||||
-rw-r--r-- | tests/utests/basic/test_set.c | 287 | ||||
-rw-r--r-- | tests/utests/basic/test_xml.c | 690 | ||||
-rw-r--r-- | tests/utests/basic/test_xpath.c | 1071 | ||||
-rw-r--r-- | tests/utests/basic/test_yanglib.c | 144 |
10 files changed, 5270 insertions, 0 deletions
diff --git a/tests/utests/basic/test_common.c b/tests/utests/basic/test_common.c new file mode 100644 index 0000000..75235a2 --- /dev/null +++ b/tests/utests/basic/test_common.c @@ -0,0 +1,416 @@ +/* + * @file test_common.c + * @author: Radek Krejci <rkrejci@cesnet.cz> + * @brief unit tests for functions from common.c + * + * Copyright (c) 2018 CESNET, z.s.p.o. + * + * This source code is licensed under BSD 3-Clause License (the "License"). + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + */ +#define _UTEST_MAIN_ +#include "utests.h" + +#include "common.h" + +static void +test_utf8(void **UNUSED(state)) +{ + char buf[5] = {0}; + const char *str = buf; + unsigned int c; + size_t len; + + /* test invalid UTF-8 characters in lyxml_getutf8 + * - https://en.wikipedia.org/wiki/UTF-8 */ + buf[0] = (char)0x04; + assert_int_equal(LY_EINVAL, ly_getutf8(&str, &c, &len)); + buf[0] = (char)0x80; + assert_int_equal(LY_EINVAL, ly_getutf8(&str, &c, &len)); + + buf[0] = (char)0xc0; + buf[1] = (char)0x00; + assert_int_equal(LY_EINVAL, ly_getutf8(&str, &c, &len)); + buf[1] = (char)0x80; + assert_int_equal(LY_EINVAL, ly_getutf8(&str, &c, &len)); + + buf[0] = (char)0xe0; + buf[1] = (char)0x00; + buf[2] = (char)0x80; + assert_int_equal(LY_EINVAL, ly_getutf8(&str, &c, &len)); + buf[1] = (char)0x80; + assert_int_equal(LY_EINVAL, ly_getutf8(&str, &c, &len)); + + buf[0] = (char)0xf0; + buf[1] = (char)0x00; + buf[2] = (char)0x80; + buf[3] = (char)0x80; + assert_int_equal(LY_EINVAL, ly_getutf8(&str, &c, &len)); + buf[1] = (char)0x80; + assert_int_equal(LY_EINVAL, ly_getutf8(&str, &c, &len)); +} + +static void +test_parse_int(void **UNUSED(state)) +{ + const char *str; + int64_t i = 500; + + str = "10"; + assert_int_equal(LY_SUCCESS, ly_parse_int(str, strlen(str), -10, 10, 10, &i)); + assert_int_equal(i, 10); + + /* leading zeros are allowed, trailing whitespaces are allowed */ + str = "000\n\t "; + assert_int_equal(LY_SUCCESS, ly_parse_int(str, strlen(str), -10, 10, 10, &i)); + assert_int_equal(i, 0); + + /* negative value */ + str = "-10"; + assert_int_equal(LY_SUCCESS, ly_parse_int(str, strlen(str), -10, 10, 10, &i)); + assert_int_equal(i, -10); + + /* non-NULL terminated string */ + str = "+5sometext"; + assert_int_equal(LY_SUCCESS, ly_parse_int(str, 2, -10, 10, 10, &i)); + assert_int_equal(i, 5); + + /* out of bounds value */ + str = "11"; + assert_int_equal(LY_EDENIED, ly_parse_int(str, strlen(str), -10, 10, 10, &i)); + str = "-11"; + assert_int_equal(LY_EDENIED, ly_parse_int(str, strlen(str), -10, 10, 10, &i)); + + /* NaN */ + str = "zero"; + assert_int_equal(LY_EVALID, ly_parse_int(str, strlen(str), -10, 10, 10, &i)); + + /* mixing number with text */ + str = "10zero"; + assert_int_equal(LY_EVALID, ly_parse_int(str, strlen(str), -10, 10, 10, &i)); + + str = "10 zero"; + assert_int_equal(LY_EVALID, ly_parse_int(str, strlen(str), -10, 10, 10, &i)); +} + +static void +test_parse_uint(void **UNUSED(state)) +{ + const char *str; + uint64_t u = 500; + + str = "10"; + assert_int_equal(LY_SUCCESS, ly_parse_uint(str, strlen(str), 10, 10, &u)); + assert_int_equal(u, 10); + + /* leading zeros are allowed, trailing whitespaces are allowed */ + str = "000\n\t "; + assert_int_equal(LY_SUCCESS, ly_parse_uint(str, strlen(str), 10, 10, &u)); + assert_int_equal(u, 0); + /* non-NULL terminated string */ + str = "+5sometext"; + assert_int_equal(LY_SUCCESS, ly_parse_uint(str, 2, 10, 10, &u)); + assert_int_equal(u, 5); + + /* out of bounds value */ + str = "11"; + assert_int_equal(LY_EDENIED, ly_parse_uint(str, strlen(str), 10, 10, &u)); + str = "-1"; + assert_int_equal(LY_EDENIED, ly_parse_uint(str, strlen(str), (uint64_t)-1, 10, &u)); + + /* NaN */ + str = "zero"; + assert_int_equal(LY_EVALID, ly_parse_uint(str, strlen(str), 10, 10, &u)); + + /* mixing number with text */ + str = "10zero"; + assert_int_equal(LY_EVALID, ly_parse_uint(str, strlen(str), 10, 10, &u)); + + str = "10 zero"; + assert_int_equal(LY_EVALID, ly_parse_uint(str, strlen(str), 10, 10, &u)); +} + +static void +test_parse_nodeid(void **UNUSED(state)) +{ + const char *str; + const char *prefix, *name; + size_t prefix_len, name_len; + + str = "123"; + assert_int_equal(LY_EINVAL, ly_parse_nodeid(&str, &prefix, &prefix_len, &name, &name_len)); + + str = "a12_-.!"; + assert_int_equal(LY_SUCCESS, ly_parse_nodeid(&str, &prefix, &prefix_len, &name, &name_len)); + assert_null(prefix); + assert_int_equal(0, prefix_len); + assert_non_null(name); + assert_int_equal(6, name_len); + assert_int_equal(0, strncmp("a12_-.", name, name_len)); + assert_string_equal("!", str); + + str = "a12_-.:_b2 xxx"; + assert_int_equal(LY_SUCCESS, ly_parse_nodeid(&str, &prefix, &prefix_len, &name, &name_len)); + assert_non_null(prefix); + assert_int_equal(6, prefix_len); + assert_int_equal(0, strncmp("a12_-.", prefix, prefix_len)); + assert_non_null(name); + assert_int_equal(3, name_len); + assert_int_equal(0, strncmp("_b2", name, name_len)); + assert_string_equal(" xxx", str); +} + +static void +test_parse_instance_predicate(void **UNUSED(state)) +{ + const char *str, *errmsg; + const char *prefix, *id, *value; + size_t prefix_len, id_len, value_len; + + str = "[ex:name='fred']"; + assert_int_equal(LY_SUCCESS, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg)); + assert_string_equal(str, ""); + assert_string_equal(prefix, "ex:name='fred']"); + assert_int_equal(prefix_len, 2); + assert_string_equal(id, "name='fred']"); + assert_int_equal(id_len, 4); + assert_string_equal(value, "fred']"); + assert_int_equal(value_len, 4); + + str = "[ex:ip = \"[192.0.2.1]\"][ex:port='80']"; + assert_int_equal(LY_SUCCESS, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg)); + assert_string_equal(str, "[ex:port='80']"); + assert_string_equal(prefix, "ex:ip = \"[192.0.2.1]\"][ex:port='80']"); + assert_int_equal(prefix_len, 2); + assert_string_equal(id, "ip = \"[192.0.2.1]\"][ex:port='80']"); + assert_int_equal(id_len, 2); + assert_string_equal(value, "[192.0.2.1]\"][ex:port='80']"); + assert_int_equal(value_len, 11); + + str = "[. = 'blowfish-cbc']"; + assert_int_equal(LY_SUCCESS, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg)); + assert_string_equal(str, ""); + assert_null(prefix); + assert_int_equal(prefix_len, 0); + assert_string_equal(id, ". = 'blowfish-cbc']"); + assert_int_equal(id_len, 1); + assert_string_equal(value, "blowfish-cbc']"); + assert_int_equal(value_len, 12); + + str = "[ 3 ]"; + assert_int_equal(LY_SUCCESS, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg)); + assert_string_equal(str, ""); + assert_null(prefix); + assert_int_equal(prefix_len, 0); + assert_null(id); + assert_int_equal(id_len, 0); + assert_string_equal(value, "3 ]"); + assert_int_equal(value_len, 1); + + /* invalid predicates */ + /* position must be positive integer */ + str = "[0]"; + assert_int_equal(LY_EVALID, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg)); + assert_string_equal(errmsg, "The position predicate cannot be zero."); + str = "[-1]"; + assert_int_equal(LY_EVALID, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg)); + assert_string_equal(errmsg, "Invalid instance predicate format (negative position or invalid node-identifier)."); + + /* invalid node-identifier */ + str = "[$node='value']"; + assert_int_equal(LY_EVALID, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg)); + assert_string_equal(errmsg, "Invalid node-identifier."); + str = "[.node='value']"; + assert_int_equal(LY_EVALID, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg)); + assert_string_equal(errmsg, "Unexpected character instead of '=' in leaf-list-predicate."); + str = "[13node='value']"; + assert_int_equal(LY_EVALID, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg)); + assert_string_equal(errmsg, "Predicate (pos) is not terminated by \']\' character."); + + str = "[ex:node]"; + assert_int_equal(LY_EVALID, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg)); + assert_string_equal(errmsg, "Unexpected character instead of '=' in key-predicate."); + + str = "[ex:node= value]"; + assert_int_equal(LY_EVALID, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg)); + assert_string_equal(errmsg, "String value is not quoted."); + + str = "[ex:node='value\"]"; + assert_int_equal(LY_EVALID, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg)); + assert_string_equal(errmsg, "Value is not terminated quoted-string."); + + str = "[ex:node='value ]"; + assert_int_equal(LY_EVALID, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg)); + assert_string_equal(errmsg, "Value is not terminated quoted-string."); + + str = "[ex:node=\"value\"[3]"; + assert_int_equal(LY_EVALID, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg)); + assert_string_equal(errmsg, "Predicate (key-predicate) is not terminated by \']\' character."); + str = "[.=\"value\"[3]"; + assert_int_equal(LY_EVALID, ly_parse_instance_predicate(&str, strlen(str), LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg)); + assert_string_equal(errmsg, "Predicate (leaf-list-predicate) is not terminated by \']\' character."); + + /* the limit of the string is too short, it ends one character earlier */ + str = "[ex:node='value']"; + assert_int_equal(LY_EINVAL, ly_parse_instance_predicate(&str, strlen(str) - 1, LYD_XML, &prefix, &prefix_len, &id, &id_len, &value, &value_len, &errmsg)); + assert_string_equal(errmsg, "Predicate is incomplete."); +} + +static void +test_value_prefix_next(void **UNUSED(state)) +{ + const char *next; + ly_bool is_prefix; + uint32_t bytes; + + assert_int_equal(LY_SUCCESS, ly_value_prefix_next(NULL, NULL, &bytes, &is_prefix, &next)); + assert_int_equal(0, bytes); + assert_int_equal(LY_SUCCESS, ly_value_prefix_next("", NULL, &bytes, &is_prefix, &next)); + assert_int_equal(0, bytes); + + /* prefix */ + next = "pref:"; + assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next)); + assert_int_equal(4, bytes); + assert_null(next); + assert_int_equal(1, is_prefix); + + /* no-prefix */ + next = "node"; + assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next)); + assert_int_equal(4, bytes); + assert_null(next); + assert_int_equal(0, is_prefix); + + /* no-prefix */ + next = "::::"; + assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next)); + assert_int_equal(4, bytes); + assert_null(next); + assert_int_equal(0, is_prefix); + + /* no-prefix */ + next = "//a/:"; + assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next)); + assert_int_equal(5, bytes); + assert_null(next); + assert_int_equal(0, is_prefix); + + /* no-prefix */ + next = "//a//"; + assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next)); + assert_int_equal(5, bytes); + assert_null(next); + assert_int_equal(0, is_prefix); + + /* prefix, prefix */ + next = "pref1:pref2:"; + assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next)); + assert_int_equal(5, bytes); + assert_string_equal(next, "pref2:"); + assert_int_equal(1, is_prefix); + assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next)); + assert_int_equal(5, bytes); + assert_null(next); + assert_int_equal(1, is_prefix); + + /* prefix, no-prefix */ + next = "pref:node"; + assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next)); + assert_int_equal(4, bytes); + assert_string_equal(next, "node"); + assert_int_equal(1, is_prefix); + assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next)); + assert_int_equal(4, bytes); + assert_null(next); + assert_int_equal(0, is_prefix); + + /* no-prefix, prefix */ + next = "/pref:"; + assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next)); + assert_int_equal(1, bytes); + assert_string_equal(next, "pref:"); + assert_int_equal(0, is_prefix); + assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next)); + assert_int_equal(4, bytes); + assert_null(next); + assert_int_equal(1, is_prefix); + + /* no-prefix, prefix */ + next = "//pref:"; + assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next)); + assert_int_equal(2, bytes); + assert_string_equal(next, "pref:"); + assert_int_equal(0, is_prefix); + assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next)); + assert_int_equal(4, bytes); + assert_null(next); + assert_int_equal(1, is_prefix); + + /* no-prefix, prefix, no-prefix */ + next = "/pref:node"; + assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next)); + assert_int_equal(1, bytes); + assert_string_equal(next, "pref:node"); + assert_int_equal(0, is_prefix); + assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next)); + assert_int_equal(4, bytes); + assert_string_equal(next, "node"); + assert_int_equal(1, is_prefix); + assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next)); + assert_int_equal(4, bytes); + assert_null(next); + assert_int_equal(0, is_prefix); + + /* prefix, no-prefix, prefix */ + next = "pref:node pref:"; + assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next)); + assert_int_equal(4, bytes); + assert_string_equal(next, "node pref:"); + assert_int_equal(1, is_prefix); + assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next)); + assert_int_equal(5, bytes); + assert_string_equal(next, "pref:"); + assert_int_equal(0, is_prefix); + assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next)); + assert_int_equal(4, bytes); + assert_null(next); + assert_int_equal(1, is_prefix); + + /* prefix, no-prefix, prefix, no-prefix */ + next = "pref:node /pref:node"; + assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next)); + assert_int_equal(4, bytes); + assert_string_equal(next, "node /pref:node"); + assert_int_equal(1, is_prefix); + assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next)); + assert_int_equal(6, bytes); + assert_string_equal(next, "pref:node"); + assert_int_equal(0, is_prefix); + assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next)); + assert_int_equal(4, bytes); + assert_string_equal(next, "node"); + assert_int_equal(1, is_prefix); + assert_int_equal(LY_SUCCESS, ly_value_prefix_next(next, NULL, &bytes, &is_prefix, &next)); + assert_int_equal(4, bytes); + assert_null(next); + assert_int_equal(0, is_prefix); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_utf8), + UTEST(test_parse_int), + UTEST(test_parse_uint), + UTEST(test_parse_nodeid), + UTEST(test_parse_instance_predicate), + UTEST(test_value_prefix_next), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/basic/test_context.c b/tests/utests/basic/test_context.c new file mode 100644 index 0000000..4c4cc3f --- /dev/null +++ b/tests/utests/basic/test_context.c @@ -0,0 +1,1087 @@ +/* + * @file test_context.c + * @author: Radek Krejci <rkrejci@cesnet.cz> + * @brief unit tests for functions from context.c + * + * Copyright (c) 2018 CESNET, z.s.p.o. + * + * This source code is licensed under BSD 3-Clause License (the "License"). + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + */ +#define _UTEST_MAIN_ +#include "utests.h" + +#include "common.h" +#include "context.h" +#include "in.h" +#include "schema_compile.h" +#include "tests_config.h" +#include "tree_schema_internal.h" +#ifdef _WIN32 +static void +slashes_to_backslashes(char *path) +{ + while ((path = strchr(path, '/'))) { + *path++ = '\\'; + } +} + +static void +test_searchdirs(void **state) +{ + const char * const *list; + char *path1 = strdup(TESTS_BIN "/utests"); + char *path2 = strdup(TESTS_SRC); + + slashes_to_backslashes(path1); + slashes_to_backslashes(path2); + + assert_int_equal(LY_EINVAL, ly_ctx_set_searchdir(NULL, NULL)); + CHECK_LOG("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); + + /* correct path */ + assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, path1)); + assert_int_equal(1, UTEST_LYCTX->search_paths.count); + assert_string_equal(path1, UTEST_LYCTX->search_paths.objs[0]); + + /* duplicated paths */ + assert_int_equal(LY_EEXIST, ly_ctx_set_searchdir(UTEST_LYCTX, path1)); + assert_int_equal(1, UTEST_LYCTX->search_paths.count); + assert_string_equal(path1, UTEST_LYCTX->search_paths.objs[0]); + + /* another path */ + assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, path2)); + assert_int_equal(2, UTEST_LYCTX->search_paths.count); + assert_string_equal(path2, UTEST_LYCTX->search_paths.objs[1]); + + /* get searchpaths */ + list = ly_ctx_get_searchdirs(UTEST_LYCTX); + assert_non_null(list); + assert_string_equal(path1, list[0]); + assert_string_equal(path2, list[1]); + assert_null(list[2]); + + /* removing searchpaths */ + /* nonexisting */ + assert_int_equal(LY_EINVAL, ly_ctx_unset_searchdir(UTEST_LYCTX, "/nonexistingfile")); + CHECK_LOG_CTX("Invalid argument value (ly_ctx_unset_searchdir()).", NULL); + + /* first */ + assert_int_equal(LY_SUCCESS, ly_ctx_unset_searchdir(UTEST_LYCTX, path1)); + assert_int_equal(1, UTEST_LYCTX->search_paths.count); + assert_string_not_equal(path1, list[0]); + + /* second */ + assert_int_equal(LY_SUCCESS, ly_ctx_unset_searchdir(UTEST_LYCTX, path2)); + assert_int_equal(0, UTEST_LYCTX->search_paths.count); + + free(path1); + free(path2); +} + +#else + +static void +test_searchdirs(void **state) +{ + const char * const *list; + + /* invalid arguments */ + assert_int_equal(LY_EINVAL, ly_ctx_set_searchdir(NULL, NULL)); + CHECK_LOG("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 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); + + /* 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 PATH_SEPARATOR TESTS_BIN PATH_SEPARATOR TESTS_BIN PATH_SEPARATOR TESTS_SRC, + LY_CTX_DISABLE_SEARCHDIRS, &UTEST_LYCTX)); + assert_int_equal(2, UTEST_LYCTX->search_paths.count); + assert_string_equal(TESTS_SRC, UTEST_LYCTX->search_paths.objs[0]); + assert_string_equal(TESTS_BIN, UTEST_LYCTX->search_paths.objs[1]); +} + +#endif + +static void +test_options(void **state) +{ + /* use own context with extra flags */ + ly_ctx_destroy(UTEST_LYCTX); + + assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, 0xffff, &UTEST_LYCTX)); + + /* invalid arguments */ + assert_int_equal(0, ly_ctx_get_options(NULL)); + CHECK_LOG("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); + + /* 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); + + /* invalid arguments */ + assert_int_equal(0, ly_ctx_get_change_count(NULL)); + CHECK_LOG("Invalid argument ctx (ly_ctx_get_change_count()).", NULL); + + assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_DISABLE_SEARCHDIRS, &UTEST_LYCTX)); + assert_int_equal(UTEST_LYCTX->change_count, ly_ctx_get_change_count(UTEST_LYCTX)); + + assert_int_equal(LY_SUCCESS, ly_in_new_memory("module x {namespace urn:x;prefix x;}", &in)); + assert_int_equal(LY_EINVAL, lys_parse_in(UTEST_LYCTX, in, 4, NULL, NULL, &unres.creating, &mod1)); + lys_unres_glob_erase(&unres); + ly_in_free(in, 0); + CHECK_LOG_CTX("Invalid schema input format.", NULL); + + /* import callback */ + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, (void *)(str = "test")); + assert_ptr_equal(test_imp_clb, UTEST_LYCTX->imp_clb); + assert_ptr_equal(str, UTEST_LYCTX->imp_clb_data); + assert_ptr_equal(test_imp_clb, ly_ctx_get_module_imp_clb(UTEST_LYCTX, (void **)&str)); + assert_string_equal("test", str); + + ly_ctx_set_module_imp_clb(UTEST_LYCTX, NULL, NULL); + assert_null(UTEST_LYCTX->imp_clb); + assert_null(UTEST_LYCTX->imp_clb_data); + + /* name collision of module and submodule */ + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule y {belongs-to a {prefix a;} revision 2018-10-30;}"); + assert_int_equal(LY_SUCCESS, ly_in_new_memory("module y {namespace urn:y;prefix y;include y;}", &in)); + assert_int_equal(LY_EVALID, lys_parse_in(UTEST_LYCTX, in, LYS_IN_YANG, NULL, NULL, &unres.creating, &mod1)); + lys_unres_glob_erase(&unres); + ly_in_free(in, 0); + CHECK_LOG_CTX("Parsing module \"y\" failed.", NULL, + "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_parse_in(UTEST_LYCTX, in, LYS_IN_YANG, NULL, NULL, &unres.creating, &mod1)); + ly_in_free(in, 0); + assert_int_equal(LY_SUCCESS, ly_in_new_memory("module y {namespace urn:y;prefix y;}", &in)); + assert_int_equal(LY_EVALID, lys_parse_in(UTEST_LYCTX, in, LYS_IN_YANG, NULL, NULL, &unres.creating, &mod1)); + lys_unres_glob_erase(&unres); + ly_in_free(in, 0); + CHECK_LOG_CTX("Parsing module \"y\" failed.", NULL, + "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_parse_in(UTEST_LYCTX, in, LYS_IN_YANG, NULL, NULL, &unres.creating, &mod1)); + lys_unres_glob_revert(UTEST_LYCTX, &unres); + lys_unres_glob_erase(&unres); + ly_in_free(in, 0); + CHECK_LOG_CTX("Parsing module \"b\" failed.", NULL, + "Including \"y\" submodule into \"b\" failed.", NULL, + "Parsing submodule 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_parse_in(UTEST_LYCTX, in, LYS_IN_YANG, NULL, NULL, &unres.creating, &mod2)); + lys_unres_glob_erase(&unres); + ly_in_free(in, 0); + assert_string_equal("2018-10-31", mod2->parsed->includes[0].submodule->revs[0].date); + + /* reloading module in case only the compiled module resists in the context */ + assert_int_equal(LY_SUCCESS, ly_in_new_memory("module w {namespace urn:w;prefix w;revision 2018-10-24;}", &in)); + assert_int_equal(LY_SUCCESS, lys_parse(UTEST_LYCTX, in, LYS_IN_YANG, NULL, &mod1)); + ly_in_free(in, 0); + assert_non_null(mod1->compiled); + assert_non_null(mod1->parsed); + +#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_parse(UTEST_LYCTX, in, LYS_IN_YANG, NULL, &mod2)); + ly_in_free(in, 0); + assert_non_null(mod2); + assert_non_null(mod1->parsed); + assert_string_equal("w", mod1->name); +} + +static void +test_imports(void **state) +{ + struct lys_module *mod1, *mod2, *mod3, *import; + char *str; + uint16_t ctx_options; + + /* use own context with extra flags */ + ly_ctx_destroy(UTEST_LYCTX); + ctx_options = LY_CTX_DISABLE_SEARCHDIRS | LY_CTX_NO_YANGLIBRARY; + + /* Import callback provides newer revision of module 'a', + * however the older revision is implemented soon and therefore it is preferred. */ + assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, ctx_options, &UTEST_LYCTX)); + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module a {namespace urn:a; prefix a; revision 2019-09-17;}"); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {namespace urn:a;prefix a;revision 2019-09-16;}", + LYS_IN_YANG, &mod1)); + assert_true(LYS_MOD_LATEST_REV & mod1->latest_revision); + assert_int_equal(1, mod1->implemented); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {namespace urn:b;prefix b;import a {prefix a;}}", + LYS_IN_YANG, &mod2)); + assert_ptr_equal(mod1, mod2->parsed->imports[0].module); + assert_true((LYS_MOD_LATEST_REV | LYS_MOD_IMPORTED_REV) & mod1->latest_revision); + assert_string_equal("2019-09-16", mod1->revision); + assert_int_equal(1, mod1->implemented); + assert_non_null(ly_ctx_get_module(UTEST_LYCTX, "a", "2019-09-16")); + ly_ctx_destroy(UTEST_LYCTX); + + /* Import callback provides older revision of module 'a' and it is + * imported by another module, so it is preferred even if newer + * revision is implemented later. */ + assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, ctx_options, &UTEST_LYCTX)); + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module a {namespace urn:a; prefix a; revision 2019-09-16;}"); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {namespace urn:b;prefix b;import a {prefix a;}}", + LYS_IN_YANG, &mod2)); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {namespace urn:a;prefix a;revision 2019-09-17;}", + LYS_IN_YANG, &mod1)); + ly_log_level(LY_LLVRB); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module c {namespace urn:c;prefix c;import a {prefix a;}}", + LYS_IN_YANG, &mod3)); + CHECK_LOG("Implemented module \"a@2019-09-17\" is not used for import, revision \"2019-09-16\" is imported instead.", NULL); + ly_log_level(LY_LLWRN); + assert_true(LYS_MOD_LATEST_SEARCHDIRS & mod1->latest_revision); + assert_int_equal(1, mod1->implemented); + import = mod2->parsed->imports[0].module; + assert_true(LYS_MOD_IMPORTED_REV & import->latest_revision); + assert_string_equal("2019-09-16", import->revision); + assert_int_equal(0, import->implemented); + import = mod3->parsed->imports[0].module; + assert_string_equal("2019-09-16", import->revision); + assert_non_null(ly_ctx_get_module(UTEST_LYCTX, "a", "2019-09-16")); + assert_non_null(ly_ctx_get_module(UTEST_LYCTX, "a", "2019-09-17")); + assert_string_equal("2019-09-17", ly_ctx_get_module_implemented(UTEST_LYCTX, "a")->revision); + ly_ctx_destroy(UTEST_LYCTX); + + /* check of circular dependency */ + assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, ctx_options, &UTEST_LYCTX)); + str = "module a {namespace urn:a; prefix a;" + "import b {prefix b;}" + "}"; + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, str); + str = "module b { yang-version 1.1; namespace urn:b; prefix b;" + "import a {prefix a;}" + "}"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL)); +} + +static void +test_get_models(void **state) +{ + struct lys_module *mod, *mod2; + const char *str0 = "module a {namespace urn:a;prefix a;}"; + const char *str1 = "module a {namespace urn:a;prefix a;revision 2018-10-23;}"; + const char *str2 = "module a {namespace urn:a;prefix a;revision 2018-10-23;revision 2018-10-24;}"; + struct ly_in *in0, *in1, *in2; + struct lys_glob_unres unres = {0}; + + unsigned int index = 0; + const char *names[] = { + "ietf-yang-metadata", "yang", "ietf-inet-types", "ietf-yang-types", "ietf-yang-schema-mount", + "ietf-yang-structure-ext", "ietf-datastores", "ietf-yang-library", "a", "a", "a" + }; + + assert_int_equal(LY_SUCCESS, ly_in_new_memory(str0, &in0)); + assert_int_equal(LY_SUCCESS, ly_in_new_memory(str1, &in1)); + assert_int_equal(LY_SUCCESS, ly_in_new_memory(str2, &in2)); + + /* invalid arguments */ + assert_ptr_equal(NULL, ly_ctx_get_module(NULL, NULL, NULL)); + CHECK_LOG("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_parse(UTEST_LYCTX, in1, LYS_IN_YANG, NULL, &mod)); + /* invalid attempts - implementing module of the same name and inserting the same module */ + assert_int_equal(LY_SUCCESS, lys_parse_in(UTEST_LYCTX, in2, LYS_IN_YANG, NULL, NULL, &unres.creating, &mod2)); + assert_int_equal(LY_EDENIED, lys_implement(mod2, NULL, &unres)); + CHECK_LOG_CTX("Module \"a@2018-10-24\" is already implemented in revision \"2018-10-23\".", NULL); + lys_unres_glob_erase(&unres); + ly_in_reset(in1); + /* it is already there, fine */ + assert_int_equal(LY_SUCCESS, lys_parse_in(UTEST_LYCTX, in1, LYS_IN_YANG, NULL, NULL, &unres.creating, NULL)); + /* insert the second module only as imported, not implemented */ + lys_unres_glob_erase(&unres); + ly_in_reset(in2); + assert_int_equal(LY_SUCCESS, lys_parse_in(UTEST_LYCTX, in2, LYS_IN_YANG, NULL, NULL, &unres.creating, &mod2)); + lys_unres_glob_erase(&unres); + assert_non_null(mod2); + assert_ptr_not_equal(mod, mod2); + mod = ly_ctx_get_module_latest(UTEST_LYCTX, "a"); + assert_ptr_equal(mod, mod2); + mod2 = ly_ctx_get_module_latest_ns(UTEST_LYCTX, mod->ns); + assert_ptr_equal(mod, mod2); + /* work with module with no revision */ + assert_int_equal(LY_SUCCESS, lys_parse_in(UTEST_LYCTX, in0, LYS_IN_YANG, NULL, NULL, &unres.creating, &mod)); + lys_unres_glob_erase(&unres); + assert_ptr_equal(mod, ly_ctx_get_module(UTEST_LYCTX, "a", NULL)); + assert_ptr_not_equal(mod, ly_ctx_get_module_latest(UTEST_LYCTX, "a")); + + str1 = "submodule b {belongs-to a {prefix a;}}"; + ly_in_free(in1, 0); + assert_int_equal(LY_SUCCESS, ly_in_new_memory(str1, &in1)); + assert_int_equal(LY_EINVAL, lys_parse_in(UTEST_LYCTX, in1, LYS_IN_YANG, NULL, NULL, &unres.creating, &mod)); + CHECK_LOG_CTX("Input data contains submodule which cannot be parsed directly without its main module.", NULL); + lys_unres_glob_erase(&unres); + + while ((mod = (struct lys_module *)ly_ctx_get_module_iter(UTEST_LYCTX, &index))) { + assert_string_equal(names[index - 1], mod->name); + } + assert_int_equal(11, index); + + /* cleanup */ + ly_in_free(in0, 0); + ly_in_free(in1, 0); + ly_in_free(in2, 0); +} + +static void +test_ylmem(void **state) +{ +#define DATA_YANG_LIBRARY_START "<yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">\n"\ + " <module-set>\n"\ + " <name>complete</name>\n"\ + " <module>\n"\ + " <name>yang</name>\n"\ + " <revision>2022-06-16</revision>\n"\ + " <namespace>urn:ietf:params:xml:ns:yang:1</namespace>\n"\ + " </module>\n"\ + " <module>\n"\ + " <name>ietf-yang-library</name>\n"\ + " <revision>2019-01-04</revision>\n"\ + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>\n"\ + " </module>\n" + +#define DATA_YANG_BASE_IMPORTS " <import-only-module>\n"\ + " <name>ietf-yang-metadata</name>\n"\ + " <revision>2016-08-05</revision>\n"\ + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-metadata</namespace>\n"\ + " </import-only-module>\n"\ + " <import-only-module>\n"\ + " <name>ietf-inet-types</name>\n"\ + " <revision>2013-07-15</revision>\n"\ + " <namespace>urn:ietf:params:xml:ns:yang:ietf-inet-types</namespace>\n"\ + " </import-only-module>\n"\ + " <import-only-module>\n"\ + " <name>ietf-yang-types</name>\n"\ + " <revision>2013-07-15</revision>\n"\ + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-types</namespace>\n"\ + " </import-only-module>\n"\ + " <import-only-module>\n"\ + " <name>ietf-datastores</name>\n"\ + " <revision>2018-02-14</revision>\n"\ + " <namespace>urn:ietf:params:xml:ns:yang:ietf-datastores</namespace>\n"\ + " </import-only-module>\n" + +#define DATA_YANG_SCHEMA_MODULE_STATE " </module-set>\n"\ + " <schema>\n"\ + " <name>complete</name>\n"\ + " <module-set>complete</module-set>\n"\ + " </schema>\n"\ + " <content-id>9</content-id>\n"\ + "</yang-library>\n"\ + "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">\n"\ + " <module-set-id>12</module-set-id>\n"\ + " <module>\n"\ + " <name>ietf-yang-metadata</name>\n"\ + " <revision>2016-08-05</revision>\n"\ + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-metadata</namespace>\n"\ + " <conformance-type>import</conformance-type>\n"\ + " </module>\n"\ + " <module>\n"\ + " <name>yang</name>\n"\ + " <revision>2022-06-16</revision>\n"\ + " <namespace>urn:ietf:params:xml:ns:yang:1</namespace>\n"\ + " <conformance-type>implement</conformance-type>\n"\ + " </module>\n"\ + " <module>\n"\ + " <name>ietf-inet-types</name>\n"\ + " <revision>2013-07-15</revision>\n"\ + " <namespace>urn:ietf:params:xml:ns:yang:ietf-inet-types</namespace>\n"\ + " <conformance-type>import</conformance-type>\n"\ + " </module>\n"\ + " <module>\n"\ + " <name>ietf-yang-types</name>\n"\ + " <revision>2013-07-15</revision>\n"\ + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-types</namespace>\n"\ + " <conformance-type>import</conformance-type>\n"\ + " </module>\n"\ + " <module>\n"\ + " <name>ietf-yang-library</name>\n"\ + " <revision>2019-01-04</revision>\n"\ + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>\n"\ + " <conformance-type>implement</conformance-type>\n"\ + " </module>\n"\ + " <module>\n"\ + " <name>ietf-datastores</name>\n"\ + " <revision>2018-02-14</revision>\n"\ + " <namespace>urn:ietf:params:xml:ns:yang:ietf-datastores</namespace>\n"\ + " <conformance-type>import</conformance-type>\n"\ + " </module>\n" + + const char *yanglibrary_only = + DATA_YANG_LIBRARY_START + DATA_YANG_BASE_IMPORTS + DATA_YANG_SCHEMA_MODULE_STATE + "</modules-state>\n"; + + const char *with_netconf = + DATA_YANG_LIBRARY_START + " <module>\n" + " <name>ietf-netconf</name>\n" + " <revision>2011-06-01</revision>\n" + " <namespace>urn:ietf:params:xml:ns:netconf:base:1.0</namespace>\n" + " </module>\n" + DATA_YANG_BASE_IMPORTS + " <import-only-module>\n" + " <name>ietf-netconf-acm</name>\n" + " <revision>2018-02-14</revision>\n" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-netconf-acm</namespace>\n" + " </import-only-module>\n" + DATA_YANG_SCHEMA_MODULE_STATE + " <module>\n" + " <name>ietf-netconf</name>\n" + " <revision>2011-06-01</revision>\n" + " <namespace>urn:ietf:params:xml:ns:netconf:base:1.0</namespace>\n" + " <conformance-type>implement</conformance-type>\n" + " </module>\n" + " <module>\n" + " <name>ietf-netconf-acm</name>\n" + " <revision>2018-02-14</revision>\n" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-netconf-acm</namespace>\n" + " <conformance-type>import</conformance-type>\n" + " </module>\n" + "</modules-state>"; + + char *with_netconf_features = malloc(8096); + + strcpy(with_netconf_features, + DATA_YANG_LIBRARY_START + " <module>\n" + " <name>ietf-netconf</name>\n" + " <revision>2011-06-01</revision>\n" + " <namespace>urn:ietf:params:xml:ns:netconf:base:1.0</namespace>\n" + " <feature>writable-running</feature>\n" + " <feature>candidate</feature>\n" + " <feature>confirmed-commit</feature>\n" + " <feature>rollback-on-error</feature>\n" + " <feature>validate</feature>\n" + " <feature>startup</feature>\n" + " <feature>url</feature>\n" + " <feature>xpath</feature>\n" + " </module>\n" + " <import-only-module>\n" + " <name>ietf-yang-metadata</name>\n" + " <revision>2016-08-05</revision>\n" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-metadata</namespace>\n" + " </import-only-module>\n" + " <import-only-module>\n" + " <name>ietf-inet-types</name>\n" + " <revision>2013-07-15</revision>\n" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-inet-types</namespace>\n" + " </import-only-module>\n" + " <import-only-module>\n" + " <name>ietf-yang-types</name>\n" + " <revision>2013-07-15</revision>\n" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-types</namespace>\n" + " </import-only-module>\n" + " <import-only-module>\n" + " <name>ietf-datastores</name>\n" + " <revision>2018-02-14</revision>\n" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-datastores</namespace>\n" + " </import-only-module>\n" + " <import-only-module>\n" + " <name>ietf-netconf-acm</name>\n" + " <revision>2018-02-14</revision>\n" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-netconf-acm</namespace>\n" + " </import-only-module>\n"); + strcpy(with_netconf_features + strlen(with_netconf_features), + DATA_YANG_SCHEMA_MODULE_STATE + " <module>\n" + " <name>ietf-netconf</name>\n" + " <revision>2011-06-01</revision>\n" + " <namespace>urn:ietf:params:xml:ns:netconf:base:1.0</namespace>\n" + " <feature>writable-running</feature>\n" + " <feature>candidate</feature>\n" + " <feature>confirmed-commit</feature>\n" + " <feature>rollback-on-error</feature>\n" + " <feature>validate</feature>\n" + " <feature>startup</feature>\n" + " <feature>url</feature>\n" + " <feature>xpath</feature>\n" + " <conformance-type>implement</conformance-type>\n" + " </module>\n" + " <module>\n" + " <name>ietf-netconf-acm</name>\n" + " <revision>2018-02-14</revision>\n" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-netconf-acm</namespace>\n" + " <conformance-type>import</conformance-type>\n" + " </module>\n" + "</modules-state>"); + + const char *garbage_revision = + "<yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">\n" + " <module-set>\n" + " <name>complete</name>\n" + " <module>\n" + " <name>yang</name>\n" + " <revision>2022-06-16</revision>\n" + " <namespace>urn:ietf:params:xml:ns:yang:1</namespace>\n" + " </module>\n" + " <module>\n" + " <name>ietf-yang-library</name>\n" + " <revision>2019-01-01</revision>\n" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>\n" + " </module>\n" + DATA_YANG_BASE_IMPORTS + DATA_YANG_SCHEMA_MODULE_STATE + "</modules-state>\n"; + + const char *no_yanglibrary = + "<yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">\n" + " <module-set>\n" + " <name>complete</name>\n" + " <module>\n" + " <name>yang</name>\n" + " <revision>2022-06-16</revision>\n" + " <namespace>urn:ietf:params:xml:ns:yang:1</namespace>\n" + " </module>\n" + DATA_YANG_BASE_IMPORTS + DATA_YANG_SCHEMA_MODULE_STATE + "</modules-state>\n"; + + (void) state; + /* seperate context to avoid double free during teadown */ + struct ly_ctx *ctx_test = NULL; + + /* test invalid parameters */ + assert_int_equal(LY_EINVAL, ly_ctx_new_ylpath(NULL, NULL, LYD_XML, 0, &ctx_test)); + assert_int_equal(LY_EINVAL, ly_ctx_new_ylpath(NULL, TESTS_SRC, LYD_XML, 0, NULL)); + assert_int_equal(LY_ESYS, ly_ctx_new_ylpath(NULL, TESTS_SRC "garbage", LYD_XML, 0, &ctx_test)); + + /* basic test with ietf-yang-library-only */ + assert_int_equal(LY_SUCCESS, ly_ctx_new_ylmem(TESTS_SRC "/modules/yang/", yanglibrary_only, LYD_XML, 0, &ctx_test)); + assert_non_null(ly_ctx_get_module(ctx_test, "ietf-yang-library", "2019-01-04")); + assert_null(ly_ctx_get_module(ctx_test, "ietf-netconf", "2011-06-01")); + ly_ctx_destroy(ctx_test); + ctx_test = NULL; + + /* test loading module, should also import other module */ + assert_int_equal(LY_SUCCESS, ly_ctx_new_ylmem(TESTS_SRC "/modules/yang/", with_netconf, LYD_XML, 0, &ctx_test)); + assert_non_null(ly_ctx_get_module(ctx_test, "ietf-netconf", "2011-06-01")); + assert_int_equal(1, ly_ctx_get_module(ctx_test, "ietf-netconf", "2011-06-01")->implemented); + assert_non_null(ly_ctx_get_module(ctx_test, "ietf-netconf-acm", "2018-02-14")); + assert_int_equal(0, ly_ctx_get_module(ctx_test, "ietf-netconf-acm", "2018-02-14")->implemented); + assert_int_equal(LY_ENOT, lys_feature_value(ly_ctx_get_module(ctx_test, "ietf-netconf", "2011-06-01"), "url")); + ly_ctx_destroy(ctx_test); + ctx_test = NULL; + + /* test loading module with feature if they are present */ + assert_int_equal(LY_SUCCESS, ly_ctx_new_ylmem(TESTS_SRC "/modules/yang/", with_netconf_features, LYD_XML, 0, &ctx_test)); + assert_non_null(ly_ctx_get_module(ctx_test, "ietf-netconf", "2011-06-01")); + assert_non_null(ly_ctx_get_module(ctx_test, "ietf-netconf-acm", "2018-02-14")); + assert_int_equal(LY_SUCCESS, lys_feature_value(ly_ctx_get_module(ctx_test, "ietf-netconf", "2011-06-01"), "url")); + ly_ctx_destroy(ctx_test); + ctx_test = NULL; + + /* test with not matching revision */ + assert_int_equal(LY_EINVAL, ly_ctx_new_ylmem(TESTS_SRC "/modules/yang/", garbage_revision, LYD_XML, 0, &ctx_test)); + + /* test data containing ietf-yang-library which conflicts with the option */ + assert_int_equal(LY_EINVAL, ly_ctx_new_ylmem(TESTS_SRC "/modules/yang/", with_netconf_features, LYD_XML, LY_CTX_NO_YANGLIBRARY, &ctx_test)); + + /* test creating without ietf-yang-library */ + assert_int_equal(LY_SUCCESS, ly_ctx_new_ylmem(TESTS_SRC "/modules/yang/", no_yanglibrary, LYD_XML, LY_CTX_NO_YANGLIBRARY, &ctx_test)); + assert_int_equal(NULL, ly_ctx_get_module(ctx_test, "ietf-yang-library", "2019-01-04")); + ly_ctx_destroy(ctx_test); + free(with_netconf_features); +} + +static LY_ERR +check_node_priv_parsed_is_set(struct lysc_node *node, void *data, ly_bool *UNUSED(dfs_continue)) +{ + const struct lysp_node *pnode; + const char ***iter; + + pnode = (const struct lysp_node *)node->priv; + CHECK_POINTER(pnode, 1); + iter = (const char ***)data; + CHECK_POINTER(**iter, 1); + CHECK_STRING(pnode->name, **iter); + (*iter)++; + + return LY_SUCCESS; +} + +static LY_ERR +check_node_priv_parsed_not_set(struct lysc_node *node, void *UNUSED(data), ly_bool *UNUSED(dfs_continue)) +{ + CHECK_POINTER(node->priv, 0); + return LY_SUCCESS; +} + +static void +check_ext_instance_priv_parsed_is_set(struct lysc_ext_instance *ext) +{ + LY_ARRAY_COUNT_TYPE u, v; + struct lysc_ext_substmt *substmts; + struct lysc_node *cnode; + const char **iter; + const char *check[] = { + "tmp_cont", "lf", NULL + }; + + LY_ARRAY_FOR(ext, u) { + substmts = ext[u].substmts; + LY_ARRAY_FOR(substmts, v) { + if (substmts && substmts[v].storage && (substmts[v].stmt & LY_STMT_DATA_NODE_MASK)) { + cnode = *(struct lysc_node **)substmts[v].storage; + iter = check; + assert_int_equal(LY_SUCCESS, lysc_tree_dfs_full(cnode, check_node_priv_parsed_is_set, &iter)); + } + } + } +} + +static void +check_ext_instance_priv_parsed_not_set(struct lysc_ext_instance *ext) +{ + LY_ARRAY_COUNT_TYPE u, v; + struct lysc_ext_substmt *substmts; + struct lysc_node *cnode; + + LY_ARRAY_FOR(ext, u) { + substmts = ext[u].substmts; + LY_ARRAY_FOR(substmts, v) { + if (substmts && substmts[v].storage && (substmts[v].stmt & LY_STMT_DATA_NODE_MASK)) { + cnode = *(struct lysc_node **)substmts[v].storage; + if (cnode) { + CHECK_POINTER((struct lysp_node *)cnode->priv, 0); + } + } + } + } +} + +/** + * @brief Testing of LY_CTX_SET_PRIV_PARSED. + */ +static void +test_set_priv_parsed(void **state) +{ + struct lys_module *mod; + const char *schema_a; + const char **iter; + const char *check[] = { + "cont", "contnotif", "contx", "grpleaf", "augleaf", "l1", + "l1a", "l1b", "l1c", "foo1", "ll", "any", "l2", + "l2c", "l2cx", "ch", "cas", "casx", "oper", + "input", "inparam", "output", "outparam", "n1", NULL + }; + + /* each node must have a unique name. */ + schema_a = "module a {\n" + " namespace urn:tests:a;\n" + " prefix a;yang-version 1.1;\n" + "\n" + " import ietf-restconf {\n" + " prefix rc;\n" + " revision-date 2017-01-26;\n" + " }\n" + "\n" + " rc:yang-data \"tmp\" {\n" + " container tmp_cont {\n" + " leaf lf {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + " container cont {\n" + " notification contnotif;\n" + " leaf-list contx {\n" + " type string;\n" + " }\n" + " uses grp;\n" + " }\n" + " list l1 {\n" + " key \"l1a l1b\";\n" + " leaf l1a {\n" + " type string;\n" + " }\n" + " leaf l1b {\n" + " type string;\n" + " }\n" + " leaf l1c {\n" + " type string;\n" + " }\n" + " }\n" + " feature f1;\n" + " feature f2;\n" + " leaf foo1 {\n" + " type uint16;\n" + " if-feature f1;\n" + " }\n" + " leaf foo2 {\n" + " type uint16;\n" + " }\n" + " leaf foo3 {\n" + " type uint16;\n" + " if-feature f2;\n" + " }\n" + " leaf-list ll {\n" + " type string;\n" + " }\n" + " anydata any {\n" + " config false;\n" + " }\n" + " list l2 {\n" + " config false;\n" + " container l2c {\n" + " leaf l2cx {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + " choice ch {\n" + " case cas {\n" + " leaf casx {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + " rpc oper {\n" + " input {\n" + " leaf inparam {\n" + " type string;\n" + " }\n" + " }\n" + " output {\n" + " leaf outparam {\n" + " type int8;\n" + " }\n" + " }\n" + " }\n" + " notification n1;\n" + " grouping grp {\n" + " leaf grpleaf {\n" + " type uint16;\n" + " }\n" + " }\n" + " augment /cont {\n" + " leaf augleaf {\n" + " type uint16;\n" + " }\n" + " }\n" + " deviation /a:foo2 {\n" + " deviate not-supported;\n" + " }\n" + "}\n"; + + /* use own context with extra flags */ + ly_ctx_destroy(UTEST_LYCTX); + const char *feats[] = {"f1", NULL}; + + assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_SET_PRIV_PARSED, &UTEST_LYCTX)); + assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_DIR_MODULES_YANG)); + assert_non_null(ly_ctx_load_module(UTEST_LYCTX, "ietf-restconf", "2017-01-26", NULL)); + UTEST_ADD_MODULE(schema_a, LYS_IN_YANG, feats, NULL); + + print_message("[ ] create context\n"); + mod = ly_ctx_get_module(UTEST_LYCTX, "a", NULL); + iter = check; + assert_int_equal(LY_SUCCESS, lysc_module_dfs_full(mod, check_node_priv_parsed_is_set, &iter)); + check_ext_instance_priv_parsed_is_set(mod->compiled->exts); + + print_message("[ ] unset option\n"); + assert_int_equal(LY_SUCCESS, ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED)); + mod = ly_ctx_get_module(UTEST_LYCTX, "a", NULL); + iter = check; + assert_int_equal(LY_SUCCESS, lysc_module_dfs_full(mod, check_node_priv_parsed_not_set, &iter)); + check_ext_instance_priv_parsed_not_set(mod->compiled->exts); + + print_message("[ ] set option\n"); + assert_int_equal(LY_SUCCESS, ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED)); + mod = ly_ctx_get_module(UTEST_LYCTX, "a", NULL); + iter = check; + assert_int_equal(LY_SUCCESS, lysc_module_dfs_full(mod, check_node_priv_parsed_is_set, &iter)); + check_ext_instance_priv_parsed_is_set(mod->compiled->exts); +} + +static void +test_explicit_compile(void **state) +{ + uint32_t i; + struct lys_module *mod; + const char *schema_a = "module a {\n" + " namespace urn:tests:a;\n" + " prefix a;yang-version 1.1;\n" + " feature f1;\n" + " feature f2;\n" + " leaf foo1 {\n" + " type uint16;\n" + " if-feature f1;\n" + " }\n" + " leaf foo2 {\n" + " type uint16;\n" + " }\n" + " container cont {\n" + " leaf foo3 {\n" + " type string;\n" + " }\n" + " }\n" + "}\n"; + const char *schema_b = "module b {\n" + " namespace urn:tests:b;\n" + " prefix b;yang-version 1.1;\n" + " import a {\n" + " prefix a;\n" + " }\n" + " augment /a:cont {\n" + " leaf augleaf {\n" + " type uint16;\n" + " }\n" + " }\n" + "}\n"; + const char *schema_c = "module c {\n" + " namespace urn:tests:c;\n" + " prefix c;yang-version 1.1;\n" + " import a {\n" + " prefix a;\n" + " }\n" + " deviation /a:foo2 {\n" + " deviate not-supported;\n" + " }\n" + "}\n"; + + /* use own context with extra flags */ + ly_ctx_destroy(UTEST_LYCTX); + const char *feats[] = {"f1", NULL}; + + assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_EXPLICIT_COMPILE, &UTEST_LYCTX)); + UTEST_ADD_MODULE(schema_a, LYS_IN_YANG, NULL, &mod); + UTEST_ADD_MODULE(schema_b, LYS_IN_YANG, NULL, NULL); + UTEST_ADD_MODULE(schema_c, LYS_IN_YANG, NULL, NULL); + assert_int_equal(LY_SUCCESS, lys_set_implemented((struct lys_module *)mod, feats)); + + /* none of the modules should be compiled */ + i = 0; + while ((mod = ly_ctx_get_module_iter(UTEST_LYCTX, &i))) { + assert_null(mod->compiled); + } + + assert_int_equal(LY_SUCCESS, ly_ctx_compile(UTEST_LYCTX)); + + /* check internal modules */ + mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "yang"); + assert_non_null(mod); + mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "ietf-datastores"); + assert_non_null(mod); + mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "ietf-yang-library"); + assert_non_null(mod); + + /* check test modules */ + mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "a"); + assert_non_null(mod); + mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "b"); + assert_non_null(mod); + mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "c"); + assert_non_null(mod); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_searchdirs), + UTEST(test_options), + UTEST(test_models), + UTEST(test_imports), + UTEST(test_get_models), + UTEST(test_ylmem), + UTEST(test_set_priv_parsed), + UTEST(test_explicit_compile), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/basic/test_hash_table.c b/tests/utests/basic/test_hash_table.c new file mode 100644 index 0000000..25b595a --- /dev/null +++ b/tests/utests/basic/test_hash_table.c @@ -0,0 +1,271 @@ +/* + * @file test_hash_table.c + * @author: Radek Krejci <rkrejci@cesnet.cz> + * @brief unit tests for functions from hash_table.c + * + * Copyright (c) 2018 CESNET, z.s.p.o. + * + * This source code is licensed under BSD 3-Clause License (the "License"). + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + */ +#define _UTEST_MAIN_ +#include "utests.h" + +#include <stdlib.h> + +#include "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 str_p (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); + 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..be27510 --- /dev/null +++ b/tests/utests/basic/test_inout.c @@ -0,0 +1,401 @@ +/** + * @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" + +#define TEST_INPUT_FILE TESTS_BIN "/libyang_test_input" +#define TEST_OUTPUT_FILE TESTS_BIN "/libyang_test_output" +#define TEST_OUTPUT_FILE2 TESTS_BIN "/libyang_test_output2" + +static int +setup_files(void **state) +{ + int fd; + + UTEST_SETUP; + + /* create input */ + fd = open(TEST_INPUT_FILE, O_CREAT | O_WRONLY, 00600); + if (fd == -1) { + return 1; + } + + /* write something */ + if (write(fd, "data", 4) != 4) { + return 1; + } + close(fd); + + /* create output */ + fd = open(TEST_OUTPUT_FILE, O_CREAT | O_RDONLY, 00600); + if (fd == -1) { + return 1; + } + close(fd); + + /* create output2 */ + fd = open(TEST_OUTPUT_FILE2, O_CREAT | O_RDONLY, 00600); + if (fd == -1) { + return 1; + } + close(fd); + + return 0; +} + +static int +teardown_files(void **state) +{ + unlink(TEST_INPUT_FILE); + unlink(TEST_OUTPUT_FILE); + unlink(TEST_OUTPUT_FILE2); + + UTEST_TEARDOWN; + return 0; +} + +static void +test_input_mem(void **UNUSED(state)) +{ + struct ly_in *in = NULL; + char *str1 = "a", *str2 = "b"; + + assert_int_equal(LY_EINVAL, ly_in_new_memory(NULL, NULL)); + 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(TEST_INPUT_FILE, O_RDONLY)); + assert_int_not_equal(-1, fd2 = open(TEST_INPUT_FILE, O_RDONLY)); + + assert_int_equal(LY_EINVAL, ly_in_new_fd(fd1, NULL)); + + assert_int_equal(LY_SUCCESS, ly_in_new_fd(fd1, &in)); + assert_int_equal(LY_IN_FD, ly_in_type(in)); + assert_ptr_equal(fd1, ly_in_fd(in, fd2)); + assert_ptr_equal(fd2, ly_in_fd(in, -1)); + assert_ptr_equal(fd2, ly_in_fd(in, -1)); + ly_in_free(in, 1); + /* fd1 is still open */ + assert_int_equal(0, fstat(fd1, &statbuf)); + close(fd1); +#ifndef _WIN32 + /* But fd2 was closed by ly_in_free(). This results in an "invalid handler" on Windows. */ + errno = 0; + assert_int_equal(-1, fstat(fd2, &statbuf)); + assert_int_equal(errno, EBADF); +#endif +} + +static void +test_input_file(void **UNUSED(state)) +{ + struct ly_in *in = NULL; + FILE *f1 = NULL, *f2 = NULL; + + assert_int_equal(LY_EINVAL, ly_in_new_file(NULL, NULL)); + assert_null(ly_in_file(NULL, NULL)); + + assert_non_null(f1 = fopen(TEST_INPUT_FILE, "rb")); + assert_non_null(f2 = fopen(TEST_INPUT_FILE, "rb")); + + assert_int_equal(LY_EINVAL, ly_in_new_file(f1, NULL)); + + assert_int_equal(LY_SUCCESS, ly_in_new_file(f1, &in)); + assert_int_equal(LY_IN_FILE, ly_in_type(in)); + assert_ptr_equal(f1, ly_in_file(in, f2)); + assert_ptr_equal(f2, ly_in_file(in, NULL)); + assert_ptr_equal(f2, ly_in_file(in, NULL)); + ly_in_free(in, 1); + /* f1 is still open */ + assert_int_not_equal(-1, fileno(f1)); + fclose(f1); + /* but f2 was closed by ly_in_free() */ +} + +static void +test_input_filepath(void **UNUSED(state)) +{ + struct ly_in *in = NULL; + const char *path1 = TEST_INPUT_FILE, *path2 = TEST_INPUT_FILE; + + assert_int_equal(LY_EINVAL, ly_in_new_filepath(NULL, 0, NULL)); + assert_int_equal(LY_EINVAL, ly_in_new_filepath(path1, 0, NULL)); + assert_ptr_equal(((void *)-1), ly_in_filepath(NULL, NULL, 0)); + + assert_int_equal(LY_SUCCESS, ly_in_new_filepath(path1, 0, &in)); + assert_int_equal(LY_IN_FILEPATH, ly_in_type(in)); + assert_ptr_equal(NULL, ly_in_filepath(in, path2, 0)); + assert_string_equal(path2, ly_in_filepath(in, NULL, 0)); + ly_in_free(in, 0); +} + +static void +test_output_mem(void **UNUSED(state)) +{ + struct ly_out *out = NULL; + char *buf1 = NULL, *buf2 = NULL; + + /* manipulate with the handler */ + assert_int_equal(LY_SUCCESS, ly_out_new_memory(&buf1, 0, &out)); + assert_int_equal(LY_OUT_MEMORY, ly_out_type(out)); + ly_write(out, "test", 4); + assert_ptr_equal(buf1, ly_out_memory(out, &buf2, 0)); + assert_ptr_equal(buf2, ly_out_memory(out, NULL, 0)); + assert_ptr_equal(buf2, ly_out_memory(out, &buf1, strlen(buf1))); + ly_out_free(out, NULL, 0); + + assert_int_equal(LY_SUCCESS, ly_out_new_memory(&buf1, strlen(buf1), &out)); + ly_out_free(out, NULL, 1); + + /* writing data */ + + assert_int_equal(LY_SUCCESS, ly_out_new_memory(&buf1, 0, &out)); + assert_int_equal(LY_SUCCESS, ly_print(out, "test %s", "print")); + assert_int_equal(10, ly_out_printed(out)); + assert_string_equal("test print", buf1); + assert_int_equal(LY_SUCCESS, ly_out_reset(out)); + assert_int_equal(LY_SUCCESS, ly_write(out, "rewrite", 8)); + assert_int_equal(8, ly_out_printed(out)); + assert_string_equal("rewrite", buf1); + ly_out_free(out, NULL, 1); +} + +static void +test_output_fd(void **UNUSED(state)) +{ + struct ly_out *out = NULL; + int fd1, fd2; + char buf[31] = {0}; + + assert_int_not_equal(-1, fd1 = open(TEST_OUTPUT_FILE, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR)); + assert_int_not_equal(-1, fd2 = open(TEST_OUTPUT_FILE, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR)); + + /* manipulate with the handler */ + assert_int_equal(LY_SUCCESS, ly_out_new_fd(fd1, &out)); + assert_int_equal(LY_OUT_FD, ly_out_type(out)); + assert_ptr_equal(fd1, ly_out_fd(out, fd2)); + assert_ptr_equal(fd2, ly_out_fd(out, -1)); + assert_ptr_equal(fd2, ly_out_fd(out, fd1)); + ly_out_free(out, NULL, 0); + assert_int_equal(0, close(fd2)); + assert_int_equal(LY_SUCCESS, ly_out_new_fd(fd1, &out)); + ly_out_free(out, NULL, 1); + + /* writing data */ + assert_int_not_equal(-1, fd1 = open(TEST_OUTPUT_FILE, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR)); + assert_int_not_equal(-1, fd2 = open(TEST_OUTPUT_FILE, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR)); + /* truncate file to start with no data */ + assert_int_equal(0, ftruncate(fd1, 0)); + + assert_int_equal(LY_SUCCESS, ly_out_new_fd(fd1, &out)); + assert_int_equal(LY_SUCCESS, ly_print(out, "test %s", "print")); + assert_int_equal(10, ly_out_printed(out)); + ly_print_flush(out); + assert_int_equal(10, read(fd2, buf, 30)); + assert_string_equal("test print", buf); + assert_int_equal(0, lseek(fd2, 0, SEEK_SET)); + assert_int_equal(LY_SUCCESS, ly_out_reset(out)); + + assert_int_equal(LY_SUCCESS, ly_write(out, "rewrite", 8)); + assert_int_equal(8, ly_out_printed(out)); + ly_print_flush(out); + assert_int_equal(8, read(fd2, buf, 30)); + assert_string_equal("rewrite", buf); + + close(fd2); + ly_out_free(out, NULL, 1); +} + +static void +test_output_file(void **UNUSED(state)) +{ + struct ly_out *out = NULL; + FILE *f1, *f2; + char buf[31] = {0}; + + assert_non_null(f1 = fopen(TEST_OUTPUT_FILE, "wb")); + assert_non_null(f2 = fopen(TEST_OUTPUT_FILE, "wb")); + + /* manipulate with the handler */ + assert_int_equal(LY_SUCCESS, ly_out_new_file(f1, &out)); + assert_int_equal(LY_OUT_FILE, ly_out_type(out)); + assert_ptr_equal(f1, ly_out_file(out, f2)); + assert_ptr_equal(f2, ly_out_file(out, NULL)); + assert_ptr_equal(f2, ly_out_file(out, f1)); + ly_out_free(out, NULL, 0); + assert_int_equal(0, fclose(f2)); + assert_int_equal(LY_SUCCESS, ly_out_new_file(f1, &out)); + ly_out_free(out, NULL, 1); + + /* writing data */ + assert_non_null(f1 = fopen(TEST_OUTPUT_FILE, "wb")); + assert_non_null(f2 = fopen(TEST_OUTPUT_FILE, "rb")); + + assert_int_equal(LY_SUCCESS, ly_out_new_file(f1, &out)); + assert_int_equal(LY_SUCCESS, ly_print(out, "test %s", "print")); + assert_int_equal(10, ly_out_printed(out)); + ly_print_flush(out); + assert_non_null(fgets(buf, 31, f2)); + assert_string_equal("test print", buf); + assert_int_equal(0, fseek(f2, 0, SEEK_SET)); + assert_int_equal(LY_SUCCESS, ly_out_reset(out)); + + assert_int_equal(LY_SUCCESS, ly_write(out, "rewrite", 8)); + assert_int_equal(8, ly_out_printed(out)); + ly_print_flush(out); + assert_non_null(fgets(buf, 31, f2)); + assert_string_equal("rewrite", buf); + + fclose(f2); + ly_out_free(out, NULL, 1); +} + +static void +test_output_filepath(void **UNUSED(state)) +{ + struct ly_out *out = NULL; + FILE *f1; + char buf[31] = {0}; + const char *fp1 = TEST_OUTPUT_FILE; + const char *fp2 = TEST_OUTPUT_FILE2; + + /* manipulate with the handler */ + assert_int_equal(LY_SUCCESS, ly_out_new_filepath(fp1, &out)); + assert_int_equal(LY_OUT_FILEPATH, ly_out_type(out)); + assert_ptr_equal(NULL, ly_out_filepath(out, fp2)); + assert_string_equal(fp2, ly_out_filepath(out, NULL)); + assert_ptr_equal(NULL, ly_out_filepath(out, fp1)); + ly_out_free(out, NULL, 0); + assert_int_equal(LY_SUCCESS, ly_out_new_filepath(fp1, &out)); + ly_out_free(out, NULL, 1); + + /* writing data */ + assert_non_null(f1 = fopen(fp1, "rb")); + + assert_int_equal(LY_SUCCESS, ly_out_new_filepath(fp1, &out)); + assert_int_equal(LY_SUCCESS, ly_print(out, "test %s", "print")); + assert_int_equal(10, ly_out_printed(out)); + ly_print_flush(out); + assert_non_null(fgets(buf, 31, f1)); + assert_string_equal("test print", buf); + assert_int_equal(0, fseek(f1, 0, SEEK_SET)); + assert_int_equal(LY_SUCCESS, ly_out_reset(out)); + + assert_int_equal(LY_SUCCESS, ly_write(out, "rewrite", 8)); + assert_int_equal(8, ly_out_printed(out)); + ly_print_flush(out); + assert_non_null(fgets(buf, 31, f1)); + assert_string_equal("rewrite", buf); + + fclose(f1); + ly_out_free(out, NULL, 1); +} + +static ssize_t +write_clb(void *user_data, const void *buf, size_t count) +{ + return write((uintptr_t)user_data, buf, count); +} + +void +close_clb(void *arg) +{ + close((uintptr_t)arg); +} + +static void +test_output_clb(void **UNUSED(state)) +{ + struct ly_out *out = NULL; + int fd1, fd2; + char buf[31] = {0}; + + assert_int_not_equal(-1, fd1 = open(TEST_OUTPUT_FILE, O_RDWR)); + assert_int_not_equal(-1, fd2 = open(TEST_OUTPUT_FILE, O_RDWR)); + + /* manipulate with the handler */ + assert_int_equal(LY_SUCCESS, ly_out_new_clb(write_clb, (void *)(intptr_t)fd1, &out)); + assert_int_equal(LY_OUT_CALLBACK, ly_out_type(out)); + assert_ptr_equal(fd1, ly_out_clb_arg(out, (void *)(intptr_t)fd2)); + assert_ptr_equal(fd2, ly_out_clb_arg(out, NULL)); + assert_ptr_equal(fd2, ly_out_clb_arg(out, (void *)(intptr_t)fd1)); + assert_ptr_equal(write_clb, ly_out_clb(out, write_clb)); + ly_out_free(out, NULL, 0); + assert_int_equal(0, close(fd2)); + assert_int_equal(LY_SUCCESS, ly_out_new_clb(write_clb, (void *)(intptr_t)fd1, &out)); + ly_out_free(out, close_clb, 0); + + /* writing data */ + assert_int_not_equal(-1, fd1 = open(TEST_OUTPUT_FILE, O_RDWR)); + assert_int_not_equal(-1, fd2 = open(TEST_OUTPUT_FILE, O_RDWR)); + /* truncate file to start with no data */ + assert_int_equal(0, ftruncate(fd1, 0)); + + assert_int_equal(LY_SUCCESS, ly_out_new_clb(write_clb, (void *)(intptr_t)fd1, &out)); + assert_int_equal(LY_SUCCESS, ly_print(out, "test %s", "print")); + assert_int_equal(10, ly_out_printed(out)); + assert_int_equal(10, read(fd2, buf, 30)); + assert_string_equal("test print", buf); + + close(fd2); + ly_out_free(out, close_clb, 0); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_input_mem), + UTEST(test_input_fd, setup_files, teardown_files), + UTEST(test_input_file, setup_files, teardown_files), + UTEST(test_input_filepath, setup_files, teardown_files), + UTEST(test_output_mem), + UTEST(test_output_fd, setup_files, teardown_files), + UTEST(test_output_file, setup_files, teardown_files), + UTEST(test_output_filepath, setup_files, teardown_files), + UTEST(test_output_clb, setup_files, teardown_files), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/basic/test_json.c b/tests/utests/basic/test_json.c new file mode 100644 index 0000000..1896b8a --- /dev/null +++ b/tests/utests/basic/test_json.c @@ -0,0 +1,794 @@ +/* + * @file test_json.c + * @author: Radek Krejci <rkrejci@cesnet.cz> + * @brief unit tests for a generic JSON parser + * + * Copyright (c) 2020 CESNET, z.s.p.o. + * + * This source code is licensed under BSD 3-Clause License (the "License"). + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + */ +#define _UTEST_MAIN_ +#include "utests.h" + +#include "context.h" +#include "in_internal.h" +#include "json.h" +static void +test_general(void **state) +{ + struct lyjson_ctx *jsonctx; + struct ly_in *in; + const char *str; + + /* empty */ + str = ""; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &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, 0, &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, 0, &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, 0, &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, 0, &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, 0, &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, 0, &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, 0, &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); + + /* integer, positive exponent */ + str = "550E3"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0)); + assert_string_equal("550000", jsonctx->value); + assert_int_equal(6, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "-550E3"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0)); + assert_string_equal("-550000", jsonctx->value); + assert_int_equal(7, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + /* integer, negative exponent */ + str = "1E-1"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0)); + assert_string_equal("0.1", jsonctx->value); + assert_int_equal(3, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "15E-1"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &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-1"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0)); + assert_string_equal("-1.5", jsonctx->value); + assert_int_equal(4, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "16E-2"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0)); + assert_string_equal("0.16", jsonctx->value); + assert_int_equal(4, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "-16E-2"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0)); + assert_string_equal("-0.16", jsonctx->value); + assert_int_equal(5, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "17E-3"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0)); + assert_string_equal("0.017", jsonctx->value); + assert_int_equal(5, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "-17E-3"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0)); + assert_string_equal("-0.017", jsonctx->value); + assert_int_equal(6, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "21000E-2"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0)); + assert_string_equal("210", jsonctx->value); + assert_int_equal(3, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "21000E-4"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0)); + assert_string_equal("2.1", jsonctx->value); + assert_int_equal(3, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "21000E-7"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0)); + assert_string_equal("0.0021", jsonctx->value); + assert_int_equal(6, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + /* decimal number, positive exponent */ + str = "5.087E1"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0)); + assert_string_equal("50.87", jsonctx->value); + assert_int_equal(5, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "-5.087E1"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0)); + assert_string_equal("-50.87", jsonctx->value); + assert_int_equal(6, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "5.087E5"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0)); + assert_string_equal("508700", jsonctx->value); + assert_int_equal(6, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "59.1e+1"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0)); + assert_string_equal("591", jsonctx->value); + assert_int_equal(3, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "0.005087E1"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0)); + assert_string_equal("0.05087", jsonctx->value); + assert_int_equal(7, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "0.005087E2"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0)); + assert_string_equal("0.5087", jsonctx->value); + assert_int_equal(6, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "0.005087E6"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0)); + assert_string_equal("5087", jsonctx->value); + assert_int_equal(4, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "0.05087E6"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0)); + assert_string_equal("50870", jsonctx->value); + assert_int_equal(5, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "0.005087E8"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0)); + assert_string_equal("508700", jsonctx->value); + assert_int_equal(6, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + /* decimal number, negative exponent */ + str = "35.94e-1"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0)); + assert_string_equal("3.594", jsonctx->value); + assert_int_equal(5, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "-35.94e-1"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0)); + assert_string_equal("-3.594", jsonctx->value); + assert_int_equal(6, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "35.94e-2"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0)); + assert_string_equal("0.3594", jsonctx->value); + assert_int_equal(6, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "35.94e-3"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0)); + assert_string_equal("0.03594", jsonctx->value); + assert_int_equal(7, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "0.3594e-1"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0)); + assert_string_equal("0.03594", jsonctx->value); + assert_int_equal(7, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "0.03594e-1"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0)); + assert_string_equal("0.003594", jsonctx->value); + assert_int_equal(8, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "0.003594e-1"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0)); + assert_string_equal("0.0003594", jsonctx->value); + assert_int_equal(9, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "0.3594e-2"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0)); + assert_string_equal("0.003594", jsonctx->value); + assert_int_equal(8, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "0.03594e-2"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0)); + assert_string_equal("0.0003594", jsonctx->value); + assert_int_equal(9, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "0.003594e-2"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0)); + assert_string_equal("0.00003594", jsonctx->value); + assert_int_equal(10, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + /* zero */ + str = "0"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0)); + assert_true(jsonctx->value[0] == '0'); + assert_int_equal(1, jsonctx->value_len); + assert_int_equal(0, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "-0"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0)); + assert_true(jsonctx->value[0] == '-'); + assert_true(jsonctx->value[1] == '0'); + assert_int_equal(2, jsonctx->value_len); + assert_int_equal(0, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "94E0"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0)); + assert_true(jsonctx->value[0] == '9'); + assert_true(jsonctx->value[1] == '4'); + assert_int_equal(2, jsonctx->value_len); + assert_int_equal(0, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "0E2"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0)); + assert_true(jsonctx->value[0] == '0'); + assert_int_equal(1, jsonctx->value_len); + assert_int_equal(0, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "-0E2"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0)); + assert_true(jsonctx->value[0] == '-'); + assert_true(jsonctx->value[1] == '0'); + assert_int_equal(2, jsonctx->value_len); + assert_int_equal(0, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "5.320e+2"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0)); + assert_string_equal("532", jsonctx->value); + assert_int_equal(3, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + str = "5.320e-1"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx)); + assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0)); + assert_string_equal("0.532", jsonctx->value); + assert_int_equal(5, jsonctx->value_len); + assert_int_equal(1, jsonctx->dynamic); + lyjson_ctx_free(jsonctx); + + /* various invalid inputs */ + str = "-x"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, 0, &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, 0, &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, 0, &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, 0, &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, 0, &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, 0, &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, 0, &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, 0, &jsonctx)); + CHECK_LOG_CTX("Unexpected end-of-input.", "Line number 1."); + + str = "1E1000"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx)); + CHECK_LOG_CTX("Number encoded as a string exceeded the LY_NUMBER_MAXLEN limit.", "Line number 1."); + + str = "1e9999999999999999999"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx)); + CHECK_LOG_CTX("Exponent out-of-bounds in a JSON Number value (1e9999999999999999999).", "Line number 1."); + + str = "1.1e66000"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx)); + CHECK_LOG_CTX("Exponent out-of-bounds in a JSON Number value (1.1e66000).", "Line number 1."); + + str = "1.1e-66000"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx)); + CHECK_LOG_CTX("Exponent out-of-bounds in a JSON Number value (1.1e-66000).", "Line number 1."); + + str = "-2.1e0."; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx)); + CHECK_LOG_CTX("Unexpected character \".\" after JSON number.", "Line number 1."); + + ly_in_free(in, 0); +} + +/* now string is tested in file ./tests/utests/types/string.c */ +static void +test_string(void **state) +{ + struct lyjson_ctx *jsonctx; + struct ly_in *in = NULL; + const char *str; + + str = ""; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in)); + +#if 0 + /* simple string */ + str = "\"hello\""; + assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &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, 0, &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, 0, &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, 0, &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, 0, &jsonctx)); + CHECK_LOG_CTX("Invalid character reference \"\\f\" (0x0000000c).", "Line number 1."); +#endif + + /* unterminated string */ + str = "\"unterminated string"; + assert_non_null(ly_in_memory(in, str)); + assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx)); + CHECK_LOG_CTX("Missing quotation-mark at the end of a JSON string.", "Line number 1."); +#if 0 + /* 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, 0, &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, 0, &jsonctx)); + CHECK_LOG_CTX("Invalid character in JSON string \"\n\" (0x0000000a).", "Line number 1."); +#endif + + 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, 0, &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, 0, &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, 0, &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, 0, &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, 0, &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, 0, &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, 0, &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, 0, &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, 0, &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_plugins.c b/tests/utests/basic/test_plugins.c new file mode 100644 index 0000000..7f3fe40 --- /dev/null +++ b/tests/utests/basic/test_plugins.c @@ -0,0 +1,109 @@ +/* + * @file test_plugins.c + * @author: Radek Krejci <rkrejci@cesnet.cz> + * @brief unit tests for functions from set.c + * + * Copyright (c) 2018 CESNET, z.s.p.o. + * + * This source code is licensed under BSD 3-Clause License (the "License"). + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + */ +#define _UTEST_MAIN_ +#include "utests.h" + +#include <stdlib.h> +#include <string.h> + +#include "config.h" +#include "plugins.h" +#include "plugins_internal.h" + +const char *simple = "module libyang-plugins-simple {" + " namespace urn:libyang:tests:plugins:simple;" + " prefix s;" + " typedef note { type string; }" + " extension hint { argument value; }" + " leaf test {" + " type s:note {length 255;}" + " s:hint \"some hint here\";" + " }" + "}"; + +static void +test_add_invalid(void **state) +{ + (void)state; + assert_int_equal(LY_ESYS, lyplg_add(TESTS_BIN "/plugins/plugin_does_not_exist" LYPLG_SUFFIX)); +} + +static void +test_add_simple(void **state) +{ + struct lys_module *mod; + struct lysc_node_leaf *leaf; + struct lyplg_ext_record *record_e; + struct lyplg_type *plugin_t; + + assert_int_equal(LY_SUCCESS, lyplg_add(TESTS_BIN "/plugins/plugin_simple" LYPLG_SUFFIX)); + + UTEST_ADD_MODULE(simple, LYS_IN_YANG, NULL, &mod); + + leaf = (struct lysc_node_leaf *)mod->compiled->data; + assert_int_equal(LYS_LEAF, leaf->nodetype); + + assert_non_null(plugin_t = lyplg_type_plugin_find("libyang-plugins-simple", NULL, "note")); + assert_string_equal("ly2 simple test v1", plugin_t->id); + assert_ptr_equal(leaf->type->plugin, plugin_t); + + assert_int_equal(1, LY_ARRAY_COUNT(leaf->exts)); + assert_non_null(record_e = lyplg_ext_record_find("libyang-plugins-simple", NULL, "hint")); + assert_string_equal("ly2 simple test v1", record_e->plugin.id); + assert_ptr_equal(leaf->exts[0].def->plugin, &record_e->plugin); + + /* the second loading of the same plugin - still success */ + assert_int_equal(LY_SUCCESS, lyplg_add(TESTS_BIN "/plugins/plugin_simple" LYPLG_SUFFIX)); +} + +static void +test_not_implemented(void **state) +{ + struct lys_module *mod; + struct lyd_node *tree; + const char *schema = "module libyang-plugins-unknown {" + " namespace urn:libyang:tests:plugins:unknown;" + " prefix u;" + " extension myext;" + " typedef mytype { type string;}" + " leaf test {" + " u:myext;" + " type mytype;" + " }" + "}"; + const char *data = "<test xmlns=\"urn:libyang:tests:plugins:unknown\">xxx</test>"; + char *printed = NULL; + + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG_COMPILED, 0)); + free(printed); + + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, 0, LYD_VALIDATE_PRESENT, &tree)); + CHECK_LOG_CTX(NULL, NULL); + + lyd_free_all(tree); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_add_invalid), + UTEST(test_add_simple), + UTEST(test_not_implemented), + }; + + 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..af39afa --- /dev/null +++ b/tests/utests/basic/test_set.c @@ -0,0 +1,287 @@ +/* + * @file test_set.c + * @author: Radek Krejci <rkrejci@cesnet.cz> + * @brief unit tests for functions from set.c + * + * Copyright (c) 2018 CESNET, z.s.p.o. + * + * This source code is licensed under BSD 3-Clause License (the "License"). + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + */ +#define _POSIX_C_SOURCE 200809L /* strdup */ +#define _UTEST_MAIN_ +#include "utests.h" + +#include <stdlib.h> +#include <string.h> + +#include "set.h" + +static void +test_basics(void **UNUSED(state)) +{ + struct ly_set *set; + char *str; + unsigned int u; + void *ptr; + uint32_t index; + + /* creation - everything is empty */ + assert_int_equal(LY_SUCCESS, ly_set_new(&set)); + assert_non_null(set); + assert_int_equal(0, set->count); + assert_int_equal(0, set->size); + assert_null(set->objs); + + /* add a testing object */ + str = strdup("test string"); + assert_non_null(str); + + assert_int_equal(LY_SUCCESS, ly_set_add(set, str, 0, NULL)); + assert_int_not_equal(0, set->size); + assert_int_equal(1, set->count); + assert_non_null(set->objs); + assert_non_null(set->objs[0]); + + /* check the presence of the testing data */ + assert_int_equal(1, ly_set_contains(set, str, &index)); + assert_int_equal(0, index); + assert_int_equal(0, ly_set_contains(set, str - 1, NULL)); + + /* remove data, but keep the set */ + u = set->size; + ptr = set->objs; + ly_set_clean(set, free); + assert_int_equal(0, set->count); + assert_int_equal(u, set->size); + assert_ptr_equal(ptr, set->objs); + + /* remove buffer, but keep the set object */ + ly_set_erase(set, NULL); + assert_int_equal(0, set->count); + assert_int_equal(0, set->size); + assert_ptr_equal(NULL, set->objs); + + /* final cleanup */ + ly_set_free(set, NULL); +} + +static void +test_inval(void **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 *(*)(const void *))strdup, &new)); + assert_non_null(new); + assert_ptr_not_equal(orig, new); + assert_int_equal(orig->count, new->count); + assert_ptr_not_equal(orig->objs[0], new->objs[0]); + assert_string_equal(orig->objs[0], new->objs[0]); + + /* cleanup */ + ly_set_free(new, free); + ly_set_free(orig, free); +} + +static void +test_add(void **UNUSED(state)) +{ + uint32_t u, index; + char *str = "test string"; + struct ly_set set; + + memset(&set, 0, sizeof set); + + /* add a testing object */ + assert_int_equal(LY_SUCCESS, ly_set_add(&set, str, 0, &index)); + assert_int_equal(0, index); + + /* test avoiding data duplicities */ + assert_int_equal(LY_SUCCESS, ly_set_add(&set, str, 0, &index)); + assert_int_equal(0, index); + assert_int_equal(1, set.count); + assert_int_equal(LY_SUCCESS, ly_set_add(&set, str, 1, &index)); + assert_int_equal(1, index); + assert_int_equal(2, set.count); + + /* test array resizing */ + u = set.size; + for (uint32_t expected_index = 2; expected_index <= u; ++expected_index) { + assert_int_equal(LY_SUCCESS, ly_set_add(&set, str, 1, &index)); + assert_int_equal(expected_index, index); + } + assert_true(u != set.size); + + /* cleanup */ + ly_set_erase(&set, NULL); +} + +static void +test_merge(void **UNUSED(state)) +{ + char *str1, *str2; + struct ly_set one, two; + + memset(&one, 0, sizeof one); + memset(&two, 0, sizeof two); + + str1 = strdup("string1"); + str2 = strdup("string2"); + + /* fill first set + * - str1 is the same as in two, so it must not be freed! */ + assert_int_equal(LY_SUCCESS, ly_set_add(&one, str1, 0, NULL)); + + /* fill second set */ + assert_int_equal(LY_SUCCESS, ly_set_add(&two, str1, 0, NULL)); + assert_int_equal(LY_SUCCESS, ly_set_add(&two, str2, 0, NULL)); + + /* merge with checking duplicities - only one item is added into one; + * also without duplicating data, so it must not be freed at the end */ + assert_int_equal(LY_SUCCESS, ly_set_merge(&one, &two, 0, NULL)); + assert_int_equal(2, one.count); + assert_ptr_equal(one.objs[1], two.objs[1]); + + /* clean and re-fill one (now duplicating str1, to allow testing duplicator) */ + ly_set_clean(&one, NULL); + assert_int_equal(LY_SUCCESS, ly_set_add(&one, strdup(str1), 0, NULL)); + + /* merge without checking duplicities - two items are added into one; + * here also with duplicator */ + assert_int_equal(LY_SUCCESS, ly_set_merge(&one, &two, 1, (void *(*)(const void *))strdup)); + assert_int_equal(3, one.count); + assert_ptr_not_equal(one.objs[1], two.objs[0]); + assert_string_equal(one.objs[1], two.objs[0]); + + /* cleanup */ + ly_set_erase(&one, free); + ly_set_erase(&two, free); +} + +static void +test_rm(void **UNUSED(state)) +{ + char *str1, *str2, *str3; + struct ly_set set; + + memset(&set, 0, sizeof set); + + /* fill the set */ + assert_int_equal(LY_SUCCESS, ly_set_add(&set, "string1", 0, NULL)); + assert_int_equal(LY_SUCCESS, ly_set_add(&set, strdup("string2"), 0, NULL)); + assert_int_equal(LY_SUCCESS, ly_set_add(&set, "string3", 0, NULL)); + + /* remove by index ... */ + /* ... in the middle ... */ + assert_int_equal(LY_SUCCESS, ly_set_rm_index(&set, 1, free)); + assert_int_equal(2, set.count); + assert_string_not_equal("string2", set.objs[0]); + assert_string_not_equal("string2", set.objs[1]); + /* ... last .. */ + assert_int_equal(LY_SUCCESS, ly_set_rm_index(&set, 1, NULL)); + assert_int_equal(1, set.count); + assert_string_not_equal("string3", set.objs[0]); + /* ... first .. */ + assert_int_equal(LY_SUCCESS, ly_set_rm_index(&set, 0, NULL)); + assert_int_equal(0, set.count); + + /* fill the set */ + assert_int_equal(LY_SUCCESS, ly_set_add(&set, str1 = "string1", 0, NULL)); + assert_int_equal(LY_SUCCESS, ly_set_add(&set, str2 = "string2", 0, NULL)); + assert_int_equal(LY_SUCCESS, ly_set_add(&set, str3 = strdup("string3"), 0, NULL)); + + /* remove by pointer ... */ + /* ... in the middle ... */ + assert_int_equal(LY_SUCCESS, ly_set_rm(&set, str2, NULL)); + assert_int_equal(2, set.count); + assert_string_not_equal("string2", set.objs[0]); + assert_string_not_equal("string2", set.objs[1]); + /* ... last (with destructor) .. */ + assert_int_equal(LY_SUCCESS, ly_set_rm(&set, str3, free)); + assert_int_equal(1, set.count); + assert_string_not_equal("string3", set.objs[0]); + /* ... first .. */ + assert_int_equal(LY_SUCCESS, ly_set_rm(&set, str1, NULL)); + assert_int_equal(0, set.count); + + /* cleanup */ + ly_set_erase(&set, NULL); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_basics), + UTEST(test_duplication), + UTEST(test_add), + UTEST(test_merge), + UTEST(test_rm), + UTEST(test_inval), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/basic/test_xml.c b/tests/utests/basic/test_xml.c new file mode 100644 index 0000000..668de4b --- /dev/null +++ b/tests/utests/basic/test_xml.c @@ -0,0 +1,690 @@ +/* + * @file test_xml.c + * @author: Radek Krejci <rkrejci@cesnet.cz> + * @brief unit tests for functions from xml.c + * + * Copyright (c) 2018 CESNET, z.s.p.o. + * + * This source code is licensed under BSD 3-Clause License (the "License"). + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + */ +#define _UTEST_MAIN_ +#include "utests.h" + +#define _POSIX_C_SOURCE 200809L /* strdup */ + +#include <string.h> + +#include "context.h" +#include "in_internal.h" +#include "xml.h" + +LY_ERR lyxml_ns_add(struct lyxml_ctx *xmlctx, const char *prefix, size_t prefix_len, char *uri); + +static void +test_element(void **state) +{ + struct lyxml_ctx *xmlctx; + struct ly_in *in; + const char *str; + + /* empty */ + str = ""; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in)); + assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx)); + assert_int_equal(LYXML_END, xmlctx->status); + lyxml_ctx_free(xmlctx); + ly_in_free(in, 0); + + /* end element */ + str = "</element>"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in)); + assert_int_equal(LY_EVALID, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx)); + CHECK_LOG_CTX("Stray closing element tag (\"element\").", "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); + + /* namespace ambiguity */ + str = "<element xmlns=\"urn1\" xmlns=\"urn2\"/>"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in)); + assert_int_equal(LY_EVALID, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx)); + CHECK_LOG_CTX("Duplicate default XML namespaces \"urn1\" and \"urn2\".", "Line number 1."); + ly_in_free(in, 0); + + /* prefix duplicate */ + str = "<element xmlns:a=\"urn1\" xmlns:a=\"urn2\"/>"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in)); + assert_int_equal(LY_EVALID, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx)); + CHECK_LOG_CTX("Duplicate XML NS prefix \"a\" used for namespaces \"urn1\" and \"urn2\".", "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 --> <?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->value, xmlctx->value_len)); + assert_int_equal(xmlctx->ws_only, 0); + assert_int_equal(xmlctx->dynamic, 0); + lyxml_ctx_free(xmlctx); + ly_in_free(in, 0); + + /* valid namespace with prefix */ + str = "<e xmlns:nc\n = \'urn\'/>"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in)); + assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx)); + assert_int_equal(LYXML_ELEMENT, xmlctx->status); + assert_int_equal(1, xmlctx->ns.count); + ns = (struct lyxml_ns *)xmlctx->ns.objs[0]; + assert_string_equal(ns->prefix, "nc"); + assert_string_equal(ns->uri, "urn"); + lyxml_ctx_free(xmlctx); + ly_in_free(in, 0); +} + +static void +test_text(void **state) +{ + const char *str; + struct lyxml_ctx *xmlctx; + struct ly_in *in; + + /* empty attribute value */ + str = "<e a=\"\""; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in)); + assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx)); + assert_int_equal(LYXML_ELEMENT, xmlctx->status); + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_ATTRIBUTE, xmlctx->status); + + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_ATTR_CONTENT, xmlctx->status); + assert_true(!strncmp("", xmlctx->value, xmlctx->value_len)); + assert_int_equal(xmlctx->ws_only, 1); + assert_int_equal(xmlctx->dynamic, 0); + ly_in_free(in, 0); + + /* empty value but in single quotes */ + assert_int_equal(LY_SUCCESS, ly_in_new_memory("=\'\'", &in)); + xmlctx->in = in; + LOG_LOCSET(NULL, NULL, NULL, in); + xmlctx->status = LYXML_ATTRIBUTE; + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_ATTR_CONTENT, xmlctx->status); + assert_true(!strncmp("", xmlctx->value, xmlctx->value_len)); + assert_int_equal(xmlctx->ws_only, 1); + assert_int_equal(xmlctx->dynamic, 0); + ly_in_free(in, 0); + + /* empty element content - only formating before defining child */ + assert_int_equal(LY_SUCCESS, ly_in_new_memory(">\n <y>", &in)); + xmlctx->in = in; + LOG_LOCSET(NULL, NULL, NULL, in); + xmlctx->status = LYXML_ELEMENT; + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_ELEM_CONTENT, xmlctx->status); + assert_true(!strncmp("\n ", xmlctx->value, xmlctx->value_len)); + assert_int_equal(xmlctx->ws_only, 1); + assert_int_equal(xmlctx->dynamic, 0); + ly_in_free(in, 0); + + /* empty element content is invalid - missing content terminating character < */ + assert_int_equal(LY_SUCCESS, ly_in_new_memory("", &in)); + xmlctx->in = in; + LOG_LOCSET(NULL, NULL, NULL, in); + xmlctx->status = LYXML_ELEM_CONTENT; + assert_int_equal(LY_EVALID, lyxml_ctx_next(xmlctx)); + CHECK_LOG_CTX("Unexpected end-of-input.", "Line number 1."); + ly_in_free(in, 0); + + assert_int_equal(LY_SUCCESS, ly_in_new_memory("xxx", &in)); + xmlctx->in = in; + LOG_LOCSET(NULL, NULL, NULL, in); + xmlctx->status = LYXML_ELEM_CONTENT; + assert_int_equal(LY_EVALID, lyxml_ctx_next(xmlctx)); + CHECK_LOG_CTX("Invalid character sequence \"xxx\", expected element tag start ('<').", "Line number 1."); + ly_in_free(in, 0); + + lyxml_ctx_free(xmlctx); + LOG_LOCBACK(0, 0, 0, 4); + + /* 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; + LOG_LOCSET(NULL, NULL, NULL, in); + xmlctx->status = LYXML_ATTRIBUTE; + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_ATTR_CONTENT, xmlctx->status); + assert_true(!strncmp("$¢€𐍈", xmlctx->value, xmlctx->value_len)); + assert_int_equal(xmlctx->ws_only, 0); + assert_int_equal(xmlctx->dynamic, 1); + ly_in_free(in, 0); + + /* CDATA value */ + assert_int_equal(LY_SUCCESS, ly_in_new_memory("> <![CDATA[ special non-escaped chars <>&\"' ]]> </a>", &in)); + xmlctx->in = in; + LOG_LOCSET(NULL, NULL, NULL, in); + xmlctx->status = LYXML_ATTR_CONTENT; + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_ELEM_CONTENT, xmlctx->status); + assert_true(!strncmp(" special non-escaped chars <>&\"' ", xmlctx->value, xmlctx->value_len)); + assert_int_equal(xmlctx->ws_only, 0); + assert_int_equal(xmlctx->dynamic, 1); + free((char *)xmlctx->value); + ly_in_free(in, 0); + + /* invalid characters in string */ + assert_int_equal(LY_SUCCESS, ly_in_new_memory("=\'R\'", &in)); + xmlctx->in = in; + LOG_LOCSET(NULL, NULL, NULL, in); + xmlctx->status = LYXML_ATTRIBUTE; + assert_int_equal(LY_EVALID, lyxml_ctx_next(xmlctx)); + CHECK_LOG_CTX("Invalid character sequence \"'\", expected ;.", "Line number 1."); + ly_in_free(in, 0); + + assert_int_equal(LY_SUCCESS, ly_in_new_memory("=\"R\"", &in)); + xmlctx->in = in; + LOG_LOCSET(NULL, NULL, NULL, in); + xmlctx->status = LYXML_ATTRIBUTE; + assert_int_equal(LY_EVALID, lyxml_ctx_next(xmlctx)); + CHECK_LOG_CTX("Invalid character sequence \"\"\", expected ;.", "Line number 1."); + ly_in_free(in, 0); + + assert_int_equal(LY_SUCCESS, ly_in_new_memory("=\"&nonsense;\"", &in)); + xmlctx->in = in; + LOG_LOCSET(NULL, NULL, NULL, in); + xmlctx->status = LYXML_ATTRIBUTE; + assert_int_equal(LY_EVALID, lyxml_ctx_next(xmlctx)); + CHECK_LOG_CTX("Entity reference \"&nonsense;\" not supported, only predefined references allowed.", "Line number 1."); + ly_in_free(in, 0); + + assert_int_equal(LY_SUCCESS, ly_in_new_memory(">&#o122;", &in)); + xmlctx->in = in; + LOG_LOCSET(NULL, NULL, NULL, in); + xmlctx->status = LYXML_ELEMENT; + assert_int_equal(LY_EVALID, lyxml_ctx_next(xmlctx)); + CHECK_LOG_CTX("Invalid character reference \"&#o122;\".", "Line number 1."); + ly_in_free(in, 0); + + assert_int_equal(LY_SUCCESS, ly_in_new_memory("=\'\'", &in)); + xmlctx->in = in; + LOG_LOCSET(NULL, NULL, NULL, in); + xmlctx->status = LYXML_ATTRIBUTE; + assert_int_equal(LY_EVALID, lyxml_ctx_next(xmlctx)); + CHECK_LOG_CTX("Invalid character reference \"\'\" (0x00000006).", "Line number 1."); + ly_in_free(in, 0); + + assert_int_equal(LY_SUCCESS, ly_in_new_memory("=\'\'", &in)); + xmlctx->in = in; + LOG_LOCSET(NULL, NULL, NULL, in); + xmlctx->status = LYXML_ATTRIBUTE; + assert_int_equal(LY_EVALID, lyxml_ctx_next(xmlctx)); + CHECK_LOG_CTX("Invalid character reference \"\'\" (0x0000fdd0).", "Line number 1."); + ly_in_free(in, 0); + + assert_int_equal(LY_SUCCESS, ly_in_new_memory("=\'\'", &in)); + xmlctx->in = in; + LOG_LOCSET(NULL, NULL, NULL, in); + xmlctx->status = LYXML_ATTRIBUTE; + assert_int_equal(LY_EVALID, lyxml_ctx_next(xmlctx)); + CHECK_LOG_CTX("Invalid character reference \"\'\" (0x0000ffff).", "Line number 1."); + ly_in_free(in, 0); + + lyxml_ctx_free(xmlctx); + LOG_LOCBACK(0, 0, 0, 9); +} + +static void +test_ns(void **state) +{ + const char *str; + struct lyxml_ctx *xmlctx; + struct ly_in *in; + const struct lyxml_ns *ns; + + /* opening element1 */ + str = "<element1/>"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in)); + assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx)); + + /* processing namespace definitions */ + assert_int_equal(LY_SUCCESS, lyxml_ns_add(xmlctx, NULL, 0, strdup("urn:default"))); + assert_int_equal(LY_SUCCESS, lyxml_ns_add(xmlctx, "nc", 2, strdup("urn:nc1"))); + /* simulate adding open element2 into context */ + xmlctx->elements.count++; + /* processing namespace definitions */ + assert_int_equal(LY_SUCCESS, lyxml_ns_add(xmlctx, "nc", 2, strdup("urn:nc2"))); + assert_int_equal(3, xmlctx->ns.count); + assert_int_not_equal(0, xmlctx->ns.size); + + ns = lyxml_ns_get(&xmlctx->ns, NULL, 0); + assert_non_null(ns); + assert_null(ns->prefix); + assert_string_equal("urn:default", ns->uri); + + ns = lyxml_ns_get(&xmlctx->ns, "nc", 2); + assert_non_null(ns); + assert_string_equal("nc", ns->prefix); + assert_string_equal("urn:nc2", ns->uri); + + /* simulate closing element2 */ + xmlctx->elements.count--; + lyxml_ns_rm(xmlctx); + assert_int_equal(2, xmlctx->ns.count); + + ns = lyxml_ns_get(&xmlctx->ns, "nc", 2); + assert_non_null(ns); + assert_string_equal("nc", ns->prefix); + assert_string_equal("urn:nc1", ns->uri); + + /* close element1 */ + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(0, xmlctx->ns.count); + + assert_null(lyxml_ns_get(&xmlctx->ns, "nc", 2)); + assert_null(lyxml_ns_get(&xmlctx->ns, NULL, 0)); + + lyxml_ctx_free(xmlctx); + ly_in_free(in, 0); +} + +static void +test_ns2(void **state) +{ + const char *str; + struct lyxml_ctx *xmlctx; + struct ly_in *in; + + /* opening element1 */ + str = "<element1/>"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in)); + assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx)); + + /* default namespace defined in parent element1 */ + assert_int_equal(LY_SUCCESS, lyxml_ns_add(xmlctx, NULL, 0, strdup("urn:default"))); + assert_int_equal(1, xmlctx->ns.count); + /* going into child element1 */ + /* simulate adding open element1 into context */ + xmlctx->elements.count++; + /* no namespace defined, going out (first, simulate closing of so far open element) */ + xmlctx->elements.count--; + lyxml_ns_rm(xmlctx); + assert_int_equal(1, xmlctx->ns.count); + + /* nothing else, going out of the parent element1 */ + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(0, xmlctx->ns.count); + + lyxml_ctx_free(xmlctx); + ly_in_free(in, 0); +} + +static void +test_simple_xml(void **state) +{ + struct lyxml_ctx *xmlctx; + struct ly_in *in; + const char *test_input = "<elem1 attr1=\"value\"> <elem2 attr2=\"value\" /> </elem1>"; + + assert_int_equal(LY_SUCCESS, ly_in_new_memory(test_input, &in)); + assert_int_equal(LY_SUCCESS, lyxml_ctx_new(UTEST_LYCTX, in, &xmlctx)); + assert_int_equal(LYXML_ELEMENT, xmlctx->status); + assert_string_equal(xmlctx->in->current, "attr1=\"value\"> <elem2 attr2=\"value\" /> </elem1>"); + + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_ATTRIBUTE, xmlctx->status); + assert_string_equal(xmlctx->in->current, "=\"value\"> <elem2 attr2=\"value\" /> </elem1>"); + + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_ATTR_CONTENT, xmlctx->status); + assert_string_equal(xmlctx->in->current, "> <elem2 attr2=\"value\" /> </elem1>"); + + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_ELEM_CONTENT, xmlctx->status); + assert_string_equal(xmlctx->in->current, "<elem2 attr2=\"value\" /> </elem1>"); + + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_ELEMENT, xmlctx->status); + assert_string_equal(xmlctx->in->current, "attr2=\"value\" /> </elem1>"); + + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_ATTRIBUTE, xmlctx->status); + assert_string_equal(xmlctx->in->current, "=\"value\" /> </elem1>"); + + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_ATTR_CONTENT, xmlctx->status); + assert_string_equal(xmlctx->in->current, " /> </elem1>"); + + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_ELEM_CONTENT, xmlctx->status); + assert_string_equal(xmlctx->in->current, "/> </elem1>"); + + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_ELEM_CLOSE, xmlctx->status); + assert_string_equal(xmlctx->in->current, " </elem1>"); + + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_ELEM_CLOSE, xmlctx->status); + assert_string_equal(xmlctx->in->current, ""); + + assert_int_equal(LY_SUCCESS, lyxml_ctx_next(xmlctx)); + assert_int_equal(LYXML_END, xmlctx->status); + assert_string_equal(xmlctx->in->current, ""); + + lyxml_ctx_free(xmlctx); + ly_in_free(in, 0); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_element), + UTEST(test_attribute), + UTEST(test_text), + UTEST(test_ns), + UTEST(test_ns2), + UTEST(test_simple_xml), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/basic/test_xpath.c b/tests/utests/basic/test_xpath.c new file mode 100644 index 0000000..b388dc3 --- /dev/null +++ b/tests/utests/basic/test_xpath.c @@ -0,0 +1,1071 @@ +/** + * @file test_xpath.c + * @author: Michal Vasko <mvasko@cesnet.cz> + * @brief unit tests for XPath evaluation + * + * Copyright (c) 2020 CESNET, z.s.p.o. + * + * This source code is licensed under BSD 3-Clause License (the "License"). + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + */ +#define _UTEST_MAIN_ +#include "utests.h" + +#include <string.h> + +#include "context.h" +#include "parser_data.h" +#include "set.h" +#include "tests_config.h" +#include "tree_data.h" +#include "tree_schema.h" + +const char *schema_a = + "module a {\n" + " namespace urn:tests:a;\n" + " prefix a;\n" + " yang-version 1.1;\n" + "\n" + " identity id_a;\n" + " identity id_b {\n" + " base id_a;\n" + " }\n" + " identity id_c {\n" + " base id_b;\n" + " }\n" + "\n" + " list l1 {\n" + " key \"a b\";\n" + " leaf a {\n" + " type string;\n" + " }\n" + " leaf b {\n" + " type string;\n" + " }\n" + " leaf c {\n" + " type string;\n" + " }\n" + " }\n" + " leaf foo {\n" + " type string;\n" + " }\n" + " leaf foo2 {\n" + " type uint8;\n" + " }\n" + " leaf foo3 {\n" + " type identityref {\n" + " base id_a;\n" + " }\n" + " }\n" + " leaf foo4 {\n" + " type decimal64 {\n" + " fraction-digits 5;\n" + " }\n" + " }\n" + " container c {\n" + " leaf x {\n" + " type string;\n" + " }\n" + " list ll {\n" + " key \"a\";\n" + " leaf a {\n" + " type string;\n" + " }\n" + " list ll {\n" + " key \"a\";\n" + " leaf a {\n" + " type string;\n" + " }\n" + " leaf b {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + " leaf-list ll2 {\n" + " type string;\n" + " }\n" + " }\n" + "}"; + +static int +setup(void **state) +{ + UTEST_SETUP; + + UTEST_ADD_MODULE(schema_a, LYS_IN_YANG, NULL, NULL); + lys_parse_path(UTEST_LYCTX, TESTS_DIR_MODULES_YANG "/ietf-interfaces@2014-05-08.yang", LYS_IN_YANG, NULL); + + return 0; +} + +static void +test_predicate(void **state) +{ + const char *data; + struct lyd_node *tree; + struct ly_set *set; + + data = + "<foo2 xmlns=\"urn:tests:a\">50</foo2>" + "<l1 xmlns=\"urn:tests:a\">" + " <a>a1</a>" + " <b>b1</b>" + " <c>c1</c>" + "</l1>" + "<l1 xmlns=\"urn:tests:a\">" + " <a>a2</a>" + " <b>b2</b>" + "</l1>" + "<l1 xmlns=\"urn:tests:a\">" + " <a>a3</a>" + " <b>b3</b>" + "</l1>" + "<l1 xmlns=\"urn:tests:a\">" + " <a>a4</a>" + " <b>b4</b>" + " <c>c4</c>" + "</l1>" + "<l1 xmlns=\"urn:tests:a\">" + " <a>a5</a>" + " <b>b5</b>" + " <c>c5</c>" + "</l1>" + "<c xmlns=\"urn:tests:a\">" + " <x>key2</x>" + " <ll>" + " <a>key1</a>" + " <ll>" + " <a>key11</a>" + " <b>val11</b>" + " </ll>" + " <ll>" + " <a>key12</a>" + " <b>val12</b>" + " </ll>" + " <ll>" + " <a>key13</a>" + " <b>val13</b>" + " </ll>" + " </ll>" + " <ll>" + " <a>key2</a>" + " <ll>" + " <a>key21</a>" + " <b>val21</b>" + " </ll>" + " <ll>" + " <a>key22</a>" + " <b>val22</b>" + " </ll>" + " </ll>" + " <ll>" + " <a>key3</a>" + " <ll>" + " <a>key31</a>" + " <b>val31</b>" + " </ll>" + " <ll>" + " <a>key32</a>" + " <b>val32</b>" + " </ll>" + " </ll>" + "</c>"; + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree)); + assert_non_null(tree); + + /* predicate after number */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/foo2[4[3 = 3]]", &set)); + assert_int_equal(0, set->count); + ly_set_free(set, NULL); + + /* reverse axis */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/c/child::ll[2]/preceding::ll[3]", &set)); + assert_int_equal(1, set->count); + assert_string_equal("key11", lyd_get_value(lyd_child(set->dnodes[0]))); + ly_set_free(set, NULL); + + /* special predicate evaluated using hashes */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:l1[a=concat('a', '1')][b=substring('ab1',2)]", &set)); + assert_int_equal(1, set->count); + ly_set_free(set, NULL); + + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:c/ll[a=../x]", &set)); + assert_int_equal(1, set->count); + ly_set_free(set, NULL); + + /* cannot use hashes */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:c/ll[a=substring(ll/a,1,4)]", &set)); + assert_int_equal(3, set->count); + ly_set_free(set, NULL); + + /* nested predicate */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:c/a:ll[a:a=string(/a:l1[a:a='foo']/a:a)]/a:a", &set)); + assert_int_equal(0, set->count); + ly_set_free(set, NULL); + + lyd_free_all(tree); +} + +static void +test_union(void **state) +{ + const char *data; + struct lyd_node *tree; + struct ly_set *set; + + data = + "<l1 xmlns=\"urn:tests:a\">\n" + " <a>a1</a>\n" + " <b>b1</b>\n" + " <c>c1</c>\n" + "</l1>\n" + "<l1 xmlns=\"urn:tests:a\">\n" + " <a>a2</a>\n" + " <b>b2</b>\n" + "</l1>" + "<l1 xmlns=\"urn:tests:a\">\n" + " <a>a3</a>\n" + " <b>b3</b>\n" + " <c>c3</c>\n" + "</l1>"; + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree)); + assert_non_null(tree); + + /* Predicate for operand. */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/l1[c[../a = 'a1'] | c]/a", &set)); + ly_set_free(set, NULL); + + lyd_free_all(tree); +} + +static void +test_invalid(void **state) +{ + const char *data = + "<foo2 xmlns=\"urn:tests:a\">50</foo2>"; + struct lyd_node *tree; + struct ly_set *set; + + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree)); + assert_non_null(tree); + + assert_int_equal(LY_EVALID, lyd_find_xpath(tree, "/a:foo2[.=]", &set)); + assert_null(set); + + assert_int_equal(LY_EVALID, lyd_find_xpath(tree, "/a:", &set)); + assert_null(set); + + lyd_free_all(tree); +} + +static void +test_hash(void **state) +{ + const char *data = + "<l1 xmlns=\"urn:tests:a\">\n" + " <a>a1</a>\n" + " <b>b1</b>\n" + " <c>c1</c>\n" + "</l1>\n" + "<l1 xmlns=\"urn:tests:a\">\n" + " <a>a2</a>\n" + " <b>b2</b>\n" + "</l1>\n" + "<l1 xmlns=\"urn:tests:a\">\n" + " <a>a3</a>\n" + " <b>b3</b>\n" + " <c>c3</c>\n" + "</l1>\n" + "<foo xmlns=\"urn:tests:a\">foo value</foo>\n" + "<c xmlns=\"urn:tests:a\">\n" + " <x>val</x>\n" + " <ll>\n" + " <a>val_a</a>\n" + " <ll>\n" + " <a>val_a</a>\n" + " <b>val</b>\n" + " </ll>\n" + " <ll>\n" + " <a>val_b</a>\n" + " </ll>\n" + " </ll>\n" + " <ll>\n" + " <a>val_b</a>\n" + " <ll>\n" + " <a>val_a</a>\n" + " </ll>\n" + " <ll>\n" + " <a>val_b</a>\n" + " <b>val</b>\n" + " </ll>\n" + " </ll>\n" + " <ll>\n" + " <a>val_c</a>\n" + " <ll>\n" + " <a>val_a</a>\n" + " </ll>\n" + " <ll>\n" + " <a>val_b</a>\n" + " </ll>\n" + " </ll>\n" + " <ll2>one</ll2>\n" + " <ll2>two</ll2>\n" + " <ll2>three</ll2>\n" + " <ll2>four</ll2>\n" + "</c>"; + struct lyd_node *tree, *node; + struct ly_set *set; + + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree)); + assert_non_null(tree); + + /* top-level, so hash table is not ultimately used but instances can be compared based on hashes */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:l1[a='a3'][b='b3']", &set)); + assert_int_equal(1, set->count); + + node = set->objs[0]; + assert_string_equal(node->schema->name, "l1"); + node = lyd_child(node); + assert_string_equal(node->schema->name, "a"); + assert_string_equal(lyd_get_value(node), "a3"); + + ly_set_free(set, NULL); + + /* hashes should be used for both searches (well, there are not enough nested ll instances, so technically not true) */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:c/ll[a='val_b']/ll[a='val_b']", &set)); + assert_int_equal(1, set->count); + + node = set->objs[0]; + assert_string_equal(node->schema->name, "ll"); + node = lyd_child(node); + assert_string_equal(node->schema->name, "a"); + assert_string_equal(lyd_get_value(node), "val_b"); + node = node->next; + assert_string_equal(node->schema->name, "b"); + assert_null(node->next); + + ly_set_free(set, NULL); + + /* hashes are not used */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:c//ll[a='val_b']", &set)); + assert_int_equal(4, set->count); + + ly_set_free(set, NULL); + + /* hashes used even for leaf-lists */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:c/ll2[. = 'three']", &set)); + assert_int_equal(1, set->count); + + node = set->objs[0]; + assert_string_equal(node->schema->name, "ll2"); + assert_string_equal(lyd_get_value(node), "three"); + + ly_set_free(set, NULL); + + /* not found using hashes */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:c/ll[a='val_d']", &set)); + assert_int_equal(0, set->count); + + ly_set_free(set, NULL); + + /* white-spaces are also ok */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:c/ll[ \na = 'val_c' ]", &set)); + assert_int_equal(1, set->count); + + ly_set_free(set, NULL); + + lyd_free_all(tree); +} + +static void +test_toplevel(void **state) +{ + const char *schema_b = + "module b {\n" + " namespace urn:tests:b;\n" + " prefix b;\n" + " yang-version 1.1;\n" + "\n" + " list l2 {\n" + " key \"a\";\n" + " leaf a {\n" + " type uint16;\n" + " }\n" + " leaf b {\n" + " type uint16;\n" + " }\n" + " }\n" + "}"; + const char *data = + "<l1 xmlns=\"urn:tests:a\">\n" + " <a>a1</a>\n" + " <b>b1</b>\n" + " <c>c1</c>\n" + "</l1>\n" + "<l1 xmlns=\"urn:tests:a\">\n" + " <a>a2</a>\n" + " <b>b2</b>\n" + "</l1>\n" + "<l1 xmlns=\"urn:tests:a\">\n" + " <a>a3</a>\n" + " <b>b3</b>\n" + " <c>c3</c>\n" + "</l1>\n" + "<foo xmlns=\"urn:tests:a\">foo value</foo>\n" + "<l2 xmlns=\"urn:tests:b\">\n" + " <a>1</a>\n" + " <b>1</b>\n" + "</l2>\n" + "<l2 xmlns=\"urn:tests:b\">\n" + " <a>2</a>\n" + " <b>1</b>\n" + "</l2>\n" + "<l2 xmlns=\"urn:tests:b\">\n" + " <a>3</a>\n" + " <b>1</b>\n" + "</l2>"; + struct lyd_node *tree; + struct ly_set *set; + + UTEST_ADD_MODULE(schema_b, LYS_IN_YANG, NULL, NULL); + + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree)); + assert_non_null(tree); + + /* all top-level nodes from one module (default container as well) */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:*", &set)); + assert_int_equal(5, set->count); + + ly_set_free(set, NULL); + + /* all top-level nodes from all modules */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/*", &set)); + assert_int_equal(8, set->count); + + ly_set_free(set, NULL); + + /* all nodes from one module */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "//a:*", &set)); + assert_int_equal(13, set->count); + + ly_set_free(set, NULL); + + /* all nodes from all modules */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "//*", &set)); + assert_int_equal(22, set->count); + + ly_set_free(set, NULL); + + /* all nodes from all modules #2 */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "//.", &set)); + assert_int_equal(22, set->count); + + ly_set_free(set, NULL); + + lyd_free_all(tree); +} + +static void +test_atomize(void **state) +{ + struct ly_set *set; + const struct lys_module *mod; + + mod = ly_ctx_get_module_latest(UTEST_LYCTX, "a"); + assert_non_null(mod); + + /* some random paths just making sure the API function works */ + assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/a:*", 0, &set)); + assert_int_equal(6, set->count); + ly_set_free(set, NULL); + + /* all nodes from all modules (including internal, which can change easily, so check just the test modules) */ + assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "//.", 0, &set)); + assert_in_range(set->count, 17, UINT32_MAX); + ly_set_free(set, NULL); + + assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/a:c/ll[a='val1']/ll[a='val2']/b", 0, &set)); + assert_int_equal(6, set->count); + ly_set_free(set, NULL); + + assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/ietf-interfaces:interfaces/*", 0, &set)); + assert_int_equal(2, set->count); + ly_set_free(set, NULL); + + assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/*", 0, &set)); + assert_int_equal(13, set->count); + ly_set_free(set, NULL); + + /* + * axes + */ + + /* ancestor */ + assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "//ll[a and b]/a/ancestor::node()", 0, &set)); + assert_int_equal(6, set->count); + ly_set_free(set, NULL); + + /* ancestor-or-self */ + assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "//ll[a and b]/ancestor-or-self::ll", 0, &set)); + assert_int_equal(5, set->count); + ly_set_free(set, NULL); + + /* attribute */ + assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/l1/attribute::key", 0, &set)); + assert_int_equal(1, set->count); + ly_set_free(set, NULL); + + /* child */ + assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/child::l1/child::a", 0, &set)); + assert_int_equal(2, set->count); + ly_set_free(set, NULL); + + /* descendant */ + assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/descendant::c/descendant::b", 0, &set)); + assert_int_equal(3, set->count); + ly_set_free(set, NULL); + + /* descendant-or-self */ + assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/a:*/descendant-or-self::c", 0, &set)); + assert_int_equal(7, set->count); + ly_set_free(set, NULL); + + /* following */ + assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/c/x/following::a", 0, &set)); + assert_int_equal(4, set->count); + ly_set_free(set, NULL); + + /* following-sibling */ + assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/c/x/following-sibling::ll", 0, &set)); + assert_int_equal(3, set->count); + ly_set_free(set, NULL); + + /* parent */ + assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/child::a:*/c/parent::l1", 0, &set)); + assert_int_equal(7, set->count); + ly_set_free(set, NULL); + + assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/child::a:c//..", 0, &set)); + assert_int_equal(8, set->count); + ly_set_free(set, NULL); + + /* preceding */ + assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/c/preceding::a", 0, &set)); + assert_int_equal(2, set->count); + ly_set_free(set, NULL); + + /* preceding-sibling */ + assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/c/ll/preceding-sibling::node()", 0, &set)); + assert_int_equal(3, set->count); + ly_set_free(set, NULL); + + /* self */ + assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/c/self::c/ll/ll/b/self::b", 0, &set)); + assert_int_equal(4, set->count); + ly_set_free(set, NULL); +} + +static void +test_canonize(void **state) +{ + const char *data = + "<foo2 xmlns=\"urn:tests:a\">50</foo2>" + "<foo3 xmlns=\"urn:tests:a\" xmlns:a=\"urn:tests:a\">a:id_b</foo3>" + "<foo4 xmlns=\"urn:tests:a\">250.5</foo4>"; + struct lyd_node *tree; + struct ly_set *set; + + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree)); + assert_non_null(tree); + + /* integer */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:foo2[.='050']", &set)); + assert_int_equal(1, set->count); + ly_set_free(set, NULL); + + /* identityref */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:foo3[.='id_b']", &set)); + assert_int_equal(1, set->count); + ly_set_free(set, NULL); + + /* decimal64 */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:foo4[.='0250.500']", &set)); + assert_int_equal(1, set->count); + ly_set_free(set, NULL); + + lyd_free_all(tree); +} + +static void +test_derived_from(void **state) +{ + const char *data = + "<foo3 xmlns=\"urn:tests:a\">id_c</foo3>"; + struct lyd_node *tree; + struct ly_set *set; + + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree)); + assert_non_null(tree); + + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:foo3[derived-from(., 'a:id_b')]", &set)); + assert_int_equal(1, set->count); + ly_set_free(set, NULL); + + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:foo3[derived-from(., 'a:id_a')]", &set)); + assert_int_equal(1, set->count); + ly_set_free(set, NULL); + + lyd_free_all(tree); +} + +static void +test_augment(void **state) +{ + const char *schema_b = + "module b {\n" + " namespace urn:tests:b;\n" + " prefix b;\n" + " yang-version 1.1;\n" + "\n" + " import a {\n" + " prefix a;\n" + " }\n" + "\n" + " augment /a:c {\n" + " leaf a {\n" + " type uint16;\n" + " }\n" + " }\n" + "}"; + const char *data = + "<c xmlns=\"urn:tests:a\">\n" + " <x>value</x>\n" + " <ll>\n" + " <a>key</a>\n" + " </ll>\n" + " <a xmlns=\"urn:tests:b\">25</a>\n" + " <ll2>c1</ll2>\n" + "</c>"; + struct lyd_node *tree; + struct ly_set *set; + + UTEST_ADD_MODULE(schema_b, LYS_IN_YANG, NULL, NULL); + + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree)); + assert_non_null(tree); + + /* get all children ignoring their module */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:c/*", &set)); + assert_int_equal(4, set->count); + + ly_set_free(set, NULL); + + lyd_free_all(tree); +} + +static void +test_variables(void **state) +{ + struct lyd_node *tree, *node; + struct ly_set *set; + const char *data; + struct lyxp_var *vars = NULL; + +#define LOCAL_SETUP(DATA, TREE) \ + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, DATA, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &TREE)); \ + assert_non_null(TREE); + +#define SET_NODE(NODE, SET, INDEX) \ + assert_non_null(SET); \ + assert_true(INDEX < SET->count); \ + NODE = SET->objs[INDEX]; + +#define LOCAL_TEARDOWN(SET, TREE, VARS) \ + ly_set_free(SET, NULL); \ + lyd_free_all(TREE); \ + lyxp_vars_free(VARS); \ + vars = NULL; + + /* Eval variable to number. */ + data = + "<l1 xmlns=\"urn:tests:a\">\n" + " <a>a1</a>\n" + " <b>b1</b>\n" + " <c>c1</c>\n" + "</l1>" + "<l1 xmlns=\"urn:tests:a\">\n" + " <a>a2</a>\n" + " <b>b2</b>\n" + " <c>c2</c>\n" + "</l1>"; + LOCAL_SETUP(data, tree); + assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "2")); + assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/l1[$var]/a", vars, &set)); + SET_NODE(node, set, 0); + assert_string_equal(lyd_get_value(node), "a2"); + LOCAL_TEARDOWN(set, tree, vars); + + /* Eval variable to string. */ + data = + "<foo xmlns=\"urn:tests:a\">mstr</foo>"; + LOCAL_SETUP(data, tree); + assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "\"mstr\"")); + assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/foo[text() = $var]", vars, &set)); + SET_NODE(node, set, 0); + assert_string_equal(lyd_get_value(node), "mstr"); + LOCAL_TEARDOWN(set, tree, vars); + + /* Eval variable to set of nodes. */ + data = + "<l1 xmlns=\"urn:tests:a\">\n" + " <a>a1</a>\n" + " <b>b1</b>\n" + "</l1>" + "<l1 xmlns=\"urn:tests:a\">\n" + " <a>a2</a>\n" + " <b>b2</b>\n" + " <c>c2</c>\n" + "</l1>"; + LOCAL_SETUP(data, tree); + assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "c")); + assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/l1[$var]/a", vars, &set)); + SET_NODE(node, set, 0); + assert_string_equal(lyd_get_value(node), "a2"); + LOCAL_TEARDOWN(set, tree, vars); + + /* Variable in union expr. */ + data = + "<l1 xmlns=\"urn:tests:a\">\n" + " <a>a1</a>\n" + " <b>b1</b>\n" + " <c>c1</c>\n" + "</l1>" + "<l1 xmlns=\"urn:tests:a\">\n" + " <a>a2</a>\n" + " <b>b2</b>\n" + " <c>c2</c>\n" + "</l1>" + "<l1 xmlns=\"urn:tests:a\">\n" + " <a>a3</a>\n" + " <b>b3</b>\n" + " <c>c3</c>\n" + "</l1>"; + LOCAL_SETUP(data, tree); + assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "c[../a = 'a3']")); + assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/l1[c[../a = 'a1'] | $var]/a", vars, &set)); + SET_NODE(node, set, 0); + assert_string_equal(lyd_get_value(node), "a1"); + SET_NODE(node, set, 1); + assert_string_equal(lyd_get_value(node), "a3"); + assert_int_equal(set->count, 2); + LOCAL_TEARDOWN(set, tree, vars); + + /* Predicate after variable. */ + data = + "<l1 xmlns=\"urn:tests:a\">\n" + " <a>a1</a>\n" + " <b>b1</b>\n" + " <c>c1</c>\n" + "</l1>" + "<l1 xmlns=\"urn:tests:a\">\n" + " <a>a2</a>\n" + " <b>b2</b>\n" + " <c>c2</c>\n" + "</l1>"; + LOCAL_SETUP(data, tree); + assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "c")); + assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/l1[$var[../a = 'a1']]/a", vars, &set)); + SET_NODE(node, set, 0); + assert_string_equal(lyd_get_value(node), "a1"); + LOCAL_TEARDOWN(set, tree, vars); + + /* Variable in variable. */ + data = + "<foo xmlns=\"urn:tests:a\">mstr</foo>"; + LOCAL_SETUP(data, tree); + assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var1", "$var2")); + assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var2", "\"mstr\"")); + assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/foo[text() = $var]", vars, &set)); + SET_NODE(node, set, 0); + assert_string_equal(lyd_get_value(node), "mstr"); + LOCAL_TEARDOWN(set, tree, vars); + + /* Compare two variables. */ + data = + "<foo xmlns=\"urn:tests:a\">mstr</foo>"; + LOCAL_SETUP(data, tree); + assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var1", "\"str\"")); + assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var2", "\"str\"")); + assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/foo[$var1 = $var2]", vars, &set)); + SET_NODE(node, set, 0); + assert_string_equal(lyd_get_value(node), "mstr"); + LOCAL_TEARDOWN(set, tree, vars); + + /* Arithmetic operation with variable. */ + data = + "<foo2 xmlns=\"urn:tests:a\">4</foo2>"; + LOCAL_SETUP(data, tree); + assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var1", "2")); + assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/foo2[.= ($var1 * 2)]", vars, &set)); + SET_NODE(node, set, 0); + assert_string_equal(lyd_get_value(node), "4"); + LOCAL_TEARDOWN(set, tree, vars); + + /* Variable as function parameter. */ + data = + "<l1 xmlns=\"urn:tests:a\">\n" + " <a>a1</a>\n" + " <b>b1</b>\n" + " <c>c1</c>\n" + "</l1>" + "<l1 xmlns=\"urn:tests:a\">\n" + " <a>a2</a>\n" + " <b>b2</b>\n" + "</l1>"; + LOCAL_SETUP(data, tree); + assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "./c")); + assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/l1[count($var) = 1]/a", vars, &set)); + SET_NODE(node, set, 0); + assert_string_equal(lyd_get_value(node), "a1"); + LOCAL_TEARDOWN(set, tree, vars); + + /* Variable in path expr. */ + /* NOTE: The variable can only be at the beginning of the expression path. */ + data = + "<l1 xmlns=\"urn:tests:a\">\n" + " <a>a1</a>\n" + " <b>b1</b>\n" + " <c>c1</c>\n" + "</l1>" + "<l1 xmlns=\"urn:tests:a\">\n" + " <a>a2</a>\n" + " <b>b2</b>\n" + "</l1>"; + LOCAL_SETUP(data, tree); + assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "/l1")); + assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/l1[$var/a]", vars, &set)); + assert_int_equal(set->count, 2); + LOCAL_TEARDOWN(set, tree, vars); + + /* Variable as function. */ + data = + "<l1 xmlns=\"urn:tests:a\">\n" + " <a>a1</a>\n" + " <b>b1</b>\n" + " <c>c1</c>\n" + "</l1>" + "<l1 xmlns=\"urn:tests:a\">\n" + " <a>a2</a>\n" + " <b>b2</b>\n" + "</l1>"; + LOCAL_SETUP(data, tree); + assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "position()")); + assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/l1[$var = 2]/a", vars, &set)); + SET_NODE(node, set, 0); + assert_string_equal(lyd_get_value(node), "a2"); + LOCAL_TEARDOWN(set, tree, vars); + + /* Dynamic change of value. */ + data = + "<l1 xmlns=\"urn:tests:a\">\n" + " <a>a1</a>\n" + " <b>b1</b>\n" + " <c>c1</c>\n" + "</l1>" + "<l1 xmlns=\"urn:tests:a\">\n" + " <a>a2</a>\n" + " <b>b2</b>\n" + " <c>c2</c>\n" + "</l1>"; + LOCAL_SETUP(data, tree); + assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "1")); + assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/l1[$var]/a", vars, &set)); + SET_NODE(node, set, 0); + assert_string_equal(lyd_get_value(node), "a1"); + ly_set_free(set, NULL); + assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "2")); + assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/l1[$var]/a", vars, &set)); + SET_NODE(node, set, 0); + assert_string_equal(lyd_get_value(node), "a2"); + LOCAL_TEARDOWN(set, tree, vars); + + /* Variable not defined. */ + data = + "<foo xmlns=\"urn:tests:a\">mstr</foo>"; + LOCAL_SETUP(data, tree); + assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var1", "\"mstr\"")); + assert_int_equal(LY_ENOTFOUND, lyd_find_xpath2(tree, "/foo[text() = $var55]", vars, &set)); + LOCAL_TEARDOWN(set, tree, vars); + + /* Syntax error in value. */ + data = + "<foo xmlns=\"urn:tests:a\">mstr</foo>"; + LOCAL_SETUP(data, tree); + assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "\"")); + assert_int_equal(LY_EVALID, lyd_find_xpath2(tree, "/foo[$var]", vars, &set)); + LOCAL_TEARDOWN(set, tree, vars); + + /* Prefix is not supported. */ + data = + "<foo xmlns=\"urn:tests:a\">mstr</foo>"; + LOCAL_SETUP(data, tree); + assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "\"")); + assert_int_equal(LY_EVALID, lyd_find_xpath2(tree, "/foo[$pref:var]", vars, &set)); + assert_string_equal("Variable with prefix is not supported.", _UC->err_msg); + LOCAL_TEARDOWN(set, tree, vars); + +#undef LOCAL_SETUP +#undef LOCAL_TEARDOWN +} + +static void +test_axes(void **state) +{ + const char *data; + struct lyd_node *tree; + struct ly_set *set; + + data = + "<l1 xmlns=\"urn:tests:a\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\">\n" + " <a>a1</a>\n" + " <b yang:operation=\"replace\">b1</b>\n" + " <c yang:operation=\"none\">c1</c>\n" + "</l1>\n" + "<l1 xmlns=\"urn:tests:a\">\n" + " <a>a2</a>\n" + " <b>b2</b>\n" + "</l1>" + "<l1 xmlns=\"urn:tests:a\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\">\n" + " <a yang:operation=\"none\" yang:key=\"[no-key='no-value']\">a3</a>\n" + " <b>b3</b>\n" + " <c yang:value=\"no-val\">c3</c>\n" + "</l1>" + "<c xmlns=\"urn:tests:a\">" + " <x>val</x>" + " <ll>" + " <a>key1</a>" + " <ll>" + " <a>key11</a>" + " <b>val11</b>" + " </ll>" + " <ll>" + " <a>key12</a>" + " <b>val12</b>" + " </ll>" + " <ll>" + " <a>key13</a>" + " <b>val13</b>" + " </ll>" + " </ll>" + " <ll>" + " <a>key2</a>" + " <ll>" + " <a>key21</a>" + " <b>val21</b>" + " </ll>" + " <ll>" + " <a>key22</a>" + " <b>val22</b>" + " </ll>" + " </ll>" + "</c>"; + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree)); + assert_non_null(tree); + + /* ancestor */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "//ll[a and b]/a/ancestor::node()", &set)); + assert_int_equal(8, set->count); + ly_set_free(set, NULL); + + /* ancestor-or-self */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "//ll[a and b]/ancestor-or-self::ll", &set)); + assert_int_equal(7, set->count); + ly_set_free(set, NULL); + + /* attribute */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/l1/@operation", &set)); + assert_int_equal(0, set->count); + ly_set_free(set, NULL); + + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/l1/attribute::key", &set)); + assert_int_equal(0, set->count); + ly_set_free(set, NULL); + + /* child */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/child::l1/child::a", &set)); + assert_int_equal(3, set->count); + ly_set_free(set, NULL); + + /* descendant */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/descendant::c/descendant::b", &set)); + assert_int_equal(5, set->count); + ly_set_free(set, NULL); + + /* descendant-or-self */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "//c", &set)); + assert_int_equal(3, set->count); + ly_set_free(set, NULL); + + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/descendant-or-self::node()/c", &set)); + assert_int_equal(3, set->count); + ly_set_free(set, NULL); + + /* following */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/c/x/following::a", &set)); + assert_int_equal(7, set->count); + ly_set_free(set, NULL); + + /* following-sibling */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/c/x/following-sibling::ll", &set)); + assert_int_equal(2, set->count); + ly_set_free(set, NULL); + + /* parent */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/child::*/c/parent::l1", &set)); + assert_int_equal(2, set->count); + ly_set_free(set, NULL); + + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/child::c//..", &set)); + assert_int_equal(8, set->count); + ly_set_free(set, NULL); + + /* preceding */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/c/preceding::a", &set)); + assert_int_equal(3, set->count); + ly_set_free(set, NULL); + + /* preceding-sibling */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/c/ll/preceding-sibling::node()", &set)); + assert_int_equal(2, set->count); + ly_set_free(set, NULL); + + /* self */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/c/self::c/ll/ll/b/self::b", &set)); + assert_int_equal(5, set->count); + ly_set_free(set, NULL); + + lyd_free_all(tree); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_predicate, setup), + UTEST(test_union, setup), + UTEST(test_invalid, setup), + UTEST(test_hash, setup), + UTEST(test_toplevel, setup), + UTEST(test_atomize, setup), + UTEST(test_canonize, setup), + UTEST(test_derived_from, setup), + UTEST(test_augment, setup), + UTEST(test_variables, setup), + UTEST(test_axes, setup), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/basic/test_yanglib.c b/tests/utests/basic/test_yanglib.c new file mode 100644 index 0000000..0349157 --- /dev/null +++ b/tests/utests/basic/test_yanglib.c @@ -0,0 +1,144 @@ +/** + * @file test_yanglib.c + * @author: Michal Vasko <mvasko@cesnet.cz> + * @brief unit tests for ietf-yang-library data + * + * Copyright (c) 2020 CESNET, z.s.p.o. + * + * This source code is licensed under BSD 3-Clause License (the "License"). + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + */ +#define _UTEST_MAIN_ +#include "utests.h" + +#include <string.h> + +#include "context.h" +#include "in.h" +#include "log.h" +#include "set.h" +#include "tests_config.h" +#include "tree_data.h" +#include "tree_schema.h" + +const char *schema_a = + "module a {\n" + " namespace urn:tests:a;\n" + " prefix a;\n" + " yang-version 1.1;\n" + "\n" + " include a_sub;\n" + "\n" + " list l2 {\n" + " key \"a\";\n" + " leaf a {\n" + " type uint16;\n" + " }\n" + " leaf b {\n" + " type uint16;\n" + " }\n" + " }\n" + "}"; +const char *schema_b = + "module b {\n" + " namespace urn:tests:b;\n" + " prefix b;\n" + " yang-version 1.1;\n" + "\n" + " import a {\n" + " prefix a;\n" + " }\n" + "\n" + " deviation /a:l2 {\n" + " deviate add {\n" + " max-elements 40;\n" + " }\n" + " }\n" + "\n" + " leaf foo {\n" + " type string;\n" + " }\n" + "}"; + +static LY_ERR +test_imp_clb(const char *mod_name, const char *mod_rev, const char *submod_name, const char *sub_rev, void *user_data, + LYS_INFORMAT *format, const char **module_data, void (**free_module_data)(void *model_data, void *user_data)) +{ + const char *schema_a_sub = + "submodule a_sub {\n" + " belongs-to a {\n" + " prefix a;\n" + " }\n" + " yang-version 1.1;\n" + "\n" + " feature feat1;\n" + "\n" + " list l3 {\n" + " key \"a\";\n" + " leaf a {\n" + " type uint16;\n" + " }\n" + " leaf b {\n" + " type uint16;\n" + " }\n" + " }\n" + "}\n"; + + assert_string_equal(mod_name, "a"); + assert_null(mod_rev); + if (!submod_name) { + return LY_ENOTFOUND; + } + assert_string_equal(submod_name, "a_sub"); + assert_null(sub_rev); + assert_null(user_data); + + *format = LYS_IN_YANG; + *module_data = schema_a_sub; + *free_module_data = NULL; + return LY_SUCCESS; +} + +static void +test_yanglib(void **state) +{ + const char *feats[] = {"feat1", NULL}; + struct lyd_node *tree; + struct ly_set *set; + LY_ERR ret; + + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, NULL); + UTEST_ADD_MODULE(schema_a, LYS_IN_YANG, feats, NULL); + UTEST_ADD_MODULE(schema_b, LYS_IN_YANG, NULL, NULL); + + assert_int_equal(LY_SUCCESS, ly_ctx_get_yanglib_data(UTEST_LYCTX, &tree, "<<%u>>", ly_ctx_get_change_count(UTEST_LYCTX))); + lyd_free_all(tree); + assert_int_equal(LY_SUCCESS, ly_ctx_get_yanglib_data(UTEST_LYCTX, &tree, "%u", -10)); + lyd_free_all(tree); + assert_int_equal(LY_SUCCESS, ly_ctx_get_yanglib_data(UTEST_LYCTX, &tree, "")); + lyd_free_all(tree); + assert_int_equal(LY_SUCCESS, ly_ctx_get_yanglib_data(UTEST_LYCTX, &tree, "%u", ly_ctx_get_change_count(UTEST_LYCTX))); + + /* make sure there is "a" with a submodule and deviation */ + ret = lyd_find_xpath(tree, "/ietf-yang-library:yang-library/module-set/module[name='a'][submodule/name='a_sub']" + "[feature='feat1'][deviation='b']", &set); + assert_int_equal(ret, LY_SUCCESS); + + assert_int_equal(set->count, 1); + ly_set_free(set, NULL); + + lyd_free_all(tree); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_yanglib), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} |