diff -r 45da884269c8 -r 3c90dfc35f06 tests/test_properties.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test_properties.c Sat Oct 12 19:34:19 2024 +0200 @@ -0,0 +1,380 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2024 Mike Becker, Olaf Wintermann All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cx/test.h" + +#include "cx/properties.h" + +CX_TEST(test_cx_properties_init) { + CxProperties prop; + CX_TEST_DO { + cxPropertiesInitDefault(&prop); + + CX_TEST_ASSERT(prop.config.delimiter == '='); + CX_TEST_ASSERT(prop.config.comment1 == '#'); + CX_TEST_ASSERT(prop.config.comment2 == 0); + CX_TEST_ASSERT(prop.config.comment3 == 0); + CX_TEST_ASSERT(prop.flags == 0); + CX_TEST_ASSERT(prop.text == NULL); + CX_TEST_ASSERT(prop.buf == NULL); + + cxPropertiesDestroy(&prop); + } +} + +CX_TEST(test_cx_properties_next) { + const char *tests[] = { + "name = value\n", + "name=value\n", + "n=value\n", + "name=v\n", + "n=v\n", + "name = value # comment\n", + "#comment\nn=v\n", + "# comment1\n# comment2\n\n \n\nname = value\n", + " name = value\n", + "name = value\n\n" + }; + + const char *keys[] = { + "name", + "name", + "n", + "name", + "n", + "name", + "n", + "name", + "name", + "name" + }; + + const char *values[] = { + "value", + "value", + "value", + "v", + "v", + "value", + "v", + "value", + "value", + "value" + }; + + CxProperties prop; + cxPropertiesInitDefault(&prop); + enum cx_properties_status result; + cxstring key; + cxstring value; + CX_TEST_DO { + for (int i = 0; i < 10; i++) { + cxPropertiesInput(&prop, tests[i], strlen(tests[i])); + CX_TEST_ASSERT(prop.text == tests[i]); + CX_TEST_ASSERT(prop.text_size == strlen(tests[i])); + CX_TEST_ASSERT(prop.text_pos == 0); + + result = cxPropertiesNext(&prop, &key, &value); + cxstring k = cx_str(keys[i]); + cxstring v = cx_str(values[i]); + CX_TEST_ASSERT(result == CX_PROPERTIES_NO_ERROR); + CX_TEST_ASSERT(0 == cx_strcmp(key, k)); + CX_TEST_ASSERT(0 == cx_strcmp(value, v)); + + result = cxPropertiesNext(&prop, &key, &value); + CX_TEST_ASSERT(result == CX_PROPERTIES_NO_DATA); + } + } + cxPropertiesDestroy(&prop); +} + +CX_TEST(test_cx_properties_next_multi) { + const char *keys[] = { + "a", + "b", + "c", + "uap", + "name", + "key1", + "key2", + "key3" + }; + + const char *values[] = { + "a value", + "b value", + "core", + "core", + "ucx", + "value1", + "value2", + "value3" + }; + + const char *str = "#\n" + "# properties\n" + "# contains key/value pairs\n" + "#\n" + "a = a value\n" + "b = b value\n" + "c = core\n" + "\n# test\n" + "uap = core\n" + "name = ucx\n" + "# no = property\n" + "key1 = value1\n" + "#key1 = wrong value\n" + "#key2 = not value 2\n" + "key2 = value2\n" + "\n\n\n \n key3=value3\n"; + + CxProperties prop; + cxPropertiesInitDefault(&prop); + enum cx_properties_status result; + cxstring key; + cxstring value; + + CX_TEST_DO { + result = cxPropertiesNext(&prop, &key, &value); + CX_TEST_ASSERT(result == CX_PROPERTIES_NULL_INPUT); + cxPropertiesInput(&prop, str, strlen(str)); + for (int i = 0; i < 8; i++) { + result = cxPropertiesNext(&prop, &key, &value); + CX_TEST_ASSERT(result == CX_PROPERTIES_NO_ERROR); + CX_TEST_ASSERT(!cx_strcmp(key, cx_str(keys[i]))); + CX_TEST_ASSERT(!cx_strcmp(value, cx_str(values[i]))); + } + result = cxPropertiesNext(&prop, &key, &value); + CX_TEST_ASSERT(result == CX_PROPERTIES_NO_DATA); + } + cxPropertiesDestroy(&prop); +} + +CX_TEST(test_cx_properties_next_part) { + CxProperties prop; + cxPropertiesInitDefault(&prop); + enum cx_properties_status result; + cxstring key; + cxstring value; + const char *str; + + CX_TEST_DO { + str = ""; + cxPropertiesFill(&prop, str, strlen(str)); + result = cxPropertiesNext(&prop, &key, &value); + CX_TEST_ASSERT(result == CX_PROPERTIES_NO_DATA); + + str = " \n"; + cxPropertiesFill(&prop, str, strlen(str)); + result = cxPropertiesNext(&prop, &key, &value); + CX_TEST_ASSERT(result == CX_PROPERTIES_NO_DATA); + + str = "name"; + cxPropertiesFill(&prop, str, strlen(str)); + result = cxPropertiesNext(&prop, &key, &value); + CX_TEST_ASSERT(result == CX_PROPERTIES_INCOMPLETE_DATA); + + str = " "; + cxPropertiesFill(&prop, str, strlen(str)); + result = cxPropertiesNext(&prop, &key, &value); + CX_TEST_ASSERT(result == CX_PROPERTIES_INCOMPLETE_DATA); + + // call fill twice in a row + str = "= "; + cxPropertiesFill(&prop, str, strlen(str)); + str = "value"; + cxPropertiesFill(&prop, str, strlen(str)); + result = cxPropertiesNext(&prop, &key, &value); + CX_TEST_ASSERT(result == CX_PROPERTIES_INCOMPLETE_DATA); + + str = "\n"; + cxPropertiesFill(&prop, str, strlen(str)); + result = cxPropertiesNext(&prop, &key, &value); + CX_TEST_ASSERT(result == CX_PROPERTIES_NO_ERROR); + CX_TEST_ASSERT(0 == cx_strcmp(key, cx_str("name"))); + CX_TEST_ASSERT(0 == cx_strcmp(value, cx_str("value"))); + + // second round + str = "#comment\n"; + cxPropertiesFill(&prop, str, strlen(str)); + result = cxPropertiesNext(&prop, &key, &value); + CX_TEST_ASSERT(result == CX_PROPERTIES_NO_DATA); + + str = "#comment\nname2 = "; + cxPropertiesFill(&prop, str, strlen(str)); + result = cxPropertiesNext(&prop, &key, &value); + CX_TEST_ASSERT(result == CX_PROPERTIES_INCOMPLETE_DATA); + + str = "value2\na = b\n"; + cxPropertiesFill(&prop, str, strlen(str)); + result = cxPropertiesNext(&prop, &key, &value); + CX_TEST_ASSERT(result == CX_PROPERTIES_NO_ERROR); + CX_TEST_ASSERT(0 == cx_strcmp(key, cx_str("name2"))); + CX_TEST_ASSERT(0 == cx_strcmp(value, cx_str("value2"))); + + result = cxPropertiesNext(&prop, &key, &value); + CX_TEST_ASSERT(result == CX_PROPERTIES_NO_ERROR); + CX_TEST_ASSERT(0 == cx_strcmp(key, cx_str("a"))); + CX_TEST_ASSERT(0 == cx_strcmp(value, cx_str("b"))); + + str = "# comment\n#\n#\ntests = "; + cxPropertiesFill(&prop, str, strlen(str)); + result = cxPropertiesNext(&prop, &key, &value); + CX_TEST_ASSERT(result == CX_PROPERTIES_INCOMPLETE_DATA); + + str = "test1 "; + cxPropertiesFill(&prop, str, strlen(str)); + result = cxPropertiesNext(&prop, &key, &value); + CX_TEST_ASSERT(result == CX_PROPERTIES_INCOMPLETE_DATA); + + str = "test2 test3 test4\n"; + cxPropertiesFill(&prop, str, strlen(str)); + result = cxPropertiesNext(&prop, &key, &value); + CX_TEST_ASSERT(result == CX_PROPERTIES_NO_ERROR); + CX_TEST_ASSERT(0 == cx_strcmp(key, cx_str("tests"))); + CX_TEST_ASSERT(0 == cx_strcmp(value, cx_str("test1 test2 test3 test4"))); + + // test if cxPropertiesNext finds a name/value after a comment + str = "# just a comment"; + cxPropertiesFill(&prop, str, strlen(str)); + result = cxPropertiesNext(&prop, &key, &value); + CX_TEST_ASSERT(result == CX_PROPERTIES_INCOMPLETE_DATA); + + str = " in 3"; + cxPropertiesFill(&prop, str, strlen(str)); + result = cxPropertiesNext(&prop, &key, &value); + CX_TEST_ASSERT(result == CX_PROPERTIES_INCOMPLETE_DATA); + + str = " parts\nx = 1\n"; + cxPropertiesFill(&prop, str, strlen(str)); + result = cxPropertiesNext(&prop, &key, &value); + CX_TEST_ASSERT(result == CX_PROPERTIES_NO_ERROR); + CX_TEST_ASSERT(0 == cx_strcmp(key, cx_str("x"))); + CX_TEST_ASSERT(0 == cx_strcmp(value, cx_str("1"))); + + // finally we are done + result = cxPropertiesNext(&prop, &key, &value); + CX_TEST_ASSERT(result == CX_PROPERTIES_NO_DATA); + } + cxPropertiesDestroy(&prop); +} + +CX_TEST(test_ucx_properties_next_long_lines) { + CxProperties prop; + cxPropertiesInitDefault(&prop); + enum cx_properties_status result; + cxstring key; + cxstring value; + + size_t key_len = 512; + char *long_key = (char*)malloc(key_len); + memset(long_key, 'a', 70); + memset(long_key + 70, 'b', 242); + memset(long_key + 312, 'c', 200); + + size_t value_len = 2048; + char *long_value = (char*)malloc(value_len); + memset(long_value, 'x', 1024); + memset(long_value+1024, 'y', 1024); + + CX_TEST_DO { + cxPropertiesFill(&prop, long_key, 10); + result = cxPropertiesNext(&prop, &key, &value); + CX_TEST_ASSERT(result == CX_PROPERTIES_INCOMPLETE_DATA); + + cxPropertiesFill(&prop, long_key + 10, 202); + result = cxPropertiesNext(&prop, &key, &value); + CX_TEST_ASSERT(result == CX_PROPERTIES_INCOMPLETE_DATA); + + cxPropertiesFill(&prop, long_key + 212, 200); + result = cxPropertiesNext(&prop, &key, &value); + CX_TEST_ASSERT(result == CX_PROPERTIES_INCOMPLETE_DATA); + + cxPropertiesFill(&prop, long_key + 412, 100); + result = cxPropertiesNext(&prop, &key, &value); + CX_TEST_ASSERT(result == CX_PROPERTIES_INCOMPLETE_DATA); + + const char *str = " = "; + cxPropertiesFill(&prop, str, strlen(str)); + result = cxPropertiesNext(&prop, &key, &value); + CX_TEST_ASSERT(result == CX_PROPERTIES_INCOMPLETE_DATA); + + cxPropertiesFill(&prop, long_value, 512); + result = cxPropertiesNext(&prop, &key, &value); + CX_TEST_ASSERT(result == CX_PROPERTIES_INCOMPLETE_DATA); + + cxPropertiesFill(&prop, long_value + 512, 1024); + result = cxPropertiesNext(&prop, &key, &value); + CX_TEST_ASSERT(result == CX_PROPERTIES_INCOMPLETE_DATA); + + cxPropertiesFill(&prop, long_value + 1536, 512); + result = cxPropertiesNext(&prop, &key, &value); + CX_TEST_ASSERT(result == CX_PROPERTIES_INCOMPLETE_DATA); + + str = "\n#comment\nkey = value\n"; + cxPropertiesFill(&prop, str, strlen(str)); + result = cxPropertiesNext(&prop, &key, &value); + cxstring k = cx_strn(long_key, key_len); + cxstring v = cx_strn(long_value, value_len); + CX_TEST_ASSERT(result == CX_PROPERTIES_NO_ERROR); + CX_TEST_ASSERT(0 == cx_strcmp(key, k)); + CX_TEST_ASSERT(0 == cx_strcmp(value, v)); + + result = cxPropertiesNext(&prop, &key, &value); + CX_TEST_ASSERT(result == CX_PROPERTIES_NO_ERROR); + CX_TEST_ASSERT(0 == cx_strcmp(key, cx_str("key"))); + CX_TEST_ASSERT(0 == cx_strcmp(value, cx_str("value"))); + + result = cxPropertiesNext(&prop, &key, &value); + CX_TEST_ASSERT(result == CX_PROPERTIES_NO_DATA); + + CX_TEST_ASSERT(prop.buf != NULL); + CX_TEST_ASSERT(prop.buf_capacity > 0); + CX_TEST_ASSERT(prop.buf_size == 0); + cxPropertiesDestroy(&prop); + + CX_TEST_ASSERT(prop.buf == NULL); + CX_TEST_ASSERT(prop.buf_capacity == 0); + CX_TEST_ASSERT(prop.buf_size == 0); + } + + free(long_key); + free(long_value); +} + +CxTestSuite *cx_test_suite_properties(void) { + CxTestSuite *suite = cx_test_suite_new("properties"); + + cx_test_register(suite, test_cx_properties_init); + cx_test_register(suite, test_cx_properties_next); + cx_test_register(suite, test_cx_properties_next_multi); + cx_test_register(suite, test_cx_properties_next_part); + cx_test_register(suite, test_ucx_properties_next_long_lines); + + return suite; +}