UnitTestBot / UTBotCpp

Tool that generates unit test by C/C++ source code, trying to reach all branches and maximize code coverage
Apache License 2.0
158 stars 27 forks source link

Tests for functions with multidimensional pointers as params can be incorrect #608

Closed IDKWNTCMF closed 1 year ago

IDKWNTCMF commented 1 year ago

Description When UTBot generates tests for function with multidimensional pointers as parameters, these parameters can be initialized incorretly, if a lazy pointer init is needed for some field.

Example

struct ElementStruct {
    struct ElementStruct *prev;
    struct ElementStruct *next;
};

struct ListStruct {
    struct ElementStruct *head;
    struct ElementStruct *tail;
    unsigned size;
};

struct MainStruct {
    struct ListStruct list;
};

int func_with_multi_dim_pointer(struct MainStruct **str) {
    if (!str) {
        return 0;
    }
    struct MainStruct *ptr = *str;
    int sz = 0;
    if (ptr) {
        struct ElementStruct *e = ptr->list.head;
        struct ElementStruct *n;
        if (e) {
            n = e->next;
            sz++;
        }
    }
    return sz;
}

To Reproduce Steps to reproduce the behavior:

  1. Copy example above to your project
  2. Generate tests for function func_with_multi_dim_pointer
  3. Try to run generated tests

Expected behavior Tests are supposed to be executed successfully.

Actual behavior An error test is generated with information about errors.

Generated test

TEST(regression, func_with_multi_dim_pointer_test1)
{
    struct MainStruct _str[2][2] = {{{
        .list = {
            .head = NULL,
            .tail = NULL,
            .size = 0U
        }
    }, {
        .list = {
            .head = NULL,
            .tail = NULL,
            .size = 0U
        }
    }}, {{
        .list = {
            .head = NULL,
            .tail = NULL,
            .size = 0U
        }
    }, {
        .list = {
            .head = NULL,
            .tail = NULL,
            .size = 0U
        }
    }}};
    struct MainStruct ** str = (struct MainStruct **) calloc(3, sizeof(struct MainStruct *));
    for (int it_9_0 = 0; it_9_0 < 2; it_9_0 ++) {
        str[it_9_0] = _str[it_9_0];
    }
    str[2] = NULL;
    struct ElementStruct utbotInnerVar1 = {
        .prev = NULL,
        .next = NULL
    };

    str.list.head = (struct ElementStruct*) &utbotInnerVar1;

    int actual = func_with_multi_dim_pointer(str);
    EXPECT_EQ(1, actual);
    struct MainStruct expected_str[2][2] = {{{
        .list = {
            .head = NULL,
            .tail = NULL,
            .size = 0U
        }
    }, {
        .list = {
            .head = NULL,
            .tail = NULL,
            .size = 0U
        }
    }}, {{
        .list = {
            .head = NULL,
            .tail = NULL,
            .size = 0U
        }
    }, {
        .list = {
            .head = NULL,
            .tail = NULL,
            .size = 0U
        }
    }}};
    for (int it_10_0 = 0; it_10_0 < 2; it_10_0 ++) {
        for (int it_10_1 = 0; it_10_1 < 2; it_10_1 ++) {
            EXPECT_EQ(expected_str[it_10_0][it_10_1].list.size, _str[it_10_0][it_10_1].list.size);
        }
    }
}

Logs

/home/utbot/UTBotCpp/integration-tests/c-example/utbot_tests/makefiles/lib/structures/structs/../../../../../utbot_tests/lib/structures/structs/complex_structs_dot_c_test.cpp:50:8: error: member reference base type 'struct MainStruct **' is not a structure or union
    str.list.head = (struct ElementStruct*) &utbotInnerVar1;
    ~~~^~~~~
1 error generated.
IDKWNTCMF commented 1 year ago

If function parameter is const, then during lazy initialization, we will try to change initialized const variable.

Example

struct ElementStruct {
    struct ElementStruct *prev;
    struct ElementStruct *next;
};

struct ListStruct {
    struct ElementStruct *head;
    struct ElementStruct *tail;
    unsigned size;
};

struct MainStruct {
    struct ListStruct list;
};

int func_with_multi_dim_pointer(const struct MainStruct **str) {
    if (!str) {
        return 0;
    }
    struct MainStruct *ptr = *str;
    int sz = 0;
    if (ptr) {
        struct ElementStruct *e = ptr->list.head;
        struct ElementStruct *n;
        if (e) {
            n = e->next;
            sz++;
        }
    }
    return sz;
}

Generated test

TEST(regression, func_with_multi_dim_pointer_test2)
{
    // Construct input
    struct MainStruct _str[2][2] = {{{
        .list = {
            .head = NULL,
            .tail = NULL,
            .size = 0U
        }
    }, {
        .list = {
            .head = NULL,
            .tail = NULL,
            .size = 0U
        }
    }}, {{
        .list = {
            .head = NULL,
            .tail = NULL,
            .size = 0U
        }
    }, {
        .list = {
            .head = NULL,
            .tail = NULL,
            .size = 0U
        }
    }}};
    const struct MainStruct ** str = (const struct MainStruct **) calloc(3, sizeof(struct MainStruct *));
    for (int it_4_0 = 0; it_4_0 < 2; it_4_0 ++) {
        str[it_4_0] = _str[it_4_0];
    }
    str[2] = NULL;

    // Construct lazy instantiated variables
    struct ElementStruct utbotInnerVar1 = {
        .prev = NULL,
        .next = NULL
    };

    // Assign lazy variables to pointer
    str[1][0].list.head = (struct ElementStruct*) &utbotInnerVar1;

    // Expected output
    int expected = 5;

    // Trigger the function
    int actual = func_with_multi_dim_pointer(str);

    // Check results
    EXPECT_EQ(expected, actual);
}

Logs

/home/utbot/UTBotCpp/integration-tests/c-example/utbot_tests/makefiles/lib/pointers/../../../../utbot_tests/lib/pointers/pointer_parameters_dot_c_test.cpp:98:25: error: read-only variable is not assignable
    str[1][0].list.head = (struct ElementStruct*) &utbotInnerVar1;