Skip to content

Commit

Permalink
Support assignment of lazy pointers in case of a multidimensional poi…
Browse files Browse the repository at this point in the history
…nter as a parameter of a function (#609)

* Support assignment of lazy pointers in case of a multidimensional pointer as a parameter of a function

* Add tests for this case

* Add usage initialization for pointers of objects
  • Loading branch information
IDKWNTCMF authored Aug 10, 2023
1 parent 38de4ee commit 38d4cfb
Show file tree
Hide file tree
Showing 8 changed files with 158 additions and 22 deletions.
70 changes: 50 additions & 20 deletions server/src/Tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -587,14 +587,17 @@ void KTestObjectParser::addToOrder(const std::vector<UTBotKTestObject> &objects,
const types::Type &paramType,
Tests::TestCaseParamValue &paramValue,
std::vector<bool> &visited,
std::vector<PointerUsage> &usages,
std::queue<JsonIndAndParam> &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;
Expand All @@ -614,16 +617,17 @@ bool KTestObjectParser::pointToStruct(const types::Type &pointerType,
void KTestObjectParser::assignTypeUnnamedVar(
Tests::MethodTestCase &testCase,
const Tests::MethodDescription &methodDescription,
std::vector<std::optional<Tests::TypeAndVarName>> &objects) {
std::vector<std::optional<Tests::TypeAndVarName>> &objects,
std::vector<PointerUsage> &usages) {
std::queue<JsonIndAndParam> order;
std::vector<bool> 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();
Expand All @@ -637,6 +641,7 @@ void KTestObjectParser::assignTypeUnnamedVar(
throw UnImplementedException("Lazy variable has baseType=void");
}

usages[curType.jsonInd] = types::PointerUsage::LAZY;
std::vector<char> byteValue = testCase.objects[curType.jsonInd].bytes;
Tests::TypeAndVarName typeAndVarName{ paramType, name };
std::shared_ptr<AbstractValueView> testParamView = testParameterView(
Expand All @@ -653,15 +658,18 @@ 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;
}
if (!visited[indObj]) {
Tests::MethodParam param = {fieldType.baseTypeObj(1), "", std::nullopt};
order.emplace(indObj, param, curType.paramValue);
visited[indObj] = true;
usages[indObj] = types::PointerUsage::PARAMETER;
}
}
}
Expand Down Expand Up @@ -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<size_t> 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) {
Expand All @@ -717,28 +740,35 @@ void KTestObjectParser::assignTypeStubVar(Tests::MethodTestCase &testCase,

void KTestObjectParser::assignAllLazyPointers(
Tests::MethodTestCase &testCase,
const std::vector<std::optional<Tests::TypeAndVarName>> &objTypeAndName) const {
const std::vector<std::optional<Tests::TypeAndVarName>> &objTypeAndName,
const std::vector<PointerUsage> &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(
Expand Down Expand Up @@ -826,11 +856,11 @@ void KTestObjectParser::parseTestCases(const UTBotKTestList &cases,
traceStream << "\treturn: " << testCase.returnValue.view->getEntryValue(nullptr);
LOG_S(MAX) << traceStream.str();

std::vector<std::optional<Tests::TypeAndVarName>> objectsValues(
testCase.objects.size());
assignTypeUnnamedVar(testCase, methodDescription, objectsValues);
std::vector<std::optional<Tests::TypeAndVarName>> objectsValues(testCase.objects.size());
std::vector<PointerUsage> 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);
Expand Down
11 changes: 9 additions & 2 deletions server/src/Tests.h
Original file line number Diff line number Diff line change
Expand Up @@ -824,25 +824,32 @@ namespace tests {
const types::Type &paramType,
Tests::TestCaseParamValue &paramValue,
std::vector<bool> &visited,
std::vector<types::PointerUsage> &usages,
std::queue<JsonIndAndParam> &order);

void assignTypeUnnamedVar(Tests::MethodTestCase &testCase,
const Tests::MethodDescription &methodDescription,
std::vector<std::optional<Tests::TypeAndVarName>> &objects);
std::vector<std::optional<Tests::TypeAndVarName>> &objects,
std::vector<types::PointerUsage> &usages);

void assignTypeStubVar(Tests::MethodTestCase &testCase,
const Tests::MethodDescription &methodDescription);

void assignAllLazyPointers(
Tests::MethodTestCase &testCase,
const std::vector<std::optional<Tests::TypeAndVarName>> &objTypeAndName) const;
const std::vector<std::optional<Tests::TypeAndVarName>> &objTypeAndName,
const std::vector<types::PointerUsage> &usages) const;

size_t findFieldIndex(const types::StructInfo &structInfo, size_t offsetInBits) const;

Tests::TypeAndVarName traverseLazyInStruct(const types::Type &curVarType,
size_t offsetInBits,
const std::string &curVarName = "") const;

size_t getOffsetInStruct(Tests::TypeAndVarName &objTypeAndName,
size_t offsetInBits,
types::PointerUsage usage) const;

std::shared_ptr<AbstractValueView>
getLazyPointerView(const std::vector<UTBotKTestObject> &objects,
std::vector<InitReference> &initReferences,
Expand Down
14 changes: 14 additions & 0 deletions server/src/utils/PrinterUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,20 @@ namespace PrinterUtils {
return StringUtils::stringFormat(access, varName);
}

void appendIndicesToVarName(std::string &varName, const std::vector<size_t> &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) {
Expand Down
2 changes: 2 additions & 0 deletions server/src/utils/PrinterUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ namespace PrinterUtils {

std::string fillVarName(std::string const &temp, std::string const &varName);

void appendIndicesToVarName(std::string &varName, const std::vector<size_t> &sizes, size_t offset);

std::string getKleePrefix(bool forKlee);

std::string wrapUserValue(const testsgen::ValidationType &type, const std::string &value);
Expand Down
38 changes: 38 additions & 0 deletions server/test/framework/Server_Tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<ServerCoverageAndResultsWriter>(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,
Expand Down
1 change: 1 addition & 0 deletions server/test/suites/server/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
23 changes: 23 additions & 0 deletions server/test/suites/server/multi_dim_pointers.c
Original file line number Diff line number Diff line change
@@ -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;
}
21 changes: 21 additions & 0 deletions server/test/suites/server/multi_dim_pointers.h
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 38d4cfb

Please sign in to comment.