From 3730861426299e7443aa5f950002056d1b3b020e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20=C5=BDidek?= Date: Tue, 15 Mar 2016 17:35:05 +0100 Subject: [PATCH 03/10] tests: Tests for rules/validators infrastructure Ticket: https://fedorahosted.org/sssd/ticket/133 Signed-off-by: Lukas Slebodnik --- Makefile.am | 12 +- ini/ini_validators_ut_check.c | 561 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 572 insertions(+), 1 deletion(-) create mode 100644 ini/ini_validators_ut_check.c diff --git a/Makefile.am b/Makefile.am index 0652cde2bd1b52f9f3d02cd4d55e7e1e6fa79e70..bd6c42338d0b594609c3078f95c88fe258531d92 100644 --- a/Makefile.am +++ b/Makefile.am @@ -69,10 +69,15 @@ libpath_utils_la_LDFLAGS = \ -Wl,--version-script=$(top_srcdir)/path_utils/libpath_utils.sym if HAVE_CHECK - check_PROGRAMS += path_utils_ut ini_configmod_ut_check ini_parse_ut_check + check_PROGRAMS += path_utils_ut \ + ini_configmod_ut_check \ + ini_parse_ut_check \ + ini_validators_ut_check \ + $(NULL) TESTS += path_utils_ut \ ini_configmod_ut_check \ ini_parse_ut_check \ + ini_validators_ut_check \ $(NULL) endif @@ -357,6 +362,11 @@ ini_parse_ut_check_LDADD = libini_config.la \ libref_array.la \ $(CHECK_LIBS) +ini_validators_ut_check_SOURCES = ini/ini_validators_ut_check.c +ini_validators_ut_check_CFLAGS = $(AM_CFLAGS) $(CHECK_CFLAGS) +ini_validators_ut_check_LDADD = libini_config.la $(CHECK_LIBS) + + ini_save_ut_SOURCES = ini/ini_save_ut.c ini_save_ut_LDADD = libini_config.la libcollection.la \ libbasicobjects.la libpath_utils.la libref_array.la diff --git a/ini/ini_validators_ut_check.c b/ini/ini_validators_ut_check.c new file mode 100644 index 0000000000000000000000000000000000000000..2a6bbb9f606026001234597eaffc6d9b378c1d0d --- /dev/null +++ b/ini/ini_validators_ut_check.c @@ -0,0 +1,561 @@ +/* + INI LIBRARY + + Unit test for the configuration file validators API. + + Copyright (C) Michal Zidek 2016 + + INI Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + INI Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with INI Library. If not, see . +*/ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +/* #define TRACE_LEVEL 7 */ +#define TRACE_HOME +#include "trace.h" +#include "ini_configobj.h" +#include "ini_config_priv.h" + +#define TEST_DIR_PATH "" +#define TEST_RULES_FILE TEST_DIR_PATH"test_rules.ini" + +static void create_rules_from_str(const char *rules, + struct ini_cfgobj **_rules_obj) +{ + FILE *file; + size_t written; + int ret; + + /* We want to test actual reading from file using + * ini_rules_read_from_file, so we create the file here */ + file = fopen(TEST_RULES_FILE, "w"); + fail_if(file == NULL, "fopen() failed: %s", strerror(errno)); + written = fwrite(rules, 1, strlen(rules), file); + fail_unless(written == strlen(rules)); + + /* allow reading */ + ret = chmod(TEST_RULES_FILE, 0664); + fail_unless(ret == 0, "chmod() failed: %s", strerror(errno)); + + fclose(file); + + ret = ini_rules_read_from_file(TEST_RULES_FILE, _rules_obj); + fail_unless(ret == 0, "read_rules_from_file() failed: %s", strerror(ret)); +} + +static struct ini_cfgobj *get_ini_config_from_str(char input_data[], + size_t input_data_len) +{ + struct ini_cfgobj *in_cfg; + struct ini_cfgfile *file_ctx; + int ret; + + ret = ini_config_create(&in_cfg); + fail_unless(ret == EOK, "Failed to create config. Error %d.\n", ret); + + ret = ini_config_file_from_mem(input_data, input_data_len, &file_ctx); + fail_unless(ret == EOK, "Failed to load config. Error %d.\n", ret); + + ret = ini_config_parse(file_ctx, INI_STOP_ON_NONE, INI_MV1S_ALLOW, 0, + in_cfg); + fail_unless(ret == EOK, "Failed to parse config. Error %d.\n", ret); + + ini_config_file_destroy(file_ctx); + + return in_cfg; +} + +START_TEST(test_ini_errobj) +{ + struct ini_errobj *errobj; + int ret; + const char TEST_MSG1[] = "Test message one."; + const char TEST_MSG2[] = "Test message two."; + const char TEST_MSG3[] = "Test message three."; + + ret = ini_errobj_create(&errobj); + fail_unless(ret == 0, "ini_errobj_create() failed: %s", strerror(ret)); + + /* We just created the errobj, it should be empty */ + fail_unless(ini_errobj_no_more_msgs(errobj)); + + /* Now add three messages, after adding each message, + * check if the errobj has correct content. */ + ret = ini_errobj_add_msg(errobj, TEST_MSG1); + fail_if(ret != 0, "ini_errobj_add_msg() failed: %s", strerror(ret)); + fail_if(ini_errobj_no_more_msgs(errobj)); + ret = strcmp(TEST_MSG1, ini_errobj_get_msg(errobj)); + fail_if(ret != 0, "TEST_MSG1 was not found."); + ini_errobj_next(errobj); + fail_unless(ini_errobj_no_more_msgs(errobj)); + + ret = ini_errobj_add_msg(errobj, TEST_MSG2); + fail_if(ret != 0, "ini_errobj_add_msg() failed: %s", strerror(ret)); + ini_errobj_reset(errobj); /* strart from first message */ + fail_if(ini_errobj_no_more_msgs(errobj)); + ret = strcmp(TEST_MSG1, ini_errobj_get_msg(errobj)); + fail_if(ret != 0, "TEST_MSG1 was not found."); + ini_errobj_next(errobj); + fail_if(ini_errobj_no_more_msgs(errobj)); + ret = strcmp(TEST_MSG2, ini_errobj_get_msg(errobj)); + fail_if(ret != 0, "TEST_MSG2 was not found."); + ini_errobj_next(errobj); + fail_unless(ini_errobj_no_more_msgs(errobj)); + + ret = ini_errobj_add_msg(errobj, TEST_MSG3); + fail_if(ret != 0, "ini_errobj_add_msg() failed: %s", strerror(ret)); + ini_errobj_reset(errobj); /* strart from first message */ + fail_if(ini_errobj_no_more_msgs(errobj)); + ret = strcmp(TEST_MSG1, ini_errobj_get_msg(errobj)); + fail_if(ret != 0, "TEST_MSG1 was not found."); + ini_errobj_next(errobj); + fail_if(ini_errobj_no_more_msgs(errobj)); + ret = strcmp(TEST_MSG2, ini_errobj_get_msg(errobj)); + fail_if(ret != 0, "TEST_MSG2 was not found."); + ini_errobj_next(errobj); + fail_if(ini_errobj_no_more_msgs(errobj)); + ret = strcmp(TEST_MSG3, ini_errobj_get_msg(errobj)); + fail_if(ret != 0, "TEST_MSG3 was not found."); + ini_errobj_next(errobj); + fail_unless(ini_errobj_no_more_msgs(errobj)); + + ini_errobj_destroy(&errobj); +} +END_TEST + +START_TEST(test_ini_noerror) +{ + struct ini_cfgobj *rules_obj; + struct ini_cfgobj *cfg_obj; + struct ini_errobj *errobj; + int ret; + + char input_rules[] = + "[rule/always_succeed]\n" + "validator = ini_dummy_noerror\n"; + + char input_cfg[] = + "[section]\n" + "# Content of this file should not matter\n"; + + create_rules_from_str(input_rules, &rules_obj); + cfg_obj = get_ini_config_from_str(input_cfg, sizeof(input_cfg)); + + ret = ini_errobj_create(&errobj); + fail_unless(ret == 0, "ini_errobj_create() failed: %s", strerror(ret)); + + ret = ini_rules_check(rules_obj, cfg_obj, NULL, 0, errobj); + fail_unless(ret == 0, "ini_rules_check() failed: %s", strerror(ret)); + fail_unless(ini_errobj_no_more_msgs(errobj)); + + ini_errobj_destroy(&errobj); + ini_config_destroy(cfg_obj); + ini_rules_destroy(rules_obj); +} +END_TEST + +START_TEST(test_ini_error) +{ + struct ini_cfgobj *rules_obj; + struct ini_cfgobj *cfg_obj; + struct ini_errobj *errobj; + int ret; + const char *errmsg; + + char input_rules[] = + "[rule/generate_error]\n" + "validator = ini_dummy_error\n"; + + char input_cfg[] = + "[section]\n" + "# Content of this file should not matter\n"; + + create_rules_from_str(input_rules, &rules_obj); + cfg_obj = get_ini_config_from_str(input_cfg, sizeof(input_cfg)); + + ret = ini_errobj_create(&errobj); + fail_unless(ret == 0, "ini_errobj_create() failed: %s", strerror(ret)); + + ret = ini_rules_check(rules_obj, cfg_obj, NULL, 0, errobj); + fail_unless(ret == 0, "ini_rules_check() failed: %s", strerror(ret)); + + /* Should generate exactly one error */ + fail_if(ini_errobj_no_more_msgs(errobj)); + errmsg = ini_errobj_get_msg(errobj); + ret = strcmp(errmsg, "[rule/generate_error]: Error"); + fail_unless(ret == 0, "Got msg: %s", errmsg); + ini_errobj_next(errobj); + fail_unless(ini_errobj_no_more_msgs(errobj)); + + ini_errobj_destroy(&errobj); + ini_config_destroy(cfg_obj); + ini_rules_destroy(rules_obj); +} +END_TEST + +START_TEST(test_unknown_validator) +{ + struct ini_cfgobj *rules_obj; + struct ini_cfgobj *cfg_obj; + struct ini_errobj *errobj; + int ret; + + char input_rules[] = + "[rule/always_succeed]\n" + "validator = nonexistent_validator\n"; + + char input_cfg[] = + "[section]\n" + "# Content of this file should not matter\n"; + + create_rules_from_str(input_rules, &rules_obj); + cfg_obj = get_ini_config_from_str(input_cfg, sizeof(input_cfg)); + + ret = ini_errobj_create(&errobj); + fail_unless(ret == 0, "ini_errobj_create() failed: %s", strerror(ret)); + + ret = ini_rules_check(rules_obj, cfg_obj, NULL, 0, errobj); + fail_unless(ret == 0, "ini_rules_check() failed: %s", strerror(ret)); + + /* Should generate exactly one error */ + fail_if(ini_errobj_no_more_msgs(errobj)); + ini_errobj_next(errobj); + fail_unless(ini_errobj_no_more_msgs(errobj)); + + ini_errobj_destroy(&errobj); + ini_config_destroy(cfg_obj); + ini_rules_destroy(rules_obj); +} +END_TEST + +static int custom_noerror(const char *rule_name, + struct ini_cfgobj *rules_obj, + struct ini_cfgobj *config_obj, + struct ini_errobj *errobj) +{ + return 0; +} + +static int custom_error(const char *rule_name, + struct ini_cfgobj *rules_obj, + struct ini_cfgobj *config_obj, + struct ini_errobj *errobj) +{ + return ini_errobj_add_msg(errobj, "Error"); +} + +START_TEST(test_custom_noerror) +{ + struct ini_cfgobj *rules_obj; + struct ini_cfgobj *cfg_obj; + struct ini_errobj *errobj; + int ret; + struct ini_validator noerror = { "custom_noerror", custom_noerror }; + + char input_rules[] = + "[rule/custom_succeed]\n" + "validator = custom_noerror\n"; + + char input_cfg[] = + "[section]\n" + "# Content of this file should not matter\n"; + + create_rules_from_str(input_rules, &rules_obj); + cfg_obj = get_ini_config_from_str(input_cfg, sizeof(input_cfg)); + + ret = ini_errobj_create(&errobj); + fail_unless(ret == 0, "ini_errobj_create() failed: %s", strerror(ret)); + + /* Pass the custom validator to ini_rules_check() */ + ret = ini_rules_check(rules_obj, cfg_obj, &noerror, 1, errobj); + fail_unless(ret == 0, "ini_rules_check() failed: %s", strerror(ret)); + + /* Should generate no errors */ + fail_unless(ini_errobj_no_more_msgs(errobj)); + + ini_errobj_destroy(&errobj); + ini_config_destroy(cfg_obj); + ini_rules_destroy(rules_obj); +} +END_TEST + +START_TEST(test_custom_error) +{ + struct ini_cfgobj *rules_obj; + struct ini_cfgobj *cfg_obj; + struct ini_errobj *errobj; + int ret; + struct ini_validator error = { "custom_error", custom_error }; + const char *errmsg; + + char input_rules[] = + "[rule/custom_error]\n" + "validator = custom_error\n"; + + char input_cfg[] = + "[section]\n" + "# Content of this file should not matter\n"; + + create_rules_from_str(input_rules, &rules_obj); + cfg_obj = get_ini_config_from_str(input_cfg, sizeof(input_cfg)); + + ret = ini_errobj_create(&errobj); + fail_unless(ret == 0, "ini_errobj_create() failed: %s", strerror(ret)); + + /* Pass the custom validator to ini_rules_check() */ + ret = ini_rules_check(rules_obj, cfg_obj, &error, 1, errobj); + fail_unless(ret == 0, "ini_rules_check() failed: %s", strerror(ret)); + + /* Should generate one error */ + fail_if(ini_errobj_no_more_msgs(errobj)); + errmsg = ini_errobj_get_msg(errobj); + ret = strcmp(errmsg, "[rule/custom_error]: Error"); + fail_unless(ret == 0, "Got msg: %s", errmsg); + ini_errobj_next(errobj); + fail_unless(ini_errobj_no_more_msgs(errobj)); + + ini_errobj_destroy(&errobj); + ini_config_destroy(cfg_obj); + ini_rules_destroy(rules_obj); +} +END_TEST + +START_TEST(test_ini_allowed_options_ok) +{ + struct ini_cfgobj *rules_obj; + struct ini_cfgobj *cfg_obj; + struct ini_errobj *errobj; + int ret; + + /* Only bar and baz are allowed for foo section */ + char input_rules[] = + "[rule/options_for_foo]\n" + "validator = ini_allowed_options\n" + "section_re = ^foo$\n" + "option = bar\n" + "option = baz\n"; + + /* Should check only foo section, other sections are + * irrelevant and can contain any option */ + char input_cfg[] = + "[foo]\n" + "bar = 0\n" + "baz = 0\n" + "[oof]\n" + "opt1 = 1\n"; + + create_rules_from_str(input_rules, &rules_obj); + cfg_obj = get_ini_config_from_str(input_cfg, sizeof(input_cfg)); + + ret = ini_errobj_create(&errobj); + fail_unless(ret == 0, "ini_errobj_create() failed: %s", strerror(ret)); + + ret = ini_rules_check(rules_obj, cfg_obj, NULL, 0, errobj); + fail_unless(ret == 0, "ini_rules_check() failed: %s", strerror(ret)); + + /* Should generate no errors */ + fail_unless(ini_errobj_no_more_msgs(errobj)); + + ini_errobj_destroy(&errobj); + ini_config_destroy(cfg_obj); + ini_rules_destroy(rules_obj); +} +END_TEST + +START_TEST(test_ini_allowed_options_no_section) +{ + struct ini_cfgobj *rules_obj; + struct ini_cfgobj *cfg_obj; + struct ini_errobj *errobj; + int ret; + size_t num_err; + + /* Ommit section_re to generate error */ + char input_rules[] = + "[rule/options_for_foo]\n" + "validator = ini_allowed_options\n" + /* "section_re = ^foo$\n" */ + "option = bar\n" + "option = baz\n"; + + /* Make 4 typos */ + char input_cfg[] = + "[foo]\n" + "bar = 0\n" + "baz = 0\n"; + + create_rules_from_str(input_rules, &rules_obj); + cfg_obj = get_ini_config_from_str(input_cfg, sizeof(input_cfg)); + + ret = ini_errobj_create(&errobj); + fail_unless(ret == 0, "ini_errobj_create() failed: %s", strerror(ret)); + + ret = ini_rules_check(rules_obj, cfg_obj, NULL, 0, errobj); + fail_unless(ret == 0, "ini_rules_check() failed: %s", strerror(ret)); + + /* Should generate 2 errors (one from rules_check and one + * from the validator itself) */ + fail_if(ini_errobj_no_more_msgs(errobj)); + + num_err = ini_errobj_count(errobj); + fail_unless(num_err == 2, "Expected 2 errors, got %d", num_err); + + ini_errobj_destroy(&errobj); + ini_config_destroy(cfg_obj); + ini_rules_destroy(rules_obj); +} +END_TEST + +START_TEST(test_ini_allowed_options_typos) +{ + struct ini_cfgobj *rules_obj; + struct ini_cfgobj *cfg_obj; + struct ini_errobj *errobj; + int ret; + size_t num_err; + + /* Only bar and baz are allowed for foo section */ + char input_rules[] = + "[rule/options_for_foo]\n" + "validator = ini_allowed_options\n" + "section_re = ^foo$\n" + "option = bar\n" + "option = baz\n"; + + /* Make 4 typos */ + char input_cfg[] = + "[foo]\n" + "br = 0\n" + "bra = 0\n" + "abr = 0\n" + "abz = 0\n"; + + create_rules_from_str(input_rules, &rules_obj); + cfg_obj = get_ini_config_from_str(input_cfg, sizeof(input_cfg)); + + ret = ini_errobj_create(&errobj); + fail_unless(ret == 0, "ini_errobj_create() failed: %s", strerror(ret)); + + ret = ini_rules_check(rules_obj, cfg_obj, NULL, 0, errobj); + fail_unless(ret == 0, "ini_rules_check() failed: %s", strerror(ret)); + + /* Should generate 4 errors */ + fail_if(ini_errobj_no_more_msgs(errobj)); + + num_err = ini_errobj_count(errobj); + fail_unless(num_err == 4, "Expected 4 errors, got %d", num_err); + + ini_errobj_destroy(&errobj); + ini_config_destroy(cfg_obj); + ini_rules_destroy(rules_obj); +} +END_TEST + +START_TEST(test_ini_allowed_sections_str_ok) +{ + struct ini_cfgobj *rules_obj; + struct ini_cfgobj *cfg_obj; + struct ini_errobj *errobj; + int ret; + + /* Only bar and baz are allowed for foo section */ + char input_rules[] = + "[rule/section_list]\n" + "validator = ini_allowed_sections\n" + "section = foo\n" + "section = bar\n"; + + /* Make 4 typos */ + char input_cfg[] = + "[foo]\n" + "br = 0\n" + "bra = 0\n" + "[bar]\n" + "abz = 0\n"; + + create_rules_from_str(input_rules, &rules_obj); + cfg_obj = get_ini_config_from_str(input_cfg, sizeof(input_cfg)); + + ret = ini_errobj_create(&errobj); + fail_unless(ret == 0, "ini_errobj_create() failed: %s", strerror(ret)); + + ret = ini_rules_check(rules_obj, cfg_obj, NULL, 0, errobj); + fail_unless(ret == 0, "ini_rules_check() failed: %s", strerror(ret)); + + /* validator is not implemented yet. */ + ret = strcmp("Rule 'rule/section_list' uses unknown validator " + "'ini_allowed_sections'.", + ini_errobj_get_msg(errobj)); + fail_unless(ret == 0, + "Unexpected errors found: [%s]", ini_errobj_get_msg(errobj)); + ini_errobj_next(errobj); + + /* Should generate 0 errors */ + fail_unless(ini_errobj_no_more_msgs(errobj), + "Unexpected errors found: [%s]", ini_errobj_get_msg(errobj)); + + ini_errobj_destroy(&errobj); + ini_config_destroy(cfg_obj); + ini_rules_destroy(rules_obj); +} +END_TEST + +static Suite *ini_validators_utils_suite(void) +{ + Suite *s = suite_create("ini_validators"); + + TCase *tc_infrastructure = tcase_create("infrastructure"); + tcase_add_test(tc_infrastructure, test_ini_errobj); + tcase_add_test(tc_infrastructure, test_ini_noerror); + tcase_add_test(tc_infrastructure, test_ini_error); + tcase_add_test(tc_infrastructure, test_unknown_validator); + tcase_add_test(tc_infrastructure, test_custom_noerror); + tcase_add_test(tc_infrastructure, test_custom_error); + + TCase *tc_allowed_options = tcase_create("ini_allowed_options"); + tcase_add_test(tc_allowed_options, test_ini_allowed_options_ok); + tcase_add_test(tc_allowed_options, test_ini_allowed_options_no_section); + tcase_add_test(tc_allowed_options, test_ini_allowed_options_typos); + + TCase *tc_allowed_sections = tcase_create("ini_allowed_sections"); + tcase_add_test(tc_allowed_sections, test_ini_allowed_sections_str_ok); + + suite_add_tcase(s, tc_infrastructure); + suite_add_tcase(s, tc_allowed_options); + suite_add_tcase(s, tc_allowed_sections); + + return s; +} + +int main(void) +{ + int number_failed; + + Suite *s = ini_validators_utils_suite(); + SRunner *sr = srunner_create(s); + /* If CK_VERBOSITY is set, use that, otherwise it defaults to CK_NORMAL */ + srunner_run_all(sr, CK_ENV); + number_failed = srunner_ntests_failed(sr); + srunner_free(sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} -- 2.7.4