Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 5 commits into from
Aug 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -40,6 +40,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 @@ -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<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