2016-02-06 02:52:06 +01:00
|
|
|
#include "./testutils.h"
|
|
|
|
|
|
|
|
#include "../application/failure.h"
|
2016-02-17 20:21:11 +01:00
|
|
|
#include "../conversion/stringconversion.h"
|
2016-02-06 02:52:06 +01:00
|
|
|
|
|
|
|
#include <cstdlib>
|
|
|
|
#include <cstring>
|
|
|
|
#include <iostream>
|
|
|
|
#include <fstream>
|
|
|
|
|
2016-02-09 02:21:42 +01:00
|
|
|
#include <sys/stat.h>
|
|
|
|
|
2016-02-06 02:52:06 +01:00
|
|
|
using namespace std;
|
|
|
|
using namespace ApplicationUtilities;
|
2016-02-17 20:21:11 +01:00
|
|
|
using namespace ConversionUtilities;
|
2016-02-06 02:52:06 +01:00
|
|
|
|
|
|
|
namespace TestUtilities {
|
|
|
|
|
|
|
|
TestApplication *TestApplication::m_instance = nullptr;
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \class TestApplication
|
2016-02-09 02:21:42 +01:00
|
|
|
* \brief The TestApplication class simplifies writing test applications that require opening test files.
|
2016-02-06 02:52:06 +01:00
|
|
|
* \remarks Only one instance is allowed at a time (singletone class).
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Constructs a TestApplication instance.
|
|
|
|
* \throws Throws std::runtime_error if an instance has already been created.
|
|
|
|
*/
|
|
|
|
TestApplication::TestApplication(int argc, char **argv) :
|
|
|
|
m_helpArg(m_parser),
|
2016-02-09 02:21:42 +01:00
|
|
|
m_testFilesPathArg("test-files-path", "p", "specifies the path of the directory with test files"),
|
|
|
|
m_workingDirArg("working-dir", "w", "specifies the directory to store working copies of test files")
|
2016-02-06 02:52:06 +01:00
|
|
|
{
|
2016-02-09 02:21:42 +01:00
|
|
|
// check whether there is already an instance
|
2016-02-06 02:52:06 +01:00
|
|
|
if(m_instance) {
|
|
|
|
throw runtime_error("only one TestApplication instance allowed at a time");
|
|
|
|
}
|
|
|
|
m_instance = this;
|
2016-02-09 02:21:42 +01:00
|
|
|
|
|
|
|
// read TEST_FILE_PATH environment variable
|
2016-02-06 02:52:06 +01:00
|
|
|
if(const char *testFilesPathEnv = getenv("TEST_FILE_PATH")) {
|
|
|
|
if(const auto len = strlen(testFilesPathEnv)) {
|
|
|
|
m_testFilesPathEnvValue.reserve(len + 1);
|
|
|
|
m_testFilesPathEnvValue += testFilesPathEnv;
|
|
|
|
m_testFilesPathEnvValue += '/';
|
|
|
|
}
|
|
|
|
}
|
2016-02-09 02:21:42 +01:00
|
|
|
|
|
|
|
// setup argument parser
|
2016-02-06 02:52:06 +01:00
|
|
|
m_testFilesPathArg.setRequiredValueCount(1);
|
|
|
|
m_testFilesPathArg.setValueNames({"path"});
|
|
|
|
m_testFilesPathArg.setCombinable(true);
|
2016-02-09 02:21:42 +01:00
|
|
|
m_workingDirArg.setRequiredValueCount(1);
|
|
|
|
m_workingDirArg.setValueNames({"path"});
|
|
|
|
m_workingDirArg.setCombinable(true);
|
|
|
|
m_parser.setMainArguments({&m_testFilesPathArg, &m_workingDirArg, &m_helpArg});
|
|
|
|
|
|
|
|
// parse arguments
|
2016-02-06 02:52:06 +01:00
|
|
|
try {
|
|
|
|
m_parser.parseArgs(argc, argv);
|
2016-02-09 02:21:42 +01:00
|
|
|
cerr << "Directories used to search for testfiles:" << endl;
|
2016-02-06 02:52:06 +01:00
|
|
|
if(m_testFilesPathArg.isPresent()) {
|
|
|
|
if(!m_testFilesPathArg.values().front().empty()) {
|
|
|
|
cerr << (m_testFilesPathArgValue = m_testFilesPathArg.values().front() + '/') << endl;
|
|
|
|
} else {
|
|
|
|
cerr << (m_testFilesPathArgValue = "./") << endl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(!m_testFilesPathEnvValue.empty()) {
|
|
|
|
cerr << m_testFilesPathEnvValue << endl;
|
|
|
|
}
|
|
|
|
cerr << "./testfiles/" << endl << endl;
|
2016-02-09 02:21:42 +01:00
|
|
|
cerr << "Directory used to store working copies:" << endl;
|
|
|
|
if(m_workingDirArg.isPresent()) {
|
|
|
|
if(!m_workingDirArg.values().front().empty()) {
|
|
|
|
m_workingDir = m_workingDirArg.values().front() + '/';
|
|
|
|
} else {
|
|
|
|
m_workingDir = "./";
|
|
|
|
}
|
|
|
|
} else if(const char *workingDirEnv = getenv("WORKING_DIR")) {
|
|
|
|
if(const auto len = strlen(workingDirEnv)) {
|
|
|
|
m_workingDir.reserve(len + 1);
|
|
|
|
m_workingDir += workingDirEnv;
|
|
|
|
m_workingDir += '/';
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if(m_testFilesPathArg.isPresent()) {
|
|
|
|
m_workingDir = m_testFilesPathArgValue + "workingdir/";
|
|
|
|
} else if(!m_testFilesPathEnvValue.empty()) {
|
|
|
|
m_workingDir = m_testFilesPathEnvValue + "workingdir/";
|
|
|
|
} else {
|
|
|
|
m_workingDir = "./testfiles/workingdir/";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cerr << m_workingDir << endl << endl;
|
|
|
|
|
2016-02-06 02:52:06 +01:00
|
|
|
m_valid = true;
|
|
|
|
cerr << "Executing test cases ..." << endl;
|
|
|
|
} catch(const Failure &failure) {
|
|
|
|
cerr << "Invalid arguments specified: " << failure.what() << endl;
|
|
|
|
m_valid = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Destroys the TestApplication.
|
|
|
|
*/
|
|
|
|
TestApplication::~TestApplication()
|
|
|
|
{
|
|
|
|
m_instance = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
2016-02-09 02:21:42 +01:00
|
|
|
* \brief Returns the full path of the test file with the specified \a name.
|
2016-02-06 02:52:06 +01:00
|
|
|
*/
|
|
|
|
string TestApplication::testFilePath(const string &name) const
|
|
|
|
{
|
|
|
|
string path;
|
2016-02-09 02:21:42 +01:00
|
|
|
fstream file; // used to check whether the file is present
|
|
|
|
|
|
|
|
// check the path specified by command line argument
|
2016-02-06 02:52:06 +01:00
|
|
|
if(m_testFilesPathArg.isPresent()) {
|
|
|
|
file.open(path = m_testFilesPathArgValue + name, ios_base::in);
|
|
|
|
if(file.good()) {
|
|
|
|
return path;
|
|
|
|
}
|
|
|
|
}
|
2016-02-09 02:21:42 +01:00
|
|
|
|
|
|
|
// check the path specified by environment variable
|
2016-02-06 02:52:06 +01:00
|
|
|
if(!m_testFilesPathEnvValue.empty()) {
|
|
|
|
file.clear();
|
|
|
|
file.open(path = m_testFilesPathEnvValue + name, ios_base::in);
|
|
|
|
if(file.good()) {
|
|
|
|
return path;
|
|
|
|
}
|
|
|
|
}
|
2016-02-09 02:21:42 +01:00
|
|
|
|
|
|
|
// file still not found -> return default path
|
2016-02-06 02:52:06 +01:00
|
|
|
return "./testfiles/" + name;
|
|
|
|
}
|
|
|
|
|
2016-02-27 01:18:54 +01:00
|
|
|
#ifdef PLATFORM_UNIX
|
2016-02-09 02:21:42 +01:00
|
|
|
/*!
|
|
|
|
* \brief Returns the full path to a working copy of the test file with the specified \a name.
|
|
|
|
*/
|
|
|
|
string TestApplication::workingCopyPath(const string &name) const
|
|
|
|
{
|
|
|
|
// create file streams
|
|
|
|
fstream origFile, workingCopy;
|
|
|
|
origFile.exceptions(ios_base::badbit | ios_base::failbit);
|
|
|
|
workingCopy.exceptions(ios_base::badbit | ios_base::failbit);
|
|
|
|
|
|
|
|
// ensure working directory is present
|
|
|
|
struct stat currentStat;
|
|
|
|
if(stat(m_workingDir.c_str(), ¤tStat) || !S_ISDIR(currentStat.st_mode)) {
|
|
|
|
if(mkdir(m_workingDir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)) {
|
|
|
|
cerr << "Unable to create working copy for \"" << name << "\": can't create working directory." << endl;
|
|
|
|
return string();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-17 20:21:11 +01:00
|
|
|
// ensure subdirectory exists
|
|
|
|
const auto parts = splitString<vector<string> >(name, string("/"), EmptyPartsTreat::Omit);
|
|
|
|
if(!parts.empty()) {
|
|
|
|
string currentLevel = m_workingDir;
|
|
|
|
for(auto i = parts.cbegin(), end = parts.end() - 1; i != end; ++i) {
|
2016-04-16 00:50:16 +02:00
|
|
|
if(currentLevel.back() != '/') {
|
|
|
|
currentLevel += '/';
|
|
|
|
}
|
|
|
|
currentLevel += *i;
|
|
|
|
if(stat(currentLevel.c_str(), ¤tStat) || !S_ISDIR(currentStat.st_mode)) {
|
2016-02-17 20:21:11 +01:00
|
|
|
if(mkdir(currentLevel.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)) {
|
|
|
|
cerr << "Unable to create working copy for \"" << name << "\": can't create working directory." << endl;
|
|
|
|
return string();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-09 02:21:42 +01:00
|
|
|
// copy file
|
|
|
|
try {
|
|
|
|
origFile.open(testFilePath(name), ios_base::in | ios_base::binary);
|
|
|
|
string path = m_workingDir + name;
|
|
|
|
workingCopy.open(path, ios_base::out | ios_base::binary | ios_base::trunc);
|
|
|
|
workingCopy << origFile.rdbuf();
|
|
|
|
return path;
|
|
|
|
} catch(const ios_base::failure &) {
|
|
|
|
cerr << "Unable to create working copy for \"" << name << "\": an IO error occured." << endl;
|
|
|
|
}
|
|
|
|
return string();
|
|
|
|
}
|
2016-02-27 01:18:54 +01:00
|
|
|
#endif
|
2016-02-09 02:21:42 +01:00
|
|
|
|
2016-02-06 02:52:06 +01:00
|
|
|
}
|