3 #include "../application/failure.h" 4 #include "../conversion/stringbuilder.h" 5 #include "../conversion/stringconversion.h" 6 #include "../io/catchiofailure.h" 11 #include <initializer_list> 32 TestApplication *TestApplication::m_instance =
nullptr;
44 TestApplication::TestApplication(
int argc,
char **argv)
46 , m_testFilesPathArg(
"test-files-path",
'p',
"specifies the path of the directory with test files")
47 , m_applicationPathArg(
"app-path",
'a',
"specifies the path of the application to be tested")
48 , m_workingDirArg(
"working-dir",
'w',
"specifies the directory to store working copies of test files")
49 , m_unitsArg(
"units",
'u',
"specifies the units to test; omit to test all units")
53 throw runtime_error(
"only one TestApplication instance allowed at a time");
58 if (
const char *testFilesPathEnv = getenv(
"TEST_FILE_PATH")) {
59 if (
const auto len = strlen(testFilesPathEnv)) {
60 m_testFilesPathEnvValue.reserve(len + 1);
61 m_testFilesPathEnvValue += testFilesPathEnv;
62 m_testFilesPathEnvValue +=
'/';
67 for (
Argument *arg : initializer_list<Argument *>{ &m_testFilesPathArg, &m_applicationPathArg, &m_workingDirArg }) {
68 arg->setRequiredValueCount(1);
69 arg->setValueNames({
"path" });
70 arg->setCombinable(
true);
75 m_parser.
setMainArguments({ &m_testFilesPathArg, &m_applicationPathArg, &m_workingDirArg, &m_unitsArg, &m_helpArg });
88 cerr <<
"Directories used to search for testfiles:" << endl;
90 if (*m_testFilesPathArg.
values().front()) {
91 cerr << ((m_testFilesPathArgValue = m_testFilesPathArg.
values().front()) +=
'/') << endl;
93 cerr << (m_testFilesPathArgValue =
"./") << endl;
96 if (!m_testFilesPathEnvValue.empty()) {
97 cerr << m_testFilesPathEnvValue << endl;
99 cerr <<
"./testfiles/" << endl << endl;
100 cerr <<
"Directory used to store working copies:" << endl;
102 if (*m_workingDirArg.
values().front()) {
103 (m_workingDir = m_workingDirArg.
values().front()) +=
'/';
107 }
else if (
const char *workingDirEnv = getenv(
"WORKING_DIR")) {
108 if (
const auto len = strlen(workingDirEnv)) {
109 m_workingDir.reserve(len + 1);
110 m_workingDir += workingDirEnv;
115 m_workingDir = m_testFilesPathArgValue +
"workingdir/";
116 }
else if (!m_testFilesPathEnvValue.empty()) {
117 m_workingDir = m_testFilesPathEnvValue +
"workingdir/";
119 m_workingDir =
"./testfiles/workingdir/";
122 cerr << m_workingDir << endl << endl;
125 if (
const char *profrawListFile = getenv(
"LLVM_PROFILE_LIST_FILE")) {
126 ofstream(profrawListFile, ios_base::trunc);
130 cerr <<
"Executing test cases ..." << endl;
131 }
catch (
const Failure &failure) {
132 cerr <<
"Invalid arguments specified: " << failure.
what() << endl;
142 m_instance =
nullptr;
155 file.open(path = m_testFilesPathArgValue + name, ios_base::in);
162 if (!m_testFilesPathEnvValue.empty()) {
164 file.open(path = m_testFilesPathEnvValue + name, ios_base::in);
172 file.open(path =
"./testfiles/" + name, ios_base::in);
174 cerr <<
"Warning: The testfile \"" << path <<
"\" can not be located." << endl;
184 string TestApplication::workingCopyPathMode(
const string &name,
WorkingCopyMode mode)
const 187 fstream origFile, workingCopy;
188 origFile.exceptions(ios_base::badbit | ios_base::failbit);
189 workingCopy.exceptions(ios_base::badbit | ios_base::failbit);
192 struct stat currentStat;
193 if (stat(m_workingDir.c_str(), ¤tStat) || !S_ISDIR(currentStat.st_mode)) {
194 if (mkdir(m_workingDir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)) {
195 cerr <<
"Unable to create working copy for \"" << name <<
"\": can't create working directory." << endl;
201 const auto parts = splitString<vector<string>>(name, string(
"/"), EmptyPartsTreat::Omit);
202 if (!parts.empty()) {
203 string currentLevel = m_workingDir;
204 for (
auto i = parts.cbegin(), end = parts.end() - 1; i != end; ++i) {
205 if (currentLevel.back() !=
'/') {
209 if (stat(currentLevel.c_str(), ¤tStat) || !S_ISDIR(currentStat.st_mode)) {
210 if (mkdir(currentLevel.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)) {
211 cerr <<
"Unable to create working copy for \"" << name <<
"\": can't create working directory." << endl;
221 origFile.open(
testFilePath(name), ios_base::in | ios_base::binary);
222 const string path = m_workingDir + name;
223 workingCopy.open(path, ios_base::out | ios_base::binary | ios_base::trunc);
224 workingCopy << origFile.rdbuf();
228 cerr <<
"Unable to create working copy for \"" << name <<
"\": an IO error occured." << endl;
231 return m_workingDir + name;
236 string TestApplication::workingCopyPath(
const string &name)
const 245 int execAppInternal(
const char *appPath,
const char *
const *args, std::string &output, std::string &errors,
bool suppressLogging,
int timeout,
246 const std::string &newProfilingPath)
249 if (!suppressLogging) {
251 for (
const char *
const *i = args; *i; ++i) {
258 int coutPipes[2], cerrPipes[2];
259 pipe(coutPipes), pipe(cerrPipes);
260 int readCoutPipe = coutPipes[0], writeCoutPipe = coutPipes[1];
261 int readCerrPipe = cerrPipes[0], writeCerrPipe = cerrPipes[1];
264 if (
int child = fork()) {
266 close(writeCoutPipe), close(writeCerrPipe);
270 throw runtime_error(
"Unable to create fork");
274 struct pollfd fileDescriptorSet[2];
275 fileDescriptorSet[0].fd = readCoutPipe;
276 fileDescriptorSet[1].fd = readCerrPipe;
277 fileDescriptorSet[0].events = fileDescriptorSet[1].events = POLLIN;
282 output.clear(), errors.clear();
286 int retpoll = poll(fileDescriptorSet, 2, timeout);
289 if (fileDescriptorSet[0].revents & POLLIN) {
290 if ((count = read(readCoutPipe, buffer,
sizeof(buffer))) > 0) {
291 output.append(buffer, count);
293 }
else if (fileDescriptorSet[0].revents & POLLHUP) {
295 fileDescriptorSet[0].fd = -1;
297 if (fileDescriptorSet[1].revents & POLLIN) {
298 if ((count = read(readCerrPipe, buffer,
sizeof(buffer))) > 0) {
299 errors.append(buffer, count);
301 }
else if (fileDescriptorSet[1].revents & POLLHUP) {
303 fileDescriptorSet[1].fd = -1;
305 }
else if (retpoll == 0) {
307 throw runtime_error(
"Poll time-out");
310 throw runtime_error(
"Poll failed");
312 }
while (fileDescriptorSet[0].fd >= 0 || fileDescriptorSet[1].fd >= 0);
315 close(readCoutPipe), close(readCerrPipe);
321 waitpid(child, &childReturnCode, 0);
322 return childReturnCode;
326 dup2(writeCoutPipe, STDOUT_FILENO), dup2(writeCerrPipe, STDERR_FILENO);
327 close(readCoutPipe), close(writeCoutPipe), close(readCerrPipe), close(writeCerrPipe);
330 if (!newProfilingPath.empty()) {
331 setenv(
"LLVM_PROFILE_FILE", newProfilingPath.data(),
true);
335 execv(appPath, const_cast<char *const *>(args));
336 cerr <<
"Unable to execute \"" << appPath <<
"\": execv() failed" << endl;
350 int TestApplication::execApp(
const char *
const *args,
string &output,
string &errors,
bool suppressLogging,
int timeout)
const 353 static unsigned int invocationCount = 0;
357 const char *
const appPath = m_applicationPathArg.
firstValue();
358 if (!appPath || !*appPath) {
359 throw runtime_error(
"Unable to execute application to be tested: no application path specified");
363 string newProfilingPath;
364 if (
const char *llvmProfileFile = getenv(
"LLVM_PROFILE_FILE")) {
366 if (
const char *llvmProfileFileEnd = strstr(llvmProfileFile,
".profraw")) {
367 const string llvmProfileFileWithoutExtension(llvmProfileFile, llvmProfileFileEnd);
369 const char *appName = strrchr(appPath,
'/');
370 appName = appName ? appName + 1 : appPath;
372 newProfilingPath =
argsToString(llvmProfileFileWithoutExtension,
'_', appName, invocationCount,
".profraw");
374 if (
const char *profrawListFile = getenv(
"LLVM_PROFILE_LIST_FILE")) {
375 ofstream(profrawListFile, ios_base::app) << newProfilingPath << endl;
380 return execAppInternal(appPath, args, output, errors, suppressLogging, timeout, newProfilingPath);
390 int execHelperApp(
const char *appPath,
const char *
const *args, std::string &output, std::string &errors,
bool suppressLogging,
int timeout)
392 return execAppInternal(appPath, args, output, errors, suppressLogging, timeout,
string());
void setCombinable(bool value)
Sets whether this argument can be combined.
virtual const char * what() const USE_NOTHROW
Returns a C-style character string describing the cause of the Failure.
Contains currently only ArgumentParser and related classes.
constexpr StringType argsToString(Args &&... args)
void setMainArguments(const ArgumentInitializerList &mainArguments)
Sets the main arguments for the parser.
std::string testFilePath(const std::string &name) const
Returns the full path of the test file with the specified name.
void parseArgs(int argc, const char *const *argv)
Parses the specified command line arguments.
const char * firstValue() const
Returns the first parameter value of the first occurrence of the argument.
Contains utility classes helping to read and write streams.
Contains classes and functions utilizing creating of test applications.
~TestApplication()
Destroys the TestApplication.
const std::vector< const char * > & values(std::size_t occurrence=0) const
Returns the parameter values for the specified occurrence of argument.
Contains several functions providing conversions between different data types.
The Argument class is a wrapper for command line argument information.
WorkingCopyMode
The WorkingCopyMode enum specifies additional options to influence behavior of TestApplication::worki...
bool isPresent() const
Returns an indication whether the argument could be detected when parsing.
The Failure class is thrown by an ArgumentParser when a parsing error occurs.
CPP_UTILITIES_EXPORT const char * catchIoFailure()
Provides a workaround for GCC Bug 66145.
void setRequiredValueCount(std::size_t requiredValueCount)
Sets the number of values which are required to be given for this argument.
void setValueNames(std::initializer_list< const char *> valueNames)
Sets the names of the requried values.