Skip to content

Commit 05a4baa

Browse files
authored
#318 Remove code duplicate and fix the procedure for writing statistic (#319)
#318 Remove code duplicate and fix the procedure for writing statistic
1 parent 0904755 commit 05a4baa

File tree

4 files changed

+144
-179
lines changed

4 files changed

+144
-179
lines changed

server/src/KleeRunner.cpp

Lines changed: 94 additions & 143 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
#include "KleeRunner.h"
22

33
#include "Paths.h"
4+
#include "TimeExecStatistics.h"
45
#include "exceptions/FileNotPresentedInArtifactException.h"
56
#include "exceptions/FileNotPresentedInCommandsException.h"
67
#include "tasks/RunKleeTask.h"
78
#include "utils/ExecUtils.h"
89
#include "utils/FileSystemUtils.h"
910
#include "utils/KleeUtils.h"
1011
#include "utils/LogUtils.h"
11-
#include "TimeExecStatistics.h"
1212

1313
#include "loguru.h"
1414

15+
#include <fstream>
1516
#include <utility>
1617

1718
using namespace tests;
@@ -92,13 +93,6 @@ void KleeRunner::runKlee(const std::vector<tests::TestMethod> &testMethods,
9293
std::move(writeFunctor));
9394
}
9495

95-
fs::path KleeRunner::getKleeMethodOutFile(const TestMethod &method) {
96-
fs::path kleeOutDir = Paths::getKleeOutDir(projectTmpPath);
97-
fs::path relative =
98-
Paths::removeExtension(fs::relative(method.sourceFilePath, projectContext.projectPath));
99-
return kleeOutDir / relative / ("klee_out_" + method.methodName);
100-
}
101-
10296
namespace {
10397
void clearUnusedData(const fs::path &kleeDir) {
10498
fs::remove(kleeDir / "assembly.ll");
@@ -119,56 +113,10 @@ namespace {
119113
}
120114
}
121115

122-
void KleeRunner::processBatchWithoutInteractive(MethodKtests &ktestChunk,
123-
const TestMethod &testMethod,
124-
Tests &tests) {
125-
if (!tests.isFilePresentedInArtifact) {
126-
return;
127-
}
128-
if (testMethod.sourceFilePath != tests.sourceFilePath) {
129-
std::string message = StringUtils::stringFormat(
130-
"While generating tests for source file: %s tried to generate tests for method %s "
131-
"from another source file: %s. This can cause invalid generation.\n",
132-
tests.sourceFilePath, testMethod.methodName, testMethod.sourceFilePath);
133-
LOG_S(WARNING) << message;
134-
}
135-
136-
std::string entryPoint = KleeUtils::entryPointFunction(tests, testMethod.methodName, true);
137-
std::string entryPointFlag = StringUtils::stringFormat("--entry-point=%s", entryPoint);
138-
auto kleeOut = getKleeMethodOutFile(testMethod);
139-
fs::create_directories(kleeOut.parent_path());
140-
std::string outputDir = "--output-dir=" + kleeOut.string();
141-
std::vector<std::string> argvData = { "klee",
142-
entryPointFlag,
143-
"--libc=klee",
144-
"--posix-runtime",
145-
"--fp-runtime",
146-
"--only-output-states-covering-new",
147-
"--allocate-determ",
148-
"--external-calls=all",
149-
"--timer-interval=1000ms",
150-
"--bcov-check-interval=6s",
151-
"-istats-write-interval=5s",
152-
"--disable-verify",
153-
"--check-div-zero=false",
154-
"--check-overshift=false",
155-
"--skip-not-lazy-and-symbolic-pointers",
156-
outputDir };
157-
if (settingsContext.useDeterministicSearcher) {
158-
argvData.emplace_back("--search=dfs");
159-
}
160-
argvData.push_back(testMethod.bitcodeFilePath);
161-
argvData.emplace_back("--sym-stdin");
162-
argvData.emplace_back(std::to_string(types::Type::symStdinSize));
163-
std::vector<char *> cargv, cenvp;
164-
std::vector<std::string> tmp;
165-
ExecUtils::toCArgumentsPtr(argvData, tmp, cargv, cenvp, false);
166-
LOG_S(DEBUG) << "Klee command :: " + StringUtils::joinWith(argvData, " ");
167-
MEASURE_FUNCTION_EXECUTION_TIME
168-
RunKleeTask task(cargv.size(), cargv.data(), settingsContext.timeoutPerFunction);
169-
ExecUtils::ExecutionResult result __attribute__((unused)) = task.run();
170-
ExecUtils::throwIfCancelled();
171-
116+
static void processMethod(MethodKtests &ktestChunk,
117+
tests::Tests &tests,
118+
const fs::path &kleeOut,
119+
const tests::TestMethod &method) {
172120
if (fs::exists(kleeOut)) {
173121
clearUnusedData(kleeOut);
174122
bool hasTimeout = false;
@@ -198,32 +146,90 @@ void KleeRunner::processBatchWithoutInteractive(MethodKtests &ktestChunk,
198146
return UTBotKTestObject{ kTestObject };
199147
});
200148

201-
ktestChunk[testMethod].emplace_back(objects, status);
149+
ktestChunk[method].emplace_back(objects, status);
202150
}
203151
}
204152
}
205153
if (hasTimeout) {
206154
std::string message = StringUtils::stringFormat(
207155
"Some tests for function '%s' were skipped, as execution of function is "
208156
"out of timeout.",
209-
testMethod.methodName);
157+
method.methodName);
210158
tests.commentBlocks.emplace_back(std::move(message));
211159
}
212160
if (hasError) {
213161
std::string message = StringUtils::stringFormat(
214162
"Some tests for function '%s' were skipped, as execution of function leads "
215163
"KLEE to the internal error. See console log for more details.",
216-
testMethod.methodName);
164+
method.methodName);
217165
tests.commentBlocks.emplace_back(std::move(message));
218166
}
167+
219168
writeKleeStats(kleeOut);
169+
170+
if (!CollectionUtils::containsKey(ktestChunk, method) || ktestChunk.at(method).empty()) {
171+
tests.commentBlocks.emplace_back(StringUtils::stringFormat(
172+
"Tests for %s were not generated. Maybe the function is too complex.",
173+
method.methodName));
174+
}
220175
}
176+
}
221177

222-
if (!CollectionUtils::containsKey(ktestChunk, testMethod) ||
223-
ktestChunk.at(testMethod).empty()) {
224-
tests.commentBlocks.emplace_back(StringUtils::stringFormat(
225-
"Tests for %s were not generated. Maybe the function is too complex.",
226-
testMethod.methodName));
178+
void KleeRunner::processBatchWithoutInteractive(MethodKtests &ktestChunk,
179+
const TestMethod &testMethod,
180+
Tests &tests) {
181+
if (!tests.isFilePresentedInArtifact) {
182+
return;
183+
}
184+
if (testMethod.sourceFilePath != tests.sourceFilePath) {
185+
std::string message = StringUtils::stringFormat(
186+
"While generating tests for source file: %s tried to generate tests for method %s "
187+
"from another source file: %s. This can cause invalid generation.\n",
188+
tests.sourceFilePath, testMethod.methodName, testMethod.sourceFilePath);
189+
LOG_S(WARNING) << message;
190+
}
191+
192+
std::string entryPoint = KleeUtils::entryPointFunction(tests, testMethod.methodName, true);
193+
std::string entryPointFlag = StringUtils::stringFormat("--entry-point=%s", entryPoint);
194+
auto kleeOut = Paths::kleeOutDirForEntrypoints(projectContext, projectTmpPath, testMethod.sourceFilePath,
195+
testMethod.methodName);
196+
fs::create_directories(kleeOut.parent_path());
197+
std::string outputDir = "--output-dir=" + kleeOut.string();
198+
std::vector<std::string> argvData = { "klee",
199+
entryPointFlag,
200+
"--libc=klee",
201+
"--posix-runtime",
202+
"--fp-runtime",
203+
"--only-output-states-covering-new",
204+
"--allocate-determ",
205+
"--external-calls=all",
206+
"--timer-interval=1000ms",
207+
"--bcov-check-interval=6s",
208+
"-istats-write-interval=5s",
209+
"--disable-verify",
210+
"--check-div-zero=false",
211+
"--check-overshift=false",
212+
"--skip-not-lazy-and-symbolic-pointers",
213+
outputDir };
214+
if (settingsContext.useDeterministicSearcher) {
215+
argvData.emplace_back("--search=dfs");
216+
}
217+
argvData.push_back(testMethod.bitcodeFilePath);
218+
argvData.emplace_back("--sym-stdin");
219+
argvData.emplace_back(std::to_string(types::Type::symStdinSize));
220+
221+
{
222+
std::vector<char *> cargv, cenvp;
223+
std::vector<std::string> tmp;
224+
ExecUtils::toCArgumentsPtr(argvData, tmp, cargv, cenvp, false);
225+
LOG_S(DEBUG) << "Klee command :: " + StringUtils::joinWith(argvData, " ");
226+
MEASURE_FUNCTION_EXECUTION_TIME
227+
228+
RunKleeTask task(cargv.size(), cargv.data(), settingsContext.timeoutPerFunction);
229+
ExecUtils::ExecutionResult result __attribute__((unused)) = task.run();
230+
ExecUtils::throwIfCancelled();
231+
232+
processMethod(ktestChunk, tests, kleeOut, testMethod);
227233
}
228234
}
229235

@@ -238,7 +244,7 @@ void KleeRunner::processBatchWithInteractive(const std::vector<tests::TestMethod
238244
if (method.sourceFilePath != tests.sourceFilePath) {
239245
std::string message = StringUtils::stringFormat(
240246
"While generating tests for source file: %s tried to generate tests for method %s "
241-
"from another source file: %s. This can cause invalid generation.\n",
247+
"from another source file: %s. This can cause invalid generation.\n",
242248
tests.sourceFilePath, method.methodName, method.sourceFilePath);
243249
LOG_S(WARNING) << message;
244250
}
@@ -247,7 +253,7 @@ void KleeRunner::processBatchWithInteractive(const std::vector<tests::TestMethod
247253
TestMethod testMethod = testMethods[0];
248254
std::string entryPoint = KleeUtils::entryPointFunction(tests, testMethod.methodName, true);
249255
std::string entryPointFlag = StringUtils::stringFormat("--entry-point=%s", entryPoint);
250-
auto kleeOut = getKleeMethodOutFile(testMethod);
256+
auto kleeOut = Paths::kleeOutDirForEntrypoints(projectContext, projectTmpPath, tests.sourceFilePath);
251257
fs::create_directories(kleeOut.parent_path());
252258

253259
fs::path entrypoints = kleeOut.parent_path() / "entrypoints.txt";
@@ -287,86 +293,31 @@ void KleeRunner::processBatchWithInteractive(const std::vector<tests::TestMethod
287293
argvData.push_back(testMethod.bitcodeFilePath);
288294
argvData.emplace_back("--sym-stdin");
289295
argvData.emplace_back(std::to_string(types::Type::symStdinSize));
290-
std::vector<char *> cargv, cenvp;
291-
std::vector<std::string> tmp;
292-
ExecUtils::toCArgumentsPtr(argvData, tmp, cargv, cenvp, false);
293-
294-
LOG_S(DEBUG) << "Klee command :: " + StringUtils::joinWith(argvData, " ");
295-
MEASURE_FUNCTION_EXECUTION_TIME
296-
if (settingsContext.timeoutPerFunction.has_value()) {
297-
RunKleeTask task(cargv.size(), cargv.data(), settingsContext.timeoutPerFunction.value() * testMethods.size());
298-
ExecUtils::ExecutionResult result __attribute__((unused)) = task.run();
299-
} else {
300-
RunKleeTask task(cargv.size(), cargv.data(), settingsContext.timeoutPerFunction);
301-
ExecUtils::ExecutionResult result __attribute__((unused)) = task.run();
302-
}
303-
304-
ExecUtils::throwIfCancelled();
305296

306-
for (const auto &method : testMethods) {
307-
std::string kleeMethodName = KleeUtils::entryPointFunction(tests, method.methodName, true);
308-
fs::path newKleeOut = kleeOut / kleeMethodName;
309-
MethodKtests ktestChunk;
310-
if (fs::exists(newKleeOut)) {
311-
clearUnusedData(newKleeOut);
312-
bool hasTimeout = false;
313-
bool hasError = false;
314-
for (auto const &entry : fs::directory_iterator(newKleeOut)) {
315-
auto const &path = entry.path();
316-
if (Paths::isKtestJson(path)) {
317-
if (Paths::hasEarly(path)) {
318-
hasTimeout = true;
319-
} else if (Paths::hasInternalError(path)) {
320-
hasError = true;
321-
} else {
322-
std::unique_ptr<TestCase, decltype(&TestCase_free)> ktestData{
323-
TC_fromFile(path.c_str()), TestCase_free
324-
};
325-
if (ktestData == nullptr) {
326-
LOG_S(WARNING) << "Unable to open .ktestjson file";
327-
continue;
328-
}
329-
UTBotKTest::Status status = Paths::hasError(path)
330-
? UTBotKTest::Status::FAILED
331-
: UTBotKTest::Status::SUCCESS;
332-
std::vector<ConcretizedObject> kTestObjects(
333-
ktestData->objects, ktestData->objects + ktestData->n_objects);
297+
{
298+
std::vector<char *> cargv, cenvp;
299+
std::vector<std::string> tmp;
300+
ExecUtils::toCArgumentsPtr(argvData, tmp, cargv, cenvp, false);
334301

335-
std::vector<UTBotKTestObject> objects = CollectionUtils::transform(
336-
kTestObjects, [](const ConcretizedObject &kTestObject) {
337-
return UTBotKTestObject{ kTestObject };
338-
});
302+
LOG_S(DEBUG) << "Klee command :: " + StringUtils::joinWith(argvData, " ");
303+
MEASURE_FUNCTION_EXECUTION_TIME
339304

340-
ktestChunk[method].emplace_back(objects, status);
341-
}
342-
}
343-
}
344-
if (hasTimeout) {
345-
std::string message = StringUtils::stringFormat(
346-
"Some tests for function '%s' were skipped, as execution of function is "
347-
"out of timeout.",
348-
method.methodName);
349-
tests.commentBlocks.emplace_back(std::move(message));
350-
}
351-
if (hasError) {
352-
std::string message = StringUtils::stringFormat(
353-
"Some tests for function '%s' were skipped, as execution of function leads "
354-
"KLEE to the internal error. See console log for more details.",
355-
method.methodName);
356-
tests.commentBlocks.emplace_back(std::move(message));
357-
}
358-
}
305+
RunKleeTask task(cargv.size(),
306+
cargv.data(),
307+
settingsContext.timeoutPerFunction.has_value()
308+
? settingsContext.timeoutPerFunction.value() * testMethods.size()
309+
: settingsContext.timeoutPerFunction);
310+
ExecUtils::ExecutionResult result __attribute__((unused)) = task.run();
359311

360-
if (fs::exists(kleeOut)) {
361-
writeKleeStats(kleeOut);
362-
}
312+
ExecUtils::throwIfCancelled();
363313

364-
if (!CollectionUtils::containsKey(ktestChunk, method) ||
365-
ktestChunk.at(method).empty()) {
366-
tests.commentBlocks.emplace_back(StringUtils::stringFormat(
367-
"Tests for %s were not generated. Maybe the function is too complex.",
368-
method.methodName));
314+
for (const auto &method : testMethods) {
315+
std::string kleeMethodName =
316+
KleeUtils::entryPointFunction(tests, method.methodName, true);
317+
fs::path newKleeOut = kleeOut / kleeMethodName;
318+
MethodKtests ktestChunk;
319+
processMethod(ktestChunk, tests, newKleeOut, method);
320+
ktests.push_back(ktestChunk);
369321
}
370-
ktests.push_back(ktestChunk);
371322
}
372323
}

server/src/KleeRunner.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@ class KleeRunner {
4949
void processBatchWithInteractive(const std::vector<tests::TestMethod> &testMethods,
5050
tests::Tests &tests,
5151
std::vector<tests::MethodKtests> &ktests);
52-
fs::path getKleeMethodOutFile(const tests::TestMethod &method);
5352
};
5453

5554

server/src/Paths.cpp

Lines changed: 11 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,17 @@ namespace Paths {
153153
[&path](auto const &suffix) { return errorFileExists(path, suffix); });
154154
}
155155

156+
fs::path kleeOutDirForEntrypoints(const utbot::ProjectContext &projectContext, const fs::path &projectTmpPath,
157+
const fs::path &srcFilePath, const std::string &methodName) {
158+
fs::path kleeOutDir = getKleeOutDir(projectTmpPath);
159+
fs::path relative = (fs::relative(addOrigExtensionAsSuffixAndAddNew(srcFilePath, ""),
160+
projectContext.projectPath));
161+
if (!methodName.empty()) {
162+
return kleeOutDir / relative / ("klee_out_" + methodName);
163+
}
164+
return kleeOutDir / relative / ("klee_out_" + srcFilePath.filename().stem().string());
165+
}
166+
156167
//endregion
157168

158169
//region extensions
@@ -281,48 +292,13 @@ namespace Paths {
281292
//endregion
282293

283294
//region transformation
284-
const std::string MAKEFILE_EXTENSION = ".mk";
285-
const std::string TEST_SUFFIX = "_test";
286-
const std::string STUB_SUFFIX = "_stub";
287-
const std::string DOT_SEP = "_dot_";
288-
const std::string MAKE_WRAPPER_SUFFIX = "_wrapper";
289-
const char dot = '.';
290295

291296
fs::path sourcePathToTestPath(const utbot::ProjectContext &projectContext,
292297
const fs::path &sourceFilePath) {
293298
return projectContext.testDirPath / getRelativeDirPath(projectContext, sourceFilePath) /
294299
sourcePathToTestName(sourceFilePath);
295300
}
296301

297-
static inline fs::path addOrigExtensionAsSuffixAndAddNew(const fs::path &path,
298-
const std::string &newExt) {
299-
std::string extensionAsSuffix = path.extension().string();
300-
if (!extensionAsSuffix.empty()) {
301-
std::string fnWithNewExt =
302-
path.stem().string() + DOT_SEP + extensionAsSuffix.substr(1) + newExt;
303-
return path.parent_path() / fnWithNewExt;
304-
}
305-
return replaceExtension(path, newExt);
306-
}
307-
308-
static inline fs::path restoreExtensionFromSuffix(const fs::path &path,
309-
const std::string &defaultExt) {
310-
std::string fnWithoutExt = path.stem();
311-
fs::path fnWithExt;
312-
std::size_t posEncodedExtension = fnWithoutExt.rfind(DOT_SEP);
313-
if (posEncodedExtension == std::string::npos) {
314-
// In `sample_class_test.cpp` the `class` is not an extension
315-
fnWithExt = fnWithoutExt + defaultExt;
316-
}
317-
else {
318-
// In `sample_class_dot_cpp.cpp` the `cpp` is an extension
319-
fnWithExt = fnWithoutExt.substr(0, posEncodedExtension)
320-
+ dot
321-
+ fnWithoutExt.substr(posEncodedExtension + DOT_SEP.length());
322-
}
323-
return path.parent_path() / fs::path(fnWithExt);
324-
}
325-
326302
fs::path sourcePathToTestName(const fs::path &source) {
327303
return addSuffix(addOrigExtensionAsSuffixAndAddNew(source, ".cpp"),
328304
TEST_SUFFIX).filename();

0 commit comments

Comments
 (0)