Viewing file: TestSupport.h (7.15 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
#ifndef _TEST_SUPPORT_H_ #define _TEST_SUPPORT_H_
#include <sys/stat.h> #include <sys/types.h> #include <iostream> #include <string> #include <vector> #include <exception> #include <cstdio> #include <cerrno> #include <cstring> #include <ctime> #include <unistd.h> #include <utime.h>
#include <oxt/thread.hpp> #include <oxt/tracable_exception.hpp> #include <uv.h>
#include "../tut/tut.h" #include <ResourceLocator.h> #include <InstanceDirectory.h> #include <BackgroundEventLoop.h> #include <Exceptions.h> #include <ProcessManagement/Spawn.h> #include <ProcessManagement/Utils.h> #include <Utils.h> #include <SystemTools/SystemTime.h> #include <jsoncpp/json-forwards.h>
extern "C" { struct ev_loop; struct ev_async; }
namespace Passenger { class SafeLibev; }
namespace TestSupport {
using namespace std; using namespace Passenger; using namespace oxt;
#define SHOW_EXCEPTION_BACKTRACE(code) \ do { \ try { \ code \ } catch (const tracable_exception &e) { \ cerr << e.what() << "\n" << e.backtrace(); \ throw; \ } \ } while (0)
#define EVENTUALLY2(deadlineMsec, sleepTimeMsec, code) \ do { \ unsigned long long deadlineTime = uv_hrtime() + deadlineMsec * 1000000ull; \ bool result = false; \ while (!result && uv_hrtime() < deadlineTime) { \ { \ code \ } \ if (!result) { \ usleep(sleepTimeMsec * 1000ull); \ } \ } \ if (!result) { \ fail("EVENTUALLY(" #code ") failed"); \ } \ } while (0)
#define EVENTUALLY(deadlineSec, code) EVENTUALLY2(deadlineSec * 1000ull, 10, code)
#define SHOULD_NEVER_HAPPEN(deadlineMsec, code) \ do { \ unsigned long long deadlineTime = uv_hrtime() + deadlineMsec * 1000000ull; \ bool result = false; \ while (!result && uv_hrtime() < deadlineTime) { \ code \ if (!result) { \ usleep(10000); \ } \ } \ if (result) { \ fail("SHOULD_NEVER_HAPPEN(" #code ") failed"); \ } \ } while (0)
// Do not run some tests in the Vagrant development environment because // they don't work over NFS. #define DONT_RUN_IN_VAGRANT() \ do { \ if (getenv("PASSENGER_VAGRANT_ENVIRONMENT") != NULL) { \ return; \ } \ } while (false)
extern LoggingKit::Level defaultLogLevel; extern ResourceLocator *resourceLocator; extern Json::Value testConfig;
/** * Create an instance directory with default parameters, suitable for unit testing. */ void createInstanceDir(InstanceDirectoryPtr &instanceDir);
/** * Writes zeroes into the given file descriptor its buffer is full (i.e. * the next write will block). * * @throws SystemException */ void writeUntilFull(int fd);
/** * Returns whether 'str' contains the given substring. */ bool containsSubstring(const StaticString &str, const StaticString &substr);
/** * Writes the given data into the given file. * * @throws FileSystemException */ void writeFile(const string &filename, const string &contents);
/** * Touch the given file: create the file if it doesn't exist, update its * timestamp if it does. If the <tt>timestamp</tt> argument is -1, then * the current system time will be used, otherwise the given timestamp * will be used. * * @throws FileSystemException */ void touchFile(const char *filename, time_t timestamp = (time_t) - 1);
/** * Returns the name of the primary group of the given user. */ string getPrimaryGroupName(const string &username);
/** * Class which creates a temporary directory of the given name, and deletes * it upon destruction. */ class TempDir { private: string name; bool ignoreRemoveErrors; public: TempDir(const string &name, bool _ignoreRemoveErrors = false) { this->name = name; if (mkdir(name.c_str(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) != 0 && errno != EEXIST) { int e = errno; string message = "Cannot create directory '"; message.append(name); message.append("'"); throw FileSystemException(message, e, name); } ignoreRemoveErrors = _ignoreRemoveErrors; }
~TempDir() { if (ignoreRemoveErrors) { try { removeDirTree(name); } catch (const RuntimeException &) { // Do nothing. } } else { removeDirTree(name); } }
string getPath() const { return name; } };
/** * All test classes must derive from this base class. */ class TestBase { public: TestBase(); };
/** * Creates a temporary copy of the given directory. This copy is deleted * upon object destruction. */ class TempDirCopy { private: string dir; public: TempDirCopy(const string &source, const string &dest) { dir = dest; removeDirTree(dest);
char command[1024]; snprintf(command, sizeof(command), "cp -pR \"%s\" \"%s\"", source.c_str(), dest.c_str()); runShellCommand(command); }
~TempDirCopy() { removeDirTree(dir); } };
/** * Class which deletes the given file upon destruction. */ class DeleteFileEventually { private: string filename; public: DeleteFileEventually(const string &filename, bool deleteNow = true) { this->filename = filename; if (deleteNow) { unlink(filename.c_str()); } }
~DeleteFileEventually() { unlink(filename.c_str()); } };
/** * Spawns a thread which will be interrupted and joined when this TempThread * object is destroyed. */ class TempThread { public: oxt::thread thread; bool joined;
TempThread(boost::function<void ()> func) : thread(boost::bind(runAndPrintExceptions, func, true)), joined(false) { }
~TempThread() { if (!joined) { thread.interrupt_and_join(); } }
void join() { thread.join(); joined = true; } };
class AtomicInt { private: mutable boost::mutex lock; int val; public: AtomicInt() { val = 0; }
AtomicInt(int value) { val = value; }
AtomicInt(const AtomicInt &other) { val = other.val; }
int get() const { boost::lock_guard<boost::mutex> l(lock); return val; }
void set(int value) { boost::lock_guard<boost::mutex> l(lock); val = value; }
AtomicInt &operator=(int value) { set(value); return *this; }
AtomicInt &operator++() { boost::lock_guard<boost::mutex> l(lock); val++; return *this; }
AtomicInt operator++(int) { boost::lock_guard<boost::mutex> l(lock); AtomicInt temp(*this); val++; return temp; }
operator int() const { return get(); } };
} // namespace TestSupport
namespace tut { using namespace TestSupport; }
#endif /* _TEST_SUPPORT_H_ */
|