3 #include "../application/failure.h" 4 #include "../conversion/stringconversion.h" 5 #include "../io/catchiofailure.h" 11 #include <initializer_list> 16 # include <sys/wait.h> 17 # include <sys/stat.h> 30 TestApplication *TestApplication::m_instance =
nullptr;
42 TestApplication::TestApplication(
int argc,
char **argv) :
44 m_testFilesPathArg(
"test-files-path",
'p',
"specifies the path of the directory with test files"),
45 m_applicationPathArg(
"app-path",
'a',
"specifies the path of the application to be tested"),
46 m_workingDirArg(
"working-dir",
'w',
"specifies the directory to store working copies of test files"),
47 m_unitsArg(
"units",
'u',
"specifies the units to test; omit to test all units")
51 throw runtime_error(
"only one TestApplication instance allowed at a time");
56 if(
const char *testFilesPathEnv = getenv(
"TEST_FILE_PATH")) {
57 if(
const auto len = strlen(testFilesPathEnv)) {
58 m_testFilesPathEnvValue.reserve(len + 1);
59 m_testFilesPathEnvValue += testFilesPathEnv;
60 m_testFilesPathEnvValue +=
'/';
65 for(
Argument *arg : initializer_list<Argument *>{&m_testFilesPathArg, &m_applicationPathArg, &m_workingDirArg}) {
66 arg->setRequiredValueCount(1);
67 arg->setValueNames({
"path"});
68 arg->setCombinable(
true);
73 m_parser.
setMainArguments({&m_testFilesPathArg, &m_applicationPathArg, &m_workingDirArg, &m_unitsArg, &m_helpArg});
82 cerr <<
"Directories used to search for testfiles:" << endl;
84 if(*m_testFilesPathArg.
values().front()) {
85 cerr << ((m_testFilesPathArgValue = m_testFilesPathArg.
values().front()) +=
'/') << endl;
87 cerr << (m_testFilesPathArgValue =
"./") << endl;
90 if(!m_testFilesPathEnvValue.empty()) {
91 cerr << m_testFilesPathEnvValue << endl;
93 cerr <<
"./testfiles/" << endl << endl;
94 cerr <<
"Directory used to store working copies:" << endl;
96 if(*m_workingDirArg.
values().front()) {
97 (m_workingDir = m_workingDirArg.
values().front()) +=
'/';
101 }
else if(
const char *workingDirEnv = getenv(
"WORKING_DIR")) {
102 if(
const auto len = strlen(workingDirEnv)) {
103 m_workingDir.reserve(len + 1);
104 m_workingDir += workingDirEnv;
109 m_workingDir = m_testFilesPathArgValue +
"workingdir/";
110 }
else if(!m_testFilesPathEnvValue.empty()) {
111 m_workingDir = m_testFilesPathEnvValue +
"workingdir/";
113 m_workingDir =
"./testfiles/workingdir/";
116 cerr << m_workingDir << endl << endl;
119 cerr <<
"Executing test cases ..." << endl;
120 }
catch(
const Failure &failure) {
121 cerr <<
"Invalid arguments specified: " << failure.
what() << endl;
131 m_instance =
nullptr;
144 file.open(path = m_testFilesPathArgValue + name, ios_base::in);
151 if(!m_testFilesPathEnvValue.empty()) {
153 file.open(path = m_testFilesPathEnvValue + name, ios_base::in);
160 path =
"./testfiles/" + name;
162 file.open(path = m_testFilesPathEnvValue + name, ios_base::in);
164 cerr <<
"Warning: The testfile \"" << path <<
"\" can not be located." << endl;
174 string TestApplication::workingCopyPathMode(
const string &name,
WorkingCopyMode mode)
const 177 fstream origFile, workingCopy;
178 origFile.exceptions(ios_base::badbit | ios_base::failbit);
179 workingCopy.exceptions(ios_base::badbit | ios_base::failbit);
182 struct stat currentStat;
183 if(stat(m_workingDir.c_str(), ¤tStat) || !S_ISDIR(currentStat.st_mode)) {
184 if(mkdir(m_workingDir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)) {
185 cerr <<
"Unable to create working copy for \"" << name <<
"\": can't create working directory." << endl;
191 const auto parts = splitString<vector<string> >(name, string(
"/"), EmptyPartsTreat::Omit);
193 string currentLevel = m_workingDir;
194 for(
auto i = parts.cbegin(), end = parts.end() - 1; i != end; ++i) {
195 if(currentLevel.back() !=
'/') {
199 if(stat(currentLevel.c_str(), ¤tStat) || !S_ISDIR(currentStat.st_mode)) {
200 if(mkdir(currentLevel.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)) {
201 cerr <<
"Unable to create working copy for \"" << name <<
"\": can't create working directory." << endl;
211 origFile.open(
testFilePath(name), ios_base::in | ios_base::binary);
212 const string path = m_workingDir + name;
213 workingCopy.open(path, ios_base::out | ios_base::binary | ios_base::trunc);
214 workingCopy << origFile.rdbuf();
218 cerr <<
"Unable to create working copy for \"" << name <<
"\": an IO error occured." << endl;
221 return m_workingDir + name;
226 string TestApplication::workingCopyPath(
const string &name)
const 240 int TestApplication::execApp(
const char *
const *args,
string &stdout,
string &stderr,
bool suppressLogging,
int timeout)
const 243 if(!suppressLogging) {
245 for(
const char *
const *i = args; *i; ++i) {
251 const char *appPath = m_applicationPathArg.
firstValue();
252 if(!appPath || !*appPath) {
253 throw runtime_error(
"Unable to execute application to be tested: no application path specified");
256 int coutPipes[2], cerrPipes[2];
257 pipe(coutPipes), pipe(cerrPipes);
258 int readCoutPipe = coutPipes[0], writeCoutPipe = coutPipes[1];
259 int readCerrPipe = cerrPipes[0], writeCerrPipe = cerrPipes[1];
261 if(
int child = fork()) {
263 close(writeCoutPipe), close(writeCerrPipe);
267 throw runtime_error(
"Unable to create fork");
271 struct pollfd fileDescriptorSet[2];
272 fileDescriptorSet[0].fd = readCoutPipe;
273 fileDescriptorSet[1].fd = readCerrPipe;
274 fileDescriptorSet[0].events = fileDescriptorSet[1].events = POLLIN;
279 stdout.clear(), stderr.clear();
283 int retpoll = poll(fileDescriptorSet, 2, timeout);
286 if(fileDescriptorSet[0].revents & POLLIN) {
287 if((count = read(readCoutPipe, buffer,
sizeof(buffer))) > 0) {
288 stdout.append(buffer, count);
290 }
else if(fileDescriptorSet[0].revents & POLLHUP) {
292 fileDescriptorSet[0].fd = -1;
294 if(fileDescriptorSet[1].revents & POLLIN) {
295 if((count = read(readCerrPipe, buffer,
sizeof(buffer))) > 0) {
296 stderr.append(buffer, count);
298 }
else if(fileDescriptorSet[1].revents & POLLHUP) {
300 fileDescriptorSet[1].fd = -1;
302 }
else if(retpoll == 0) {
304 throw runtime_error(
"Poll time-out");
307 throw runtime_error(
"Poll failed");
309 }
while(fileDescriptorSet[0].fd >= 0 || fileDescriptorSet[1].fd >= 0);
312 close(readCoutPipe), close(readCerrPipe);
318 waitpid(child, &childReturnCode, 0);
319 return childReturnCode;
322 dup2(writeCoutPipe, STDOUT_FILENO), dup2(writeCerrPipe, STDERR_FILENO);
323 close(readCoutPipe), close(writeCoutPipe), close(readCerrPipe), close(writeCerrPipe);
324 execv(appPath, const_cast<char *const *>(args));
325 cerr <<
"Unable to execute \"" << appPath <<
"\": execv() failed" << endl;
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.
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.