From c1987bd124bf8cdd808f50297a0d8fd834050d4a Mon Sep 17 00:00:00 2001 From: Ondrej Kusnirik Date: Tue, 9 Apr 2024 15:31:59 +0200 Subject: [PATCH] test plugin NEW tests for datastore plugins --- tests/CMakeLists.txt | 6 +- tests/files/plugin.yang | 129 ++++ tests/test_plugin.c | 1429 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 1562 insertions(+), 2 deletions(-) create mode 100644 tests/files/plugin.yang create mode 100644 tests/test_plugin.c diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 42f7cb742..ef9320b58 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -53,7 +53,7 @@ if(ENABLE_TESTS) # lists of all the tests set(tests test_modules test_context_change test_validation test_edit test_candidate test_oper_pull test_oper_push test_lock test_apply_changes test_copy_config test_rpc_action test_notif test_get test_process - test_multi_connection test_nacm test_rotation test_sub_notif) + test_multi_connection test_nacm test_rotation test_sub_notif test_plugin) foreach(test_name IN LISTS tests) add_executable(${test_name} ${test_sources} ${test_name}.c) @@ -62,10 +62,12 @@ if(ENABLE_TESTS) # set common attributes of all tests foreach(test_name IN LISTS tests) target_link_libraries(${test_name} ${CMOCKA_LIBRARIES} sysrepo) + if(${test_name} STREQUAL "test_plugin") + target_link_libraries(${test_name} srobj) + endif() add_test(NAME ${test_name} COMMAND $) set_property(TEST ${test_name} APPEND PROPERTY ENVIRONMENT "MALLOC_CHECK_=3" - "CMOCKA_TEST_ABORT=1" "SYSREPO_REPOSITORY_PATH=${PROJECT_BINARY_DIR}/test_repositories/${test_name}" "SYSREPO_SHM_PREFIX=_tests_sr_${test_name}" ) diff --git a/tests/files/plugin.yang b/tests/files/plugin.yang new file mode 100644 index 000000000..ca0b374e2 --- /dev/null +++ b/tests/files/plugin.yang @@ -0,0 +1,129 @@ +module plugin { + yang-version 1.1; + namespace s; + prefix s; + + container simple-cont { + + container simple-cont1 { + presence "This is an example of a presence container."; + } + + container simple-cont2 { + container ac1 { + list acl1 { + key acs1; + leaf acs1 { + type string; + } + leaf acs2 { + type string; + } + leaf acs3 { + type string; + } + container inner { + leaf inner-leaf { + type string; + } + } + anydata data; + } + + list acl2 { + key acs1; + ordered-by user; + leaf acs1 { + type string; + } + } + + list acl3 { + key "acs1 acs2 acs3"; + ordered-by user; + leaf acs1 { + type string; + } + leaf acs2 { + type string; + } + leaf acs3 { + type string; + } + container inner { + leaf inner-leaf { + type string; + } + } + } + + list acl4 { + config false; + leaf acs1 { + type string; + } + leaf acs2 { + type string; + } + description + "Keyless list."; + } + + list acl5 { + key "acs1 acs2 acs3"; + leaf acs1 { + type string; + } + leaf acs2 { + type string; + } + leaf acs3 { + type string; + } + } + + leaf-list dup-keys { + config false; + type string; + description + "Leaf-list with duplicate keys."; + } + } + } + + container simple-cont3 { + leaf-list user-list { + type string; + ordered-by user; + description + "User ordered list."; + } + anyxml data; + } + + container simple-cont4 { + choice rand-type { + default random; + case random { + leaf random { + type uint32; + } + } + } + } + + container simple-cont5 { + leaf-list user-list { + type string; + ordered-by user; + description + "User ordered list 2."; + } + leaf-list system-list { + type string; + description + "System ordered list."; + } + } + } +} \ No newline at end of file diff --git a/tests/test_plugin.c b/tests/test_plugin.c new file mode 100644 index 000000000..bdfc470ab --- /dev/null +++ b/tests/test_plugin.c @@ -0,0 +1,1429 @@ +/** + * @file test_plugin.c + * @author Ondrej Kusnirik + * @brief test for all datastore plugins + * + * @copyright + * Copyright (c) 2018 - 2022 Deutsche Telekom AG. + * Copyright (c) 2018 - 2022 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 _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include // temp + +#include +#include + +#include "sysrepo.h" + +#include "common.h" +#include "plugins_datastore.h" +#include "tests/tcommon.h" + +typedef struct test_data_s { + sr_conn_ctx_t *conn; + const struct ly_ctx *ctx; + sr_session_ctx_t *sess; +} test_data_t; + +const char *plg_name; + +static int +testutil_uid2usr(uid_t uid, char **username) +{ + int rc = SR_ERR_OK, r; + struct passwd pwd, *pwd_p; + char *buf = NULL, *mem; + ssize_t buflen = 0; + + do { + if (!buflen) { + // learn suitable buffer size + buflen = sysconf(_SC_GETPW_R_SIZE_MAX); + if (buflen == -1) { + buflen = 2048; + } + } else { + // enlarge buffer + buflen += 2048; + } + + // allocate some buffer + mem = realloc(buf, buflen); + if (!mem) { + SRPLG_LOG_ERR("TESTS", "Memory allocation failed."); + rc = SR_ERR_NO_MEMORY; + goto cleanup; + } + buf = mem; + + // UID -> user + r = getpwuid_r(uid, &pwd, buf, buflen, &pwd_p); + } + while (r == ERANGE); + + if (r) { + SRPLG_LOG_ERR("TESTS", "Retrieving UID \"%lu\" passwd entry failed (%s).", (unsigned long int)uid, strerror(r)); + rc = SR_ERR_INTERNAL; + goto cleanup; + } + else if (!pwd_p) { + SRPLG_LOG_ERR("TESTS", "Retrieving UID \"%lu\" passwd entry failed (No such UID).", (unsigned long int)uid); + rc = SR_ERR_NOT_FOUND; + goto cleanup; + } + + *username = strdup(pwd.pw_name); + if (!*username) { + SRPLG_LOG_ERR("TESTS", "Memory allocation failed."); + rc = SR_ERR_NO_MEMORY; + goto cleanup; + } + +cleanup: + free(buf); + return rc; +} + +static int +testutil_gid2grp(gid_t gid, char **group) +{ + int rc = SR_ERR_OK, r; + struct group grp, *grp_p; + char *buf = NULL, *mem; + ssize_t buflen = 0; + + do { + if (!buflen) { + // learn suitable buffer size + buflen = sysconf(_SC_GETGR_R_SIZE_MAX); + if (buflen == -1) { + buflen = 2048; + } + } else { + // enlarge buffer + buflen += 2048; + } + + // allocate some buffer + mem = realloc(buf, buflen); + if (!mem) { + SRPLG_LOG_ERR("TESTS", "Memory allocation failed."); + rc = SR_ERR_NO_MEMORY; + goto cleanup; + } + buf = mem; + + // GID -> group + r = getgrgid_r(gid, &grp, buf, buflen, &grp_p); + } + while (r == ERANGE); + + if (r) { + SRPLG_LOG_ERR("TESTS", "Retrieving GID \"%lu\" grp entry failed (%s).", (unsigned long int)gid, strerror(r)); + rc = SR_ERR_INTERNAL; + goto cleanup; + } + else if (!grp_p) { + SRPLG_LOG_ERR("TESTS", "Retrieving GID \"%lu\" grp entry failed (No such GID).", (unsigned long int)gid); + rc = SR_ERR_NOT_FOUND; + goto cleanup; + } + + // assign group + *group = strdup(grp.gr_name); + if (!*group) { + SRPLG_LOG_ERR("TESTS", "Memory allocation failed."); + rc = SR_ERR_NO_MEMORY; + goto cleanup; + } + +cleanup: + free(buf); + return rc; +} + +int +setup(void **state) +{ + int rc, i; + test_data_t *tdata; + sr_module_ds_t mod_ds; + const char *init_data = "" + "" + "" + "" + "" + "initial" + "" + "" + "" + ""; + + for (i = 0; i < 5; ++i) mod_ds.plugin_name[i] = plg_name; + mod_ds.plugin_name[5] = "JSON notif"; + + tdata = calloc(1, sizeof *tdata); + if (!tdata) { + return 1; + } + + rc = sr_connect(SR_CONN_DEFAULT, &tdata->conn); + if (rc != SR_ERR_OK) { + return 1; + } + + // install datastores + rc = sr_install_module2(tdata->conn, TESTS_SRC_DIR "/files/plugin.yang", TESTS_SRC_DIR "/files", NULL, &mod_ds, NULL, NULL, 0, init_data, NULL, LYD_XML); + if (rc != SR_ERR_OK) { + return 1; + } + + rc = sr_set_module_ds_access(tdata->conn, "plugin", SR_DS_STARTUP, NULL, NULL, S_IRUSR | S_IWUSR | S_IROTH); + if (rc != SR_ERR_OK) { + return 1; + } + + rc = sr_set_module_ds_access(tdata->conn, "plugin", SR_DS_RUNNING, NULL, NULL, S_IRUSR | S_IWUSR); + if (rc != SR_ERR_OK) { + return 1; + } + + rc = sr_set_module_ds_access(tdata->conn, "plugin", SR_DS_CANDIDATE, NULL, NULL, S_IRUSR | S_IWUSR); + if (rc != SR_ERR_OK) { + return 1; + } + + rc = sr_set_module_ds_access(tdata->conn, "plugin", SR_DS_OPERATIONAL, NULL, NULL, S_IRUSR | S_IWUSR); + if (rc != SR_ERR_OK) { + return 1; + } + + rc = sr_set_module_ds_access(tdata->conn, "plugin", SR_DS_FACTORY_DEFAULT, NULL, NULL, S_IRUSR | S_IWUSR); + if (rc != SR_ERR_OK) { + return 1; + } + + tdata->ctx = sr_acquire_context(tdata->conn); + + // start session + rc = sr_session_start(tdata->conn, SR_DS_RUNNING, &(tdata->sess)); + if (rc != SR_ERR_OK) { + return 1; + } + + *state = tdata; + return 0; +} + +int +teardown(void **state) +{ + int rc; + test_data_t *tdata = *state; + + // stop session + rc = sr_session_stop(tdata->sess); + if (rc != SR_ERR_OK) { + return 1; + } + + sr_release_context(tdata->conn); + + // uninstall datastores + rc = sr_remove_module(tdata->conn, "plugin", 0); + if (rc != SR_ERR_OK) { + return 1; + } + + rc = sr_disconnect(tdata->conn); + if (rc != SR_ERR_OK) { + return 1; + } + + free(tdata); + return 0; +} + +/* TEST */ +static void +test_dummy(void **state) +{ + (void) state; + assert_true(1); +} + +static void +load_empty_ds(test_data_t *tdata) +{ + int rc; + sr_data_t *data = NULL; + char *str1 = NULL; + const char *str2 = "" + "" + "" + "" + "" + "initial" + "" + "" + "" + "" + "" + "" + "" + ""; + + // load module data + rc = sr_get_data(tdata->sess, "/plugin:*", 0, 0, 0, &data); + assert_int_equal(rc, SR_ERR_OK); + + rc = lyd_print_mem(&str1, data->tree, LYD_XML, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_WD_ALL_TAG | LYD_PRINT_SHRINK | LYD_PRINT_KEEPEMPTYCONT); + assert_int_equal(rc, LY_SUCCESS); + sr_release_data(data); + + // compare + assert_string_equal(str1, str2); + free(str1); +} + +static int +run_cb(sr_session_ctx_t *session, uint32_t sub_id, const char *module_name, const char *xpath, + sr_event_t event, uint32_t request_id, void *private_data) +{ + (void) session; + (void) sub_id; + (void) module_name; + (void) xpath; + (void) event; + (void) request_id; + (void) private_data; + return SR_ERR_OK; +} + +/* TEST */ +static void +test_load_empty(void **state) +{ + int rc; + test_data_t *tdata = *state; + sr_subscription_ctx_t *subscription = NULL; + + /* STARTUP */ + rc = sr_session_switch_ds(tdata->sess, SR_DS_STARTUP); + assert_int_equal(rc, SR_ERR_OK); + load_empty_ds(tdata); + + /* RUNNING */ + rc = sr_session_switch_ds(tdata->sess, SR_DS_RUNNING); + assert_int_equal(rc, SR_ERR_OK); + load_empty_ds(tdata); + + /* CANDIDATE */ + rc = sr_session_switch_ds(tdata->sess, SR_DS_CANDIDATE); + assert_int_equal(rc, SR_ERR_OK); + load_empty_ds(tdata); + + /* OPERATIONAL */ + rc = sr_session_switch_ds(tdata->sess, SR_DS_RUNNING); + assert_int_equal(rc, SR_ERR_OK); + rc = sr_module_change_subscribe(tdata->sess, "plugin", NULL, run_cb, NULL, 10, 0, &subscription); + assert_int_equal(rc, SR_ERR_OK); + rc = sr_session_switch_ds(tdata->sess, SR_DS_OPERATIONAL); + assert_int_equal(rc, SR_ERR_OK); + load_empty_ds(tdata); + sr_unsubscribe(subscription); + + /* FACTORY DEFAULT */ + rc = sr_session_switch_ds(tdata->sess, SR_DS_FACTORY_DEFAULT); + assert_int_equal(rc, SR_ERR_OK); + load_empty_ds(tdata); +} + +int +teardown_store(void **state) +{ + int rc; + test_data_t *tdata = *state; + + /* + * STARTUP + */ + rc = sr_session_switch_ds(tdata->sess, SR_DS_STARTUP); + if (rc != SR_ERR_OK) { + return 1; + } + + // delete all datastore data + rc = sr_replace_config(tdata->sess, "plugin", NULL, 0); + if (rc != SR_ERR_OK) { + return 1; + } + + /* + * RUNNING + */ + rc = sr_session_switch_ds(tdata->sess, SR_DS_RUNNING); + if (rc != SR_ERR_OK) { + return 1; + } + + // delete all datastore data + rc = sr_replace_config(tdata->sess, "plugin", NULL, 0); + if (rc != SR_ERR_OK) { + return 1; + } + + /* + * CANDIDATE + */ + rc = sr_session_switch_ds(tdata->sess, SR_DS_CANDIDATE); + if (rc != SR_ERR_OK) { + return 1; + } + + // reset candidate + rc = sr_copy_config(tdata->sess, "plugin", SR_DS_RUNNING, 0); + if (rc != SR_ERR_OK) { + return 1; + } + + return 0; +} + +static void +store_and_load_example(test_data_t *tdata) +{ + int rc; + sr_data_t *data = NULL; + struct lyd_node *node = NULL; + char *str1 = NULL; + const char *str2 = "" + "" + "" + "" + "a" + "a" + "" + "" + "b" + "a" + "" + "a" + "" + "" + "" + "" + ""; + + rc = lyd_parse_data_mem(tdata->ctx, str2, LYD_XML, LYD_PARSE_STRICT | LYD_PARSE_ONLY, 0, &node); + assert_int_equal(rc, LY_SUCCESS); + + rc = sr_replace_config(tdata->sess, "plugin", node, 0); + assert_int_equal(rc, SR_ERR_OK); + + // load module + rc = sr_get_data(tdata->sess, "/plugin:*", 0, 0, 0, &data); + assert_int_equal(rc, SR_ERR_OK); + + rc = lyd_print_mem(&str1, data->tree, LYD_XML, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_WD_ALL_TAG | LYD_PRINT_SHRINK); + assert_int_equal(rc, LY_SUCCESS); + sr_release_data(data); + + // compare + assert_string_equal(str1, str2); + free(str1); +} + +/* TEST */ +static void +test_store_example(void **state) +{ + int rc; + test_data_t *tdata = *state; + + /* STARTUP */ + rc = sr_session_switch_ds(tdata->sess, SR_DS_STARTUP); + assert_int_equal(rc, SR_ERR_OK); + store_and_load_example(tdata); + + /* RUNNING */ + rc = sr_session_switch_ds(tdata->sess, SR_DS_RUNNING); + assert_int_equal(rc, SR_ERR_OK); + store_and_load_example(tdata); + + /* CANDIDATE */ + rc = sr_session_switch_ds(tdata->sess, SR_DS_CANDIDATE); + assert_int_equal(rc, SR_ERR_OK); + store_and_load_example(tdata); +} + +static void +store_and_load_complex(test_data_t *tdata) +{ + int rc; + sr_data_t *data = NULL; + struct lyd_node *node = NULL; + char *str1 = NULL; + const char *str2 = + "" + "" + "" + "" + "a" + "a" + "" + "" + "b" + "a" + "" + "a" + "" + "" + + "" + "a" + "" + "" + "b" + "" + "" + "c" + "" + "" + "d" + "" + "" + "e" + "" + "" + "f" + "" + "" + "g" + "" + + "" + "a" + "b" + "c" + "" + "a" + "" + "" + "" + "b" + "c" + "d" + "" + "a" + "" + "" + "" + "c" + "d" + "e" + "" + "" + "d" + "e" + "f" + "" + "" + "e" + "f" + "g" + "" + "" + "f" + "g" + "h" + "" + "" + "g" + "h" + "i" + "" + + "" + "first" + "second" + "third" + "" + "" + "one" + "two" + "three" + "" + "" + "second" + "third" + "first" + "" + "" + "two" + "one" + "one" + "" + + "" + "" + "" + "green" + "undefined" + "brown" + "jellyfish" + "pink" + "" + "" + "pink" + "jellyfish" + "magenta" + "brown" + "yellow" + "cyan" + + "brown" + "cyan" + "jellyfish" + "magenta" + "pink" + "yellow" + "" + ""; + + const char *str3 = + "" + "" + "" + "" + "" + "b" + "f" + "" + "a" + "" + "a" + "" + "" + "c" + "a" + "" + + "" + "g" + "" + "" + "h" + "" + "" + "a" + "" + "" + "c" + "" + "" + "d" + "" + "" + "i" + "" + "" + "e" + "" + "" + "b" + "" + "" + "f" + "" + + "" + "b" + "c" + "d" + "" + "b" + "" + "" + "" + "z" + "b" + "c" + "" + "" + "a" + "c" + "d" + "" + "" + "c" + "d" + "e" + "" + "b" + "" + "" + "" + "a" + "b" + "c" + "" + "" + "d" + "e" + "c" + "" + "" + "d" + "e" + "f" + "" + "" + "e" + "f" + "g" + "" + + "" + "first" + "second" + "third" + "" + "" + "one" + "one" + "one" + "" + "" + "third" + "second" + "third" + "" + "" + "two" + "one" + "one" + "" + + "" + "" + "" + "violet" + "brown" + "black" + "white" + "magenta" + "purple" + "green" + "orange" + "maroon" + "indigo" + "cyan" + "blue" + "red" + "yellow" + "pink" + "Awesome string" + "" + "" + "violet" + "brown" + "black" + "white" + "magenta" + "purple" + "green" + "orange" + "maroon" + "indigo" + "cyan" + "blue" + "red" + "yellow" + "pink" + + "black" + "blue" + "brown" + "cyan" + "green" + "indigo" + "magenta" + "maroon" + "orange" + "pink" + "purple" + "red" + "violet" + "white" + "yellow" + "" + ""; + + /* + * FIRST STORE + */ + rc = lyd_parse_data_mem(tdata->ctx, str2, LYD_XML, LYD_PARSE_STRICT | LYD_PARSE_ONLY, 0, &node); + assert_int_equal(rc, LY_SUCCESS); + + rc = sr_replace_config(tdata->sess, "plugin", node, 0); + assert_int_equal(rc, SR_ERR_OK); + + // load module + rc = sr_get_data(tdata->sess, "/plugin:*", 0, 0, 0, &data); + assert_int_equal(rc, SR_ERR_OK); + + rc = lyd_print_mem(&str1, data->tree, LYD_XML, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_WD_ALL_TAG | LYD_PRINT_SHRINK); + assert_int_equal(rc, LY_SUCCESS); + sr_release_data(data); + + // compare + assert_string_equal(str1, str2); + free(str1); + + /* + * SECOND STORE + */ + rc = lyd_parse_data_mem(tdata->ctx, str3, LYD_XML, LYD_PARSE_STRICT | LYD_PARSE_ONLY, 0, &node); + assert_int_equal(rc, LY_SUCCESS); + + rc = sr_replace_config(tdata->sess, "plugin", node, 0); + assert_int_equal(rc, SR_ERR_OK); + + // load module + rc = sr_get_data(tdata->sess, "/plugin:*", 0, 0, 0, &data); + assert_int_equal(rc, SR_ERR_OK); + + rc = lyd_print_mem(&str1, data->tree, LYD_XML, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_WD_ALL_TAG | LYD_PRINT_SHRINK); + assert_int_equal(rc, LY_SUCCESS); + sr_release_data(data); + + // compare + assert_string_equal(str1, str3); + free(str1); +} + +/* TEST */ +static void +test_store_complex(void **state) +{ + int rc; + test_data_t *tdata = *state; + + /* STARTUP */ + rc = sr_session_switch_ds(tdata->sess, SR_DS_STARTUP); + assert_int_equal(rc, SR_ERR_OK); + store_and_load_complex(tdata); + + /* RUNNING */ + rc = sr_session_switch_ds(tdata->sess, SR_DS_RUNNING); + assert_int_equal(rc, SR_ERR_OK); + store_and_load_complex(tdata); + + /* CANDIDATE */ + rc = sr_session_switch_ds(tdata->sess, SR_DS_CANDIDATE); + assert_int_equal(rc, SR_ERR_OK); + store_and_load_complex(tdata); +} + +int +teardown_store_oper(void **state) +{ + int rc; + test_data_t *tdata = *state; + + rc = sr_session_switch_ds(tdata->sess, SR_DS_OPERATIONAL); + if (rc != SR_ERR_OK) { + return 1; + } + + // delete all datastore data + rc = sr_discard_items(tdata->sess, "/plugin:*"); + if (rc != SR_ERR_OK) { + return 1; + } + rc = sr_apply_changes(tdata->sess, 0); + if (rc != SR_ERR_OK) { + return 1; + } + + return 0; +} + +/* TEST */ +static void +test_store_oper(void **state) +{ + int rc; + test_data_t *tdata = *state; + sr_data_t *data = NULL; + struct lyd_node *node = NULL; + char *str1 = NULL; + const char *str2 = + "" + "" + "" + "" + "hello" + "this is a keyless instance" + "" + "" + "hello" + "this is a keyless instance" + "" + "" + "bye" + "no data" + "" + "" + "bye" + "no data" + "" + "first" + "first" + "second" + "first" + "second" + "third" + "" + "" + ""; + + const char *str3 = + "" + "" + "" + "" + "hello" + "this is a keyless instance" + "" + "" + "bye" + "" + "" + "bye" + "this is a keyless instance" + "" + "" + "hello" + "this is a keyless instance" + "" + "" + "bye" + "" + "first" + "first" + "third" + "second" + "second" + "first" + "fourth" + "first" + "" + "" + ""; + + const char *final_comp = + "" + "" + "" + "" + "hello" + "this is a keyless instance" + "" + "" + "bye" + "no data" + "" + "" + "hello" + "this is a keyless instance" + "" + "" + "bye" + "" + "" + "bye" + "this is a keyless instance" + "" + "" + "hello" + "this is a keyless instance" + "" + "" + "bye" + "" + "first" + "first" + "first" + "first" + "third" + "second" + "second" + "first" + "fourth" + "first" + "" + "" + ""; + + + /* OPERATIONAL */ + rc = sr_session_switch_ds(tdata->sess, SR_DS_OPERATIONAL); + assert_int_equal(rc, SR_ERR_OK); + + /* + * FIRST STORE + */ + rc = lyd_parse_data_mem(tdata->ctx, str2, LYD_XML, LYD_PARSE_OPAQ | LYD_PARSE_ONLY, 0, &node); + assert_int_equal(rc, LY_SUCCESS); + + rc = sr_edit_batch(tdata->sess, node, "merge"); + assert_int_equal(rc, SR_ERR_OK); + lyd_free_all(node); + + rc = sr_apply_changes(tdata->sess, 0); + assert_int_equal(rc, SR_ERR_OK); + + // load module + rc = sr_get_data(tdata->sess, "/plugin:*", 0, 0, SR_OPER_WITH_ORIGIN, &data); + assert_int_equal(rc, SR_ERR_OK); + + rc = lyd_print_mem(&str1, data->tree, LYD_XML, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_WD_ALL_TAG | LYD_PRINT_SHRINK); + assert_int_equal(rc, LY_SUCCESS); + sr_release_data(data); + + // compare + assert_string_equal(str1, str2); + free(str1); + + /* + * SECOND STORE + */ + rc = lyd_parse_data_mem(tdata->ctx, str3, LYD_XML, LYD_PARSE_OPAQ | LYD_PARSE_ONLY, 0, &node); + assert_int_equal(rc, LY_SUCCESS); + + rc = sr_discard_items(tdata->sess, "/plugin:simple-cont/simple-cont2/ac1/acl4[1]"); + assert_int_equal(rc, SR_ERR_OK); + + rc = sr_discard_items(tdata->sess, "/plugin:simple-cont/simple-cont2/ac1/acl4[3]"); + assert_int_equal(rc, SR_ERR_OK); + + rc = sr_discard_items(tdata->sess, "/plugin:simple-cont/simple-cont2/ac1/dup-keys[3]"); + assert_int_equal(rc, SR_ERR_OK); + + rc = sr_discard_items(tdata->sess, "/plugin:simple-cont/simple-cont2/ac1/dup-keys[3]"); + assert_int_equal(rc, SR_ERR_OK); + + rc = sr_discard_items(tdata->sess, "/plugin:simple-cont/simple-cont2/ac1/dup-keys[3]"); + assert_int_equal(rc, SR_ERR_OK); + + rc = sr_discard_items(tdata->sess, "/plugin:simple-cont/simple-cont2/ac1/dup-keys[3]"); + assert_int_equal(rc, SR_ERR_OK); + + rc = sr_apply_changes(tdata->sess, 0); + assert_int_equal(rc, SR_ERR_OK); + + rc = sr_edit_batch(tdata->sess, node, "merge"); + assert_int_equal(rc, SR_ERR_OK); + lyd_free_all(node); + + rc = sr_apply_changes(tdata->sess, 0); + assert_int_equal(rc, SR_ERR_OK); + + // load module + rc = sr_get_data(tdata->sess, "/plugin:*", 0, 0, SR_OPER_WITH_ORIGIN, &data); + assert_int_equal(rc, SR_ERR_OK); + + rc = lyd_print_mem(&str1, data->tree, LYD_XML, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_WD_ALL_TAG | LYD_PRINT_SHRINK); + assert_int_equal(rc, LY_SUCCESS); + sr_release_data(data); + + // compare + assert_string_equal(str1, final_comp); + free(str1); +} + +int +teardown_access(void **state) +{ + int rc; + test_data_t *tdata = *state; + + // reset access rights + rc = sr_set_module_ds_access(tdata->conn, "plugin", SR_DS_STARTUP, NULL, NULL, S_IRUSR | S_IWUSR | S_IROTH); + if (rc != SR_ERR_OK) { + return 1; + } + + rc = sr_set_module_ds_access(tdata->conn, "plugin", SR_DS_RUNNING, NULL, NULL, S_IRUSR | S_IWUSR); + if (rc != SR_ERR_OK) { + return 1; + } + + rc = sr_set_module_ds_access(tdata->conn, "plugin", SR_DS_CANDIDATE, NULL, NULL, S_IRUSR | S_IWUSR); + if (rc != SR_ERR_OK) { + return 1; + } + + rc = sr_set_module_ds_access(tdata->conn, "plugin", SR_DS_OPERATIONAL, NULL, NULL, S_IRUSR | S_IWUSR); + if (rc != SR_ERR_OK) { + return 1; + } + + rc = sr_set_module_ds_access(tdata->conn, "plugin", SR_DS_FACTORY_DEFAULT, NULL, NULL, S_IRUSR | S_IWUSR); + if (rc != SR_ERR_OK) { + return 1; + } + + return 0; +} + +static void +check_access(char *username, char *groupname, mode_t perm, char *username_out, char *groupname_out, mode_t perm_out) +{ + assert_string_equal(username, username_out); + free(username_out); + assert_string_equal(groupname, groupname_out); + free(groupname_out); + assert_true(perm == perm_out); +} + +/* TEST */ +static void +test_access_get(void **state) +{ + int rc; + test_data_t *tdata = *state; + char *username = NULL, *groupname = NULL; + char *username_out = NULL, *groupname_out = NULL; + mode_t perm; + + rc = testutil_uid2usr(getuid(), &username); + assert_int_equal(rc, SR_ERR_OK); + + rc = testutil_gid2grp(getgid(), &groupname); + assert_int_equal(rc, SR_ERR_OK); + + /* STARTUP */ + rc = sr_get_module_ds_access(tdata->conn, "plugin", SR_DS_STARTUP, &username_out, &groupname_out, &perm); + assert_int_equal(rc, SR_ERR_OK); + check_access(username, groupname, perm, username_out, groupname_out, (S_IRUSR | S_IWUSR | S_IROTH)); + + /* RUNNING */ + rc = sr_get_module_ds_access(tdata->conn, "plugin", SR_DS_RUNNING, &username_out, &groupname_out, &perm); + assert_int_equal(rc, SR_ERR_OK); + check_access(username, groupname, perm, username_out, groupname_out, (S_IRUSR | S_IWUSR)); + + /* CANDIDATE */ + rc = sr_get_module_ds_access(tdata->conn, "plugin", SR_DS_CANDIDATE, &username_out, &groupname_out, &perm); + assert_int_equal(rc, SR_ERR_OK); + check_access(username, groupname, perm, username_out, groupname_out, (S_IRUSR | S_IWUSR)); + + /* OPERATIONAL */ + rc = sr_get_module_ds_access(tdata->conn, "plugin", SR_DS_OPERATIONAL, &username_out, &groupname_out, &perm); + assert_int_equal(rc, SR_ERR_OK); + check_access(username, groupname, perm, username_out, groupname_out, (S_IRUSR | S_IWUSR)); + + /* FACTORY DEFAULT */ + rc = sr_get_module_ds_access(tdata->conn, "plugin", SR_DS_FACTORY_DEFAULT, &username_out, &groupname_out, &perm); + assert_int_equal(rc, SR_ERR_OK); + check_access(username, groupname, perm, username_out, groupname_out, (S_IRUSR | S_IWUSR)); + + free(username); + free(groupname); +} + +/* TEST */ +static void +test_access_setandget(void **state) +{ + int rc; + test_data_t *tdata = *state; + char *username = NULL, *groupname = NULL; + char *username_out = NULL, *groupname_out = NULL; + mode_t perm; + + rc = testutil_uid2usr(getuid(), &username); + assert_int_equal(rc, SR_ERR_OK); + + rc = testutil_gid2grp(getgid(), &groupname); + assert_int_equal(rc, SR_ERR_OK); + + /* STARTUP */ + rc = sr_set_module_ds_access(tdata->conn, "plugin", SR_DS_STARTUP, username, groupname, S_IRUSR | S_IRGRP | S_IROTH | S_IWOTH); + assert_int_equal(rc, SR_ERR_OK); + rc = sr_get_module_ds_access(tdata->conn, "plugin", SR_DS_STARTUP, &username_out, &groupname_out, &perm); + assert_int_equal(rc, SR_ERR_OK); + check_access(username, groupname, perm, username_out, groupname_out, (S_IRUSR | S_IRGRP | S_IROTH | S_IWOTH)); + + /* RUNNING */ + rc = sr_set_module_ds_access(tdata->conn, "plugin", SR_DS_RUNNING, username, groupname, S_IRUSR | S_IRGRP | S_IROTH | S_IWOTH); + assert_int_equal(rc, SR_ERR_OK); + rc = sr_get_module_ds_access(tdata->conn, "plugin", SR_DS_RUNNING, &username_out, &groupname_out, &perm); + assert_int_equal(rc, SR_ERR_OK); + check_access(username, groupname, perm, username_out, groupname_out, (S_IRUSR | S_IRGRP | S_IROTH | S_IWOTH)); + + /* CANDIDATE */ + rc = sr_set_module_ds_access(tdata->conn, "plugin", SR_DS_CANDIDATE, username, groupname, S_IRUSR | S_IRGRP | S_IROTH | S_IWOTH); + assert_int_equal(rc, SR_ERR_OK); + rc = sr_get_module_ds_access(tdata->conn, "plugin", SR_DS_CANDIDATE, &username_out, &groupname_out, &perm); + assert_int_equal(rc, SR_ERR_OK); + check_access(username, groupname, perm, username_out, groupname_out, (S_IRUSR | S_IRGRP | S_IROTH | S_IWOTH)); + + /* OPERATIONAL */ + rc = sr_set_module_ds_access(tdata->conn, "plugin", SR_DS_OPERATIONAL, username, groupname, S_IRUSR | S_IRGRP | S_IROTH | S_IWOTH); + assert_int_equal(rc, SR_ERR_OK); + rc = sr_get_module_ds_access(tdata->conn, "plugin", SR_DS_OPERATIONAL, &username_out, &groupname_out, &perm); + assert_int_equal(rc, SR_ERR_OK); + check_access(username, groupname, perm, username_out, groupname_out, (S_IRUSR | S_IRGRP | S_IROTH | S_IWOTH)); + + /* FACTORY DEFAULT */ + rc = sr_set_module_ds_access(tdata->conn, "plugin", SR_DS_FACTORY_DEFAULT, username, groupname, S_IRUSR | S_IRGRP | S_IROTH | S_IWOTH); + assert_int_equal(rc, SR_ERR_OK); + rc = sr_get_module_ds_access(tdata->conn, "plugin", SR_DS_FACTORY_DEFAULT, &username_out, &groupname_out, &perm); + assert_int_equal(rc, SR_ERR_OK); + check_access(username, groupname, perm, username_out, groupname_out, (S_IRUSR | S_IRGRP | S_IROTH | S_IWOTH)); + + free(username); + free(groupname); +} + +/* TEST */ +static void +test_access_setandget2(void **state) +{ + int rc; + test_data_t *tdata = *state; + char *username = NULL, *groupname = NULL; + char *username_out = NULL, *groupname_out = NULL; + mode_t perm; + + rc = testutil_uid2usr(getuid(), &username); + assert_int_equal(rc, SR_ERR_OK); + + rc = testutil_gid2grp(getgid(), &groupname); + assert_int_equal(rc, SR_ERR_OK); + + /* STARTUP */ + rc = sr_set_module_ds_access(tdata->conn, "plugin", SR_DS_STARTUP, NULL, NULL, S_IRUSR); // user and group from install + assert_int_equal(rc, SR_ERR_OK); + rc = sr_get_module_ds_access(tdata->conn, "plugin", SR_DS_STARTUP, &username_out, &groupname_out, &perm); + assert_int_equal(rc, SR_ERR_OK); + check_access(username, groupname, perm, username_out, groupname_out, S_IRUSR); + + /* RUNNING */ + rc = sr_set_module_ds_access(tdata->conn, "plugin", SR_DS_RUNNING, NULL, NULL, S_IRUSR); // user and group from install + assert_int_equal(rc, SR_ERR_OK); + rc = sr_get_module_ds_access(tdata->conn, "plugin", SR_DS_RUNNING, &username_out, &groupname_out, &perm); + assert_int_equal(rc, SR_ERR_OK); + check_access(username, groupname, perm, username_out, groupname_out, S_IRUSR); + + /* CANDIDATE */ + rc = sr_set_module_ds_access(tdata->conn, "plugin", SR_DS_CANDIDATE, NULL, NULL, S_IRUSR); // user and group from install + assert_int_equal(rc, SR_ERR_OK); + rc = sr_get_module_ds_access(tdata->conn, "plugin", SR_DS_CANDIDATE, &username_out, &groupname_out, &perm); + assert_int_equal(rc, SR_ERR_OK); + check_access(username, groupname, perm, username_out, groupname_out, S_IRUSR); + + /* OPERATIONAL */ + rc = sr_set_module_ds_access(tdata->conn, "plugin", SR_DS_OPERATIONAL, NULL, NULL, S_IRUSR); // user and group from install + assert_int_equal(rc, SR_ERR_OK); + rc = sr_get_module_ds_access(tdata->conn, "plugin", SR_DS_OPERATIONAL, &username_out, &groupname_out, &perm); + assert_int_equal(rc, SR_ERR_OK); + check_access(username, groupname, perm, username_out, groupname_out, S_IRUSR); + + /* FACTORY DEFAULT */ + rc = sr_set_module_ds_access(tdata->conn, "plugin", SR_DS_FACTORY_DEFAULT, NULL, NULL, S_IRUSR); // user and group from install + assert_int_equal(rc, SR_ERR_OK); + rc = sr_get_module_ds_access(tdata->conn, "plugin", SR_DS_FACTORY_DEFAULT, &username_out, &groupname_out, &perm); + assert_int_equal(rc, SR_ERR_OK); + check_access(username, groupname, perm, username_out, groupname_out, S_IRUSR); + + free(username); + free(groupname); +} + +/* TEST */ +static void +test_access_check(void **state) +{ + int rc; + test_data_t *tdata = *state; + int read, write; + + /* STARTUP */ + read = -1; write = -1; + rc = sr_check_module_ds_access(tdata->conn, "plugin", SR_DS_STARTUP, &read, &write); + assert_int_equal(rc, SR_ERR_OK); + assert_int_equal(read, 1); + assert_int_equal(write, 1); + + /* RUNNING */ + read = -1; write = -1; + rc = sr_check_module_ds_access(tdata->conn, "plugin", SR_DS_RUNNING, &read, &write); + assert_int_equal(rc, SR_ERR_OK); + assert_int_equal(read, 1); + assert_int_equal(write, 1); + + /* CANDIDATE */ + read = -1; write = -1; + rc = sr_check_module_ds_access(tdata->conn, "plugin", SR_DS_CANDIDATE, &read, &write); + assert_int_equal(rc, SR_ERR_OK); + assert_int_equal(read, 1); + assert_int_equal(write, 1); + + /* OPERATIONAL */ + read = -1; write = -1; + rc = sr_check_module_ds_access(tdata->conn, "plugin", SR_DS_OPERATIONAL, &read, &write); + assert_int_equal(rc, SR_ERR_OK); + assert_int_equal(read, 1); + assert_int_equal(write, 1); + + /* FACTORY DEFAULT */ + read = -1; write = -1; + rc = sr_check_module_ds_access(tdata->conn, "plugin", SR_DS_FACTORY_DEFAULT, &read, &write); + assert_int_equal(rc, SR_ERR_OK); + assert_int_equal(read, 1); + assert_int_equal(write, 1); +} + +/* TEST */ +static void +test_copy(void **state) +{ + int rc; + test_data_t *tdata = *state; + mode_t perm; + struct lyd_node *node = NULL; + sr_data_t *data = NULL; + char *str1 = NULL; + const char *str2 = "" + "" + "" + "" + "a" + "a" + "" + "" + "b" + "a" + "" + "a" + "" + "" + "" + "" + ""; + + rc = lyd_parse_data_mem(tdata->ctx, str2, LYD_XML, LYD_PARSE_STRICT | LYD_PARSE_ONLY, 0, &node); + assert_int_equal(rc, LY_SUCCESS); + + /* STARTUP */ + rc = sr_session_switch_ds(tdata->sess, SR_DS_STARTUP); + assert_int_equal(rc, SR_ERR_OK); + + rc = sr_replace_config(tdata->sess, "plugin", node, 0); + assert_int_equal(rc, SR_ERR_OK); + + /* RUNNING */ + rc = sr_session_switch_ds(tdata->sess, SR_DS_RUNNING); + assert_int_equal(rc, SR_ERR_OK); + + rc = sr_copy_config(tdata->sess, "plugin", SR_DS_STARTUP, 0); + assert_int_equal(rc, SR_ERR_OK); + + // load module + rc = sr_get_data(tdata->sess, "/plugin:*", 0, 0, 0, &data); + assert_int_equal(rc, SR_ERR_OK); + + rc = lyd_print_mem(&str1, data->tree, LYD_XML, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_WD_ALL_TAG | LYD_PRINT_SHRINK); + assert_int_equal(rc, LY_SUCCESS); + sr_release_data(data); + + // compare + assert_string_equal(str1, str2); + free(str1); + + /* STARTUP */ + rc = sr_session_switch_ds(tdata->sess, SR_DS_STARTUP); + assert_int_equal(rc, SR_ERR_OK); + + // load module + rc = sr_get_data(tdata->sess, "/plugin:*", 0, 0, 0, &data); + assert_int_equal(rc, SR_ERR_OK); + + rc = lyd_print_mem(&str1, data->tree, LYD_XML, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_WD_ALL_TAG | LYD_PRINT_SHRINK); + assert_int_equal(rc, LY_SUCCESS); + sr_release_data(data); + + // compare + assert_string_equal(str1, str2); + free(str1); + + // check whether the access rights were copied (should not happen) + rc = sr_get_module_ds_access(tdata->conn, "plugin", SR_DS_RUNNING, NULL, NULL, &perm); + assert_int_equal(rc, SR_ERR_OK); + assert_true(perm == (S_IRUSR | S_IWUSR)); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_dummy), + cmocka_unit_test_teardown(test_load_empty, teardown_store_oper), + cmocka_unit_test_teardown(test_store_example, teardown_store), + cmocka_unit_test_teardown(test_store_complex, teardown_store), + cmocka_unit_test_teardown(test_store_oper, teardown_store_oper), + cmocka_unit_test(test_access_get), + cmocka_unit_test_teardown(test_access_setandget, teardown_access), + cmocka_unit_test_teardown(test_access_setandget2, teardown_access), + cmocka_unit_test(test_access_check), + cmocka_unit_test_teardown(test_copy, teardown_store), + }; + + int rc; + int err = 0; + uint32_t i; + uint32_t plgnum = sr_ds_plugin_int_count(); + struct timeval start, end; + + test_log_init(); + for (i = 0; i < plgnum; ++i) { + plg_name = sr_internal_ds_plugins[i]->name; + printf("\nTesting plugin %s\n", plg_name); + gettimeofday(&start, NULL); + if ((rc = cmocka_run_group_tests(tests, setup, teardown))) { + err = rc; + } + gettimeofday(&end, NULL); + if (end.tv_usec > start.tv_usec) { + printf("Tests of plugin %s lasted: %ld microseconds\n", plg_name, (end.tv_sec - start.tv_sec)*1000000 + end.tv_usec - start.tv_usec); + } + else { + printf("Tests of plugin %s lasted: %ld microseconds\n", plg_name, (end.tv_sec - start.tv_sec - 1)*1000000 + 1000000 + end.tv_usec - start.tv_usec); + } + } + + return err; +}