diff --git a/server/src/Tests.cpp b/server/src/Tests.cpp index 2e692e9eb..8103bb92b 100644 --- a/server/src/Tests.cpp +++ b/server/src/Tests.cpp @@ -587,14 +587,17 @@ void KTestObjectParser::addToOrder(const std::vector &objects, const types::Type ¶mType, Tests::TestCaseParamValue ¶mValue, std::vector &visited, + std::vector &usages, std::queue &order) { auto it = std::find_if(objects.begin(), objects.end(), [paramName](const UTBotKTestObject &obj) { return obj.name == paramName; }); if (it != objects.end()) { size_t jsonInd = it - objects.begin(); visited[jsonInd] = true; - Tests::MethodParam param = {paramType.isObjectPointer() ? paramType.baseTypeObj() - : paramType, + usages[jsonInd] = types::PointerUsage::PARAMETER; + Tests::MethodParam param = { paramType.isObjectPointer() && !paramType.isPointerToPointer() + ? paramType.baseTypeObj() + : paramType, paramName, std::nullopt }; order.emplace(jsonInd, param, paramValue); return; @@ -614,16 +617,17 @@ bool KTestObjectParser::pointToStruct(const types::Type &pointerType, void KTestObjectParser::assignTypeUnnamedVar( Tests::MethodTestCase &testCase, const Tests::MethodDescription &methodDescription, - std::vector> &objects) { + std::vector> &objects, + std::vector &usages) { std::queue order; std::vector visited(testCase.objects.size(), false); for (size_t paramInd = 0; paramInd < testCase.paramValues.size(); paramInd++) { addToOrder(testCase.objects, methodDescription.params[paramInd].name, methodDescription.params[paramInd].type, testCase.paramValues[paramInd], visited, - order); + usages, order); } addToOrder(testCase.objects, KleeUtils::RESULT_VARIABLE_NAME, methodDescription.returnType, - testCase.returnValue, visited, order); + testCase.returnValue, visited, usages, order); while (!order.empty()) { auto curType = order.front(); @@ -637,6 +641,7 @@ void KTestObjectParser::assignTypeUnnamedVar( throw UnImplementedException("Lazy variable has baseType=void"); } + usages[curType.jsonInd] = types::PointerUsage::LAZY; std::vector byteValue = testCase.objects[curType.jsonInd].bytes; Tests::TypeAndVarName typeAndVarName{ paramType, name }; std::shared_ptr testParamView = testParameterView( @@ -653,8 +658,10 @@ void KTestObjectParser::assignTypeUnnamedVar( if (indexOffset != 0) { continue; } - types::Type fieldType = - traverseLazyInStruct(paramType, SizeUtils::bytesToBits(offset)).type; + Tests::TypeAndVarName typeAndName = { paramType, "" }; + size_t offsetInStruct = + getOffsetInStruct(typeAndName, SizeUtils::bytesToBits(offset), usages[indObj]); + types::Type fieldType = traverseLazyInStruct(typeAndName.type, offsetInStruct).type; if (!pointToStruct(fieldType, testCase.objects[indObj])) { continue; } @@ -662,6 +669,7 @@ void KTestObjectParser::assignTypeUnnamedVar( Tests::MethodParam param = {fieldType.baseTypeObj(1), "", std::nullopt}; order.emplace(indObj, param, curType.paramValue); visited[indObj] = true; + usages[indObj] = types::PointerUsage::PARAMETER; } } } @@ -698,6 +706,21 @@ Tests::TypeAndVarName KTestObjectParser::traverseLazyInStruct(const types::Type } } +size_t KTestObjectParser::getOffsetInStruct(Tests::TypeAndVarName &objTypeAndName, + size_t offsetInBits, + types::PointerUsage usage) const { + if (!objTypeAndName.type.isPointerToPointer() || usage != types::PointerUsage::PARAMETER) { + return offsetInBits; + } + std::vector sizes = objTypeAndName.type.arraysSizes(usage); + objTypeAndName.type = objTypeAndName.type.baseTypeObj(); + size_t sizeInBits = typesHandler.typeSize(objTypeAndName.type); + size_t offset = offsetInBits / sizeInBits; + PrinterUtils::appendIndicesToVarName(objTypeAndName.varName, sizes, offset); + offsetInBits %= sizeInBits; + return offsetInBits; +} + void KTestObjectParser::assignTypeStubVar(Tests::MethodTestCase &testCase, const Tests::MethodDescription &methodDescription) { for (auto const &obj : testCase.objects) { @@ -717,28 +740,35 @@ void KTestObjectParser::assignTypeStubVar(Tests::MethodTestCase &testCase, void KTestObjectParser::assignAllLazyPointers( Tests::MethodTestCase &testCase, - const std::vector> &objTypeAndName) const { + const std::vector> &objTypeAndName, + const std::vector &usages) const { for (size_t ind = 0; ind < testCase.objects.size(); ind++) { const auto &object = testCase.objects[ind]; if (!objTypeAndName[ind].has_value()) { continue; } for (const auto &pointer : object.pointers) { - Tests::TypeAndVarName fromPtr = traverseLazyInStruct( - objTypeAndName[ind]->type, SizeUtils::bytesToBits(pointer.offset), - objTypeAndName[ind]->varName); + Tests::TypeAndVarName typeAndName = objTypeAndName[ind].value(); + size_t offset = getOffsetInStruct(typeAndName, + SizeUtils::bytesToBits(pointer.offset), + usages[ind]); + Tests::TypeAndVarName fromPtr = + traverseLazyInStruct(typeAndName.type, offset, typeAndName.varName); if (!objTypeAndName[pointer.index].has_value()) { continue; } std::string toPtrName; - if (pointer.indexOffset == 0 && + Tests::TypeAndVarName pointerTypeAndName = objTypeAndName[pointer.index].value(); + size_t indexOffset = getOffsetInStruct(pointerTypeAndName, + SizeUtils::bytesToBits(pointer.indexOffset), + usages[pointer.index]); + if (indexOffset == 0 && pointToStruct(fromPtr.type, testCase.objects[pointer.index])) { - toPtrName = objTypeAndName[pointer.index]->varName; + toPtrName = pointerTypeAndName.varName; } else { - toPtrName = traverseLazyInStruct(objTypeAndName[pointer.index]->type, - SizeUtils::bytesToBits(pointer.indexOffset), - objTypeAndName[pointer.index]->varName).varName; + toPtrName = traverseLazyInStruct(pointerTypeAndName.type, indexOffset, + pointerTypeAndName.varName).varName; } testCase.lazyReferences.emplace_back( @@ -826,11 +856,11 @@ void KTestObjectParser::parseTestCases(const UTBotKTestList &cases, traceStream << "\treturn: " << testCase.returnValue.view->getEntryValue(nullptr); LOG_S(MAX) << traceStream.str(); - std::vector> objectsValues( - testCase.objects.size()); - assignTypeUnnamedVar(testCase, methodDescription, objectsValues); + std::vector> objectsValues(testCase.objects.size()); + std::vector usages(testCase.objects.size()); + assignTypeUnnamedVar(testCase, methodDescription, objectsValues, usages); assignTypeStubVar(testCase, methodDescription); - assignAllLazyPointers(testCase, objectsValues); + assignAllLazyPointers(testCase, objectsValues, usages); methodDescription.testCases.push_back(testCase); methodDescription.suiteTestCases[testCase.suiteName].push_back(testCase.testIndex); diff --git a/server/src/Tests.h b/server/src/Tests.h index 981e65d67..c3abb4a9a 100644 --- a/server/src/Tests.h +++ b/server/src/Tests.h @@ -824,18 +824,21 @@ namespace tests { const types::Type ¶mType, Tests::TestCaseParamValue ¶mValue, std::vector &visited, + std::vector &usages, std::queue &order); void assignTypeUnnamedVar(Tests::MethodTestCase &testCase, const Tests::MethodDescription &methodDescription, - std::vector> &objects); + std::vector> &objects, + std::vector &usages); void assignTypeStubVar(Tests::MethodTestCase &testCase, const Tests::MethodDescription &methodDescription); void assignAllLazyPointers( Tests::MethodTestCase &testCase, - const std::vector> &objTypeAndName) const; + const std::vector> &objTypeAndName, + const std::vector &usages) const; size_t findFieldIndex(const types::StructInfo &structInfo, size_t offsetInBits) const; @@ -843,6 +846,10 @@ namespace tests { size_t offsetInBits, const std::string &curVarName = "") const; + size_t getOffsetInStruct(Tests::TypeAndVarName &objTypeAndName, + size_t offsetInBits, + types::PointerUsage usage) const; + std::shared_ptr getLazyPointerView(const std::vector &objects, std::vector &initReferences, diff --git a/server/src/utils/PrinterUtils.cpp b/server/src/utils/PrinterUtils.cpp index 45692f0b8..5fcd15bdf 100644 --- a/server/src/utils/PrinterUtils.cpp +++ b/server/src/utils/PrinterUtils.cpp @@ -40,6 +40,20 @@ namespace PrinterUtils { return StringUtils::stringFormat(access, varName); } + void appendIndicesToVarName(std::string &varName, const std::vector &sizes, size_t offset) { + if (varName.empty()) { + return; + } + size_t dimension = sizes.size(); + std::string indices; + while (dimension != 0) { + size_t index = offset % sizes[--dimension]; + offset /= sizes[dimension]; + indices = StringUtils::stringFormat("[%d]%s", index, indices); + } + varName += indices; + } + std::string initializePointer(const std::string &type, const std::string &value, size_t additionalPointersCount) { diff --git a/server/src/utils/PrinterUtils.h b/server/src/utils/PrinterUtils.h index b61e1e4a1..9c557011a 100644 --- a/server/src/utils/PrinterUtils.h +++ b/server/src/utils/PrinterUtils.h @@ -72,6 +72,8 @@ namespace PrinterUtils { std::string fillVarName(std::string const &temp, std::string const &varName); + void appendIndicesToVarName(std::string &varName, const std::vector &sizes, size_t offset); + std::string getKleePrefix(bool forKlee); std::string wrapUserValue(const testsgen::ValidationType &type, const std::string &value); diff --git a/server/test/framework/Server_Tests.cpp b/server/test/framework/Server_Tests.cpp index 8107d8159..30339bcd5 100644 --- a/server/test/framework/Server_Tests.cpp +++ b/server/test/framework/Server_Tests.cpp @@ -2141,6 +2141,44 @@ namespace { testUtils::checkStatuses(resultsMap, tests); } + TEST_F(Server_Test, Run_Tests_For_Multi_Dim_Pointers) { + fs::path multi_dim_pointers_c = getTestFilePath("multi_dim_pointers.c"); + auto request = testUtils::createFileRequest(projectName, suitePath, buildDirRelativePath, + srcPaths, multi_dim_pointers_c, + GrpcUtils::UTBOT_AUTO_TARGET_PATH, true, false); + auto testGen = FileTestGen(*request, writer.get(), TESTMODE); + Status status = Server::TestsGenServiceImpl::ProcessBaseTestRequest(testGen, writer.get()); + ASSERT_TRUE(status.ok()) << status.error_message(); + EXPECT_GE(testUtils::getNumberOfTests(testGen.tests), 2); + + fs::path testsDirPath = getTestFilePath("tests"); + + fs::path multi_dim_pointers_test_cpp = Paths::sourcePathToTestPath( + utbot::ProjectContext(projectName, suitePath, testsDirPath, buildDirRelativePath, clientProjectPath), + multi_dim_pointers_c); + auto testFilter = GrpcUtils::createTestFilterForFile(multi_dim_pointers_test_cpp); + auto runRequest = testUtils::createCoverageAndResultsRequest( + projectName, suitePath, testsDirPath, buildDirRelativePath, std::move(testFilter)); + + static auto coverageAndResultsWriter = + std::make_unique(nullptr); + CoverageAndResultsGenerator coverageGenerator{ runRequest.get(), + coverageAndResultsWriter.get() }; + utbot::SettingsContext settingsContext{ + true, false, 45, 0, false, false, ErrorMode::FAILING, false + }; + coverageGenerator.generate(false, settingsContext); + + EXPECT_FALSE(coverageGenerator.hasExceptions()); + ASSERT_TRUE(coverageGenerator.getCoverageMap().empty()); + + auto resultsMap = coverageGenerator.getTestResultMap(); + auto tests = coverageGenerator.getTestsToLaunch(); + + StatusCountMap expectedStatusCountMap{ { testsgen::TEST_PASSED, 2 } }; + testUtils::checkStatuses(resultsMap, tests); + } + TEST_F(Server_Test, Run_Tests_For_Struct_With_Union) { fs::path struct_with_union_c = getTestFilePath("struct_with_union.c"); auto request = testUtils::createFileRequest(projectName, suitePath, buildDirRelativePath, diff --git a/server/test/suites/server/CMakeLists.txt b/server/test/suites/server/CMakeLists.txt index 0c0502edf..f67852e6e 100644 --- a/server/test/suites/server/CMakeLists.txt +++ b/server/test/suites/server/CMakeLists.txt @@ -20,6 +20,7 @@ add_executable(server keywords.c linkage.c main.c + multi_dim_pointers.c pointer_parameters.c pointer_return.c simple_structs.c diff --git a/server/test/suites/server/multi_dim_pointers.c b/server/test/suites/server/multi_dim_pointers.c new file mode 100644 index 000000000..15146509f --- /dev/null +++ b/server/test/suites/server/multi_dim_pointers.c @@ -0,0 +1,23 @@ +#include "multi_dim_pointers.h" + +int func_with_multi_dim_pointer(struct MainStruct **str) { + if (!str) { + return 0; + } + str++; + struct MainStruct *ptr = *str; + int sz = 0; + if (ptr) { + struct ElementStruct *e = ptr->list.head; + struct ElementStruct *n; + for (int i = 0; i < 5; i++) { + if (e) { + n = e->next; + sz++; + } else { + break; + } + } + } + return sz; +} diff --git a/server/test/suites/server/multi_dim_pointers.h b/server/test/suites/server/multi_dim_pointers.h new file mode 100644 index 000000000..7dce8b47d --- /dev/null +++ b/server/test/suites/server/multi_dim_pointers.h @@ -0,0 +1,21 @@ +#ifndef UNITTESTBOT_MULTI_DIM_POINTERS_H +#define UNITTESTBOT_MULTI_DIM_POINTERS_H + +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); + +#endif // UNITTESTBOT_MULTI_DIM_POINTERS_H