From 25ef4e28a2f4ecc5558ff8f446d0c908063aec44 Mon Sep 17 00:00:00 2001 From: Martchus Date: Sat, 29 Sep 2018 20:52:13 +0200 Subject: [PATCH] Make tests compile under Windows --- application/argumentparser.h | 2 +- tests/argumentparsertests.cpp | 13 ++++++ tests/testutils.cpp | 76 +++++++++++++++++++++++++++-------- tests/testutils.h | 6 +-- 4 files changed, 77 insertions(+), 20 deletions(-) diff --git a/application/argumentparser.h b/application/argumentparser.h index e60557d..2815581 100644 --- a/application/argumentparser.h +++ b/application/argumentparser.h @@ -148,7 +148,7 @@ template > /// \cond namespace Helper { -struct ArgumentValueConversionError { +struct CPP_UTILITIES_EXPORT ArgumentValueConversionError { const char *const errorMessage; const char *const valueToConvert; const char *const targetTypeName; diff --git a/tests/argumentparsertests.cpp b/tests/argumentparsertests.cpp index 4aad1f9..51396d8 100644 --- a/tests/argumentparsertests.cpp +++ b/tests/argumentparsertests.cpp @@ -29,6 +29,19 @@ using namespace TestUtilities::Literals; using namespace CPPUNIT_NS; +#ifdef PLATFORM_WINDOWS +void setenv(const char *variableName, const char *value, bool replace) +{ + VAR_UNUSED(replace) + _putenv_s(variableName, value); +} + +void unsetenv(const char *variableName) +{ + _putenv_s(variableName, ""); +} +#endif + /*! * \brief The ArgumentParserTests class tests the ArgumentParser and Argument classes. */ diff --git a/tests/testutils.cpp b/tests/testutils.cpp index 4ecf6e8..e0413c1 100644 --- a/tests/testutils.cpp +++ b/tests/testutils.cpp @@ -7,6 +7,7 @@ #include "../io/catchiofailure.h" #include "../io/misc.h" #include "../io/path.h" +#include "../io/nativefilestream.h" #include #include @@ -23,6 +24,10 @@ #include #endif +#ifdef PLATFORM_WINDOWS +#include +#endif + using namespace std; using namespace ApplicationUtilities; using namespace ConversionUtilities; @@ -34,6 +39,52 @@ using namespace IoUtilities; */ namespace TestUtilities { +bool fileSystemItemExists(const string &path) +{ +#ifdef PLATFORM_UNIX + struct stat res; + return stat(path.data(), &res) == 0; +#else + const auto widePath(NativeFileStream::makeWidePath(path)); + const auto fileType(GetFileAttributesW(widePath.get())); + return fileType != INVALID_FILE_ATTRIBUTES; +#endif +} + +bool fileExists(const string &path) +{ +#ifdef PLATFORM_UNIX + struct stat res; + return stat(path.data(), &res) == 0 && !S_ISDIR(res.st_mode); +#else + const auto widePath(NativeFileStream::makeWidePath(path)); + const auto fileType(GetFileAttributesW(widePath.get())); + return fileType != INVALID_FILE_ATTRIBUTES && fileType != FILE_ATTRIBUTE_DIRECTORY; +#endif +} + +bool dirExists(const string &path) +{ +#ifdef PLATFORM_UNIX + struct stat res; + return stat(path.data(), &res) == 0 && S_ISDIR(res.st_mode); +#else + const auto widePath(NativeFileStream::makeWidePath(path)); + const auto fileType(GetFileAttributesW(widePath.get())); + return fileType != INVALID_FILE_ATTRIBUTES && fileType == FILE_ATTRIBUTE_DIRECTORY; +#endif +} + +bool makeDir(const string &path) +{ +#ifdef PLATFORM_UNIX + return mkdir(path.data(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) == 0; +#else + const auto widePath(NativeFileStream::makeWidePath(path)); + return CreateDirectoryW(widePath.get(), nullptr) || GetLastError() == ERROR_ALREADY_EXISTS; +#endif +} + TestApplication *TestApplication::m_instance = nullptr; /*! @@ -175,31 +226,25 @@ string TestApplication::testFilePath(const string &name) const // check the path specified by command line argument or via environment variable if (!m_testFilesPath.empty()) { - file.open(path = m_testFilesPath + name, ios_base::in); - if (file.good()) { + if (fileExists(path = m_testFilesPath + name)) { return path; } } // check the fallback path (value from environment variable or source directory) if (!m_fallbackTestFilesPath.empty()) { - file.clear(); - file.open(path = m_fallbackTestFilesPath + name, ios_base::in); - if (file.good()) { + if (fileExists(path = m_fallbackTestFilesPath + name)) { return path; } } // file still not found -> return default path - file.clear(); - file.open(path = "./testfiles/" + name, ios_base::in); - if (!file.good()) { + if (!fileExists(path = "./testfiles/" + name)) { cerr << Phrases::Warning << "The testfile \"" << path << "\" can not be located." << Phrases::EndFlush; } return path; } -#ifdef PLATFORM_UNIX /*! * \brief Returns the full path to a working copy of the test file with the specified \a name. * @@ -211,9 +256,7 @@ string TestApplication::testFilePath(const string &name) const string TestApplication::workingCopyPathMode(const string &name, WorkingCopyMode mode) const { // ensure working directory is present - struct stat currentStat; - if ((stat(m_workingDir.c_str(), ¤tStat) || !S_ISDIR(currentStat.st_mode)) - && mkdir(m_workingDir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)) { + if (!dirExists(m_workingDir) && !makeDir(m_workingDir)) { cerr << Phrases::Error << "Unable to create working copy for \"" << name << "\": can't create working directory." << Phrases::EndFlush; return string(); } @@ -232,11 +275,11 @@ string TestApplication::workingCopyPathMode(const string &name, WorkingCopyMode currentLevel += *i; // continue if subdirectory level already exists - if (!stat(currentLevel.c_str(), ¤tStat) && S_ISDIR(currentStat.st_mode)) { + if (dirExists(currentLevel)) { continue; } // continue if we can successfully create the directory - if (!mkdir(currentLevel.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)) { + if (!makeDir(currentLevel)) { continue; } // fail otherwise @@ -254,7 +297,7 @@ string TestApplication::workingCopyPathMode(const string &name, WorkingCopyMode const auto origFilePath(testFilePath(name)); auto workingCopyPath(m_workingDir + name); size_t workingCopyPathAttempt = 0; - fstream origFile, workingCopy; + NativeFileStream origFile, workingCopy; origFile.open(origFilePath, ios_base::in | ios_base::binary); if (origFile.fail()) { cerr << Phrases::Error << "Unable to create working copy for \"" << name << "\": an IO error occurred when opening original file \"" @@ -263,7 +306,7 @@ string TestApplication::workingCopyPathMode(const string &name, WorkingCopyMode return string(); } workingCopy.open(workingCopyPath, ios_base::out | ios_base::binary | ios_base::trunc); - while (workingCopy.fail() && !stat(workingCopyPath.c_str(), ¤tStat)) { + while (workingCopy.fail() && fileSystemItemExists(workingCopyPath)) { // adjust the working copy path if the target file already exists and can not be truncated workingCopyPath = argsToString(m_workingDir, name, '.', ++workingCopyPathAttempt); workingCopy.clear(); @@ -307,6 +350,7 @@ string TestApplication::workingCopyPath(const string &name) const return workingCopyPathMode(name, WorkingCopyMode::CreateCopy); } +#ifdef PLATFORM_UNIX /*! * \brief Executes an application with the specified \a args. * \remarks Provides internal implementation of execApp() and execHelperApp(). diff --git a/tests/testutils.h b/tests/testutils.h index 15e22e8..84b01b0 100644 --- a/tests/testutils.h +++ b/tests/testutils.h @@ -26,9 +26,9 @@ public: operator bool() const; std::string testFilePath(const std::string &name) const; -#ifdef PLATFORM_UNIX std::string workingCopyPathMode(const std::string &name, WorkingCopyMode mode) const; std::string workingCopyPath(const std::string &name) const; +#ifdef PLATFORM_UNIX int execApp(const char *const *args, std::string &output, std::string &errors, bool suppressLogging = false, int timeout = -1) const; #endif bool unitsSpecified() const; @@ -107,7 +107,6 @@ inline CPP_UTILITIES_EXPORT std::string testFilePath(const std::string &name) return TestApplication::instance()->testFilePath(name); } -#ifdef PLATFORM_UNIX /*! * \brief Convenience function which returns the full path to a working copy of the test file with the specified \a name. * \remarks A TestApplication must be present. @@ -128,6 +127,7 @@ inline CPP_UTILITIES_EXPORT std::string workingCopyPathMode(const std::string &n return TestApplication::instance()->workingCopyPathMode(name, mode); } +#ifdef PLATFORM_UNIX /*! * \brief Convenience function which executes the application to be tested with the specified \a args. * \remarks A TestApplication must be present. @@ -140,7 +140,7 @@ inline CPP_UTILITIES_EXPORT int execApp(const char *const *args, std::string &ou CPP_UTILITIES_EXPORT int execHelperApp( const char *appPath, const char *const *args, std::string &output, std::string &errors, bool suppressLogging = false, int timeout = -1); -#endif +#endif // PLATFORM_UNIX /*! * \brief The AsHexNumber class allows printing values asserted with cppunit (or similar test framework) using the