tests/test_properties.c

Sat, 12 Oct 2024 19:41:04 +0200

author
Mike Becker <universe@uap-core.de>
date
Sat, 12 Oct 2024 19:41:04 +0200
changeset 925
fd6e191f3268
parent 924
3c90dfc35f06
child 928
d2d42cb1d59e
permissions
-rw-r--r--

fix invalid reads when removing linked list nodes

/*
 * 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;
}

mercurial