From df8144c9930debf63a1effc40ca7154e5a67a004 Mon Sep 17 00:00:00 2001 From: IDKWNTCMF Date: Fri, 28 Jul 2023 11:33:01 +0300 Subject: [PATCH 1/5] Support assignment of lazy pointers in case of a multidimensional pointer as a parameter of a function --- server/src/Tests.cpp | 50 +++++++++++++++++++++++++++++++++----------- server/src/Tests.h | 2 ++ 2 files changed, 40 insertions(+), 12 deletions(-) diff --git a/server/src/Tests.cpp b/server/src/Tests.cpp index 2e692e9eb..36de1c059 100644 --- a/server/src/Tests.cpp +++ b/server/src/Tests.cpp @@ -593,8 +593,9 @@ void KTestObjectParser::addToOrder(const std::vector &objects, if (it != objects.end()) { size_t jsonInd = it - objects.begin(); visited[jsonInd] = true; - Tests::MethodParam param = {paramType.isObjectPointer() ? paramType.baseTypeObj() - : paramType, + Tests::MethodParam param = { paramType.isObjectPointer() && !paramType.isPointerToPointer() + ? paramType.baseTypeObj() + : paramType, paramName, std::nullopt }; order.emplace(jsonInd, param, paramValue); return; @@ -653,8 +654,9 @@ 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)); + types::Type fieldType = traverseLazyInStruct(typeAndName.type, offsetInStruct).type; if (!pointToStruct(fieldType, testCase.objects[indObj])) { continue; } @@ -698,6 +700,26 @@ Tests::TypeAndVarName KTestObjectParser::traverseLazyInStruct(const types::Type } } +size_t KTestObjectParser::getOffsetInStruct(Tests::TypeAndVarName &objTypeAndName, size_t offsetInBits) const { + if (!objTypeAndName.type.isPointerToPointer()) { + return offsetInBits; + } + std::vector sizes = objTypeAndName.type.arraysSizes(types::PointerUsage::PARAMETER); + size_t dimension = sizes.size(); + objTypeAndName.type = objTypeAndName.type.baseTypeObj(); + size_t sizeInBits = typesHandler.typeSize(objTypeAndName.type); + size_t offset = offsetInBits / sizeInBits; + std::string indices; + while (dimension != 0) { + size_t index = offset % sizes[--dimension]; + offset /= sizes[dimension]; + indices = StringUtils::stringFormat("[%d]%s", index, indices); + } + objTypeAndName.varName += indices; + offsetInBits %= sizeInBits; + return offsetInBits; +} + void KTestObjectParser::assignTypeStubVar(Tests::MethodTestCase &testCase, const Tests::MethodDescription &methodDescription) { for (auto const &obj : testCase.objects) { @@ -724,21 +746,25 @@ void KTestObjectParser::assignAllLazyPointers( 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)); + 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)); + 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( diff --git a/server/src/Tests.h b/server/src/Tests.h index 981e65d67..b414c596b 100644 --- a/server/src/Tests.h +++ b/server/src/Tests.h @@ -843,6 +843,8 @@ namespace tests { size_t offsetInBits, const std::string &curVarName = "") const; + size_t getOffsetInStruct(Tests::TypeAndVarName &objTypeAndName, size_t offsetInBits) const; + std::shared_ptr getLazyPointerView(const std::vector &objects, std::vector &initReferences, From 3447d195c5520bafc29994b77cc1ecf885256590 Mon Sep 17 00:00:00 2001 From: IDKWNTCMF Date: Fri, 28 Jul 2023 12:17:00 +0300 Subject: [PATCH 2/5] Add tests for this case --- server/test/framework/Server_Tests.cpp | 38 +++++++++++++++++++ server/test/suites/server/CMakeLists.txt | 1 + .../test/suites/server/multi_dim_pointers.c | 23 +++++++++++ .../test/suites/server/multi_dim_pointers.h | 21 ++++++++++ 4 files changed, 83 insertions(+) create mode 100644 server/test/suites/server/multi_dim_pointers.c create mode 100644 server/test/suites/server/multi_dim_pointers.h 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 From 3969dee698cc7f3005934ba15004a6290797509f Mon Sep 17 00:00:00 2001 From: IDKWNTCMF Date: Fri, 28 Jul 2023 23:39:52 +0300 Subject: [PATCH 3/5] Some fixes --- server/src/Tests.cpp | 42 ++++++++++++++++++++++++++---------------- server/src/Tests.h | 11 ++++++++--- 2 files changed, 34 insertions(+), 19 deletions(-) diff --git a/server/src/Tests.cpp b/server/src/Tests.cpp index 36de1c059..2e291a039 100644 --- a/server/src/Tests.cpp +++ b/server/src/Tests.cpp @@ -587,12 +587,14 @@ 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; + usages[jsonInd] = types::PointerUsage::PARAMETER; Tests::MethodParam param = { paramType.isObjectPointer() && !paramType.isPointerToPointer() ? paramType.baseTypeObj() : paramType, @@ -615,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(); @@ -638,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( @@ -655,7 +659,8 @@ void KTestObjectParser::assignTypeUnnamedVar( continue; } Tests::TypeAndVarName typeAndName = { paramType, "" }; - size_t offsetInStruct = getOffsetInStruct(typeAndName, SizeUtils::bytesToBits(offset)); + 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; @@ -700,11 +705,13 @@ Tests::TypeAndVarName KTestObjectParser::traverseLazyInStruct(const types::Type } } -size_t KTestObjectParser::getOffsetInStruct(Tests::TypeAndVarName &objTypeAndName, size_t offsetInBits) const { - if (!objTypeAndName.type.isPointerToPointer()) { +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(types::PointerUsage::PARAMETER); + std::vector sizes = objTypeAndName.type.arraysSizes(usage); size_t dimension = sizes.size(); objTypeAndName.type = objTypeAndName.type.baseTypeObj(); size_t sizeInBits = typesHandler.typeSize(objTypeAndName.type); @@ -739,7 +746,8 @@ 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()) { @@ -747,8 +755,9 @@ void KTestObjectParser::assignAllLazyPointers( } for (const auto &pointer : object.pointers) { Tests::TypeAndVarName typeAndName = objTypeAndName[ind].value(); - size_t offset = - getOffsetInStruct(typeAndName, SizeUtils::bytesToBits(pointer.offset)); + 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()) { @@ -757,8 +766,9 @@ void KTestObjectParser::assignAllLazyPointers( std::string toPtrName; Tests::TypeAndVarName pointerTypeAndName = objTypeAndName[pointer.index].value(); - size_t indexOffset = - getOffsetInStruct(pointerTypeAndName, SizeUtils::bytesToBits(pointer.indexOffset)); + size_t indexOffset = getOffsetInStruct(pointerTypeAndName, + SizeUtils::bytesToBits(pointer.indexOffset), + usages[pointer.index]); if (indexOffset == 0 && pointToStruct(fromPtr.type, testCase.objects[pointer.index])) { toPtrName = pointerTypeAndName.varName; @@ -852,11 +862,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 b414c596b..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,7 +846,9 @@ namespace tests { size_t offsetInBits, const std::string &curVarName = "") const; - size_t getOffsetInStruct(Tests::TypeAndVarName &objTypeAndName, size_t offsetInBits) const; + size_t getOffsetInStruct(Tests::TypeAndVarName &objTypeAndName, + size_t offsetInBits, + types::PointerUsage usage) const; std::shared_ptr getLazyPointerView(const std::vector &objects, From 062a1b2a28cf24e0eab27d00186f6fbed4a145a5 Mon Sep 17 00:00:00 2001 From: IDKWNTCMF Date: Mon, 31 Jul 2023 11:12:58 +0300 Subject: [PATCH 4/5] Add usage initialization for pointers of objects --- server/src/Tests.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/server/src/Tests.cpp b/server/src/Tests.cpp index 2e291a039..507ad9ac9 100644 --- a/server/src/Tests.cpp +++ b/server/src/Tests.cpp @@ -669,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; } } } @@ -716,6 +717,10 @@ size_t KTestObjectParser::getOffsetInStruct(Tests::TypeAndVarName &objTypeAndNam objTypeAndName.type = objTypeAndName.type.baseTypeObj(); size_t sizeInBits = typesHandler.typeSize(objTypeAndName.type); size_t offset = offsetInBits / sizeInBits; + offsetInBits %= sizeInBits; + if (objTypeAndName.varName.empty()) { + return offsetInBits; + } std::string indices; while (dimension != 0) { size_t index = offset % sizes[--dimension]; @@ -723,7 +728,6 @@ size_t KTestObjectParser::getOffsetInStruct(Tests::TypeAndVarName &objTypeAndNam indices = StringUtils::stringFormat("[%d]%s", index, indices); } objTypeAndName.varName += indices; - offsetInBits %= sizeInBits; return offsetInBits; } From ff4c3066f34ae8d6cca7c5533172b3c85ad9aa84 Mon Sep 17 00:00:00 2001 From: IDKWNTCMF Date: Wed, 9 Aug 2023 15:31:30 +0300 Subject: [PATCH 5/5] Code refactoring --- server/src/Tests.cpp | 12 +----------- server/src/utils/PrinterUtils.cpp | 14 ++++++++++++++ server/src/utils/PrinterUtils.h | 2 ++ 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/server/src/Tests.cpp b/server/src/Tests.cpp index 507ad9ac9..8103bb92b 100644 --- a/server/src/Tests.cpp +++ b/server/src/Tests.cpp @@ -713,21 +713,11 @@ size_t KTestObjectParser::getOffsetInStruct(Tests::TypeAndVarName &objTypeAndNam return offsetInBits; } std::vector sizes = objTypeAndName.type.arraysSizes(usage); - size_t dimension = sizes.size(); 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; - if (objTypeAndName.varName.empty()) { - return offsetInBits; - } - std::string indices; - while (dimension != 0) { - size_t index = offset % sizes[--dimension]; - offset /= sizes[dimension]; - indices = StringUtils::stringFormat("[%d]%s", index, indices); - } - objTypeAndName.varName += indices; return offsetInBits; } 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);