--- /dev/null
+#ifndef BLOBS_IO_TOKEN_HPP_
+#define BLOBS_IO_TOKEN_HPP_
+
+#include <iosfwd>
+#include <string>
+
+
+namespace blobs {
+namespace io {
+
+struct Token {
+ enum Type {
+ UNKNOWN = 0,
+ ANGLE_BRACKET_OPEN = '{',
+ ANGLE_BRACKET_CLOSE = '}',
+ CHEVRON_OPEN = '<',
+ CHEVRON_CLOSE = '>',
+ BRACKET_OPEN = '[',
+ BRACKET_CLOSE = ']',
+ PARENTHESIS_OPEN = '(',
+ PARENTHESIS_CLOSE = ')',
+ COLON = ':',
+ SEMICOLON = ';',
+ COMMA = ',',
+ EQUALS = '=',
+ NUMBER = '0',
+ STRING = '"',
+ IDENTIFIER = 'a',
+ COMMENT = '#',
+ } type = UNKNOWN;
+ std::string value;
+};
+
+std::ostream &operator <<(std::ostream &, Token::Type);
+std::ostream &operator <<(std::ostream &, const Token &);
+
+}
+}
+
+#endif
--- /dev/null
+#ifndef BLOBS_IO_TOKENSTREAMREADER_HPP_
+#define BLOBS_IO_TOKENSTREAMREADER_HPP_
+
+#include "Token.hpp"
+#include "Tokenizer.hpp"
+#include "../graphics/glm.hpp"
+
+#include <iosfwd>
+#include <string>
+
+
+namespace blobs {
+namespace io {
+
+class TokenStreamReader {
+
+public:
+ explicit TokenStreamReader(std::istream &);
+
+ bool HasMore();
+ const Token &Next();
+ const Token &Peek();
+
+ void Skip(Token::Type);
+
+ void ReadBoolean(bool &);
+ void ReadIdentifier(std::string &);
+ void ReadNumber(float &);
+ void ReadNumber(int &);
+ void ReadNumber(unsigned long &);
+ void ReadString(std::string &);
+
+ void ReadVec(glm::vec2 &);
+ void ReadVec(glm::vec3 &);
+ void ReadVec(glm::vec4 &);
+
+ void ReadVec(glm::ivec2 &);
+ void ReadVec(glm::ivec3 &);
+ void ReadVec(glm::ivec4 &);
+
+ void ReadQuat(glm::quat &);
+
+ // the Get* functions advance to the next token
+ // the As* functions try to cast the current token
+ // if the value could not be converted, a std::runtime_error is thrown
+
+ bool GetBool();
+ bool AsBool() const;
+ float GetFloat();
+ float AsFloat() const;
+ int GetInt();
+ int AsInt() const;
+ unsigned long GetULong();
+ unsigned long AsULong() const;
+
+private:
+ void SkipComments();
+
+ void Assert(Token::Type) const;
+ Token::Type GetType() const noexcept;
+ const std::string &GetValue() const noexcept;
+
+ Tokenizer in;
+ bool cached;
+
+};
+
+}
+}
+
+#endif
--- /dev/null
+#ifndef BLOBS_IO_TOKENIZER_HPP_
+#define BLOBS_IO_TOKENIZER_HPP_
+
+#include "Token.hpp"
+
+#include <iosfwd>
+
+
+namespace blobs {
+namespace io {
+
+class Tokenizer {
+
+public:
+
+public:
+ explicit Tokenizer(std::istream &in);
+
+ bool HasMore();
+ const Token &Next();
+ const Token &Current() const noexcept { return current; }
+
+private:
+ void ReadToken();
+
+ void ReadNumber();
+ void ReadString();
+ void ReadComment();
+ void ReadIdentifier();
+
+ std::istream ∈
+ Token current;
+
+};
+
+}
+}
+
+#endif
--- /dev/null
+#include "event.hpp"
+
+#include <cctype>
+#include <ostream>
+
+using std::ostream;
+
+
+namespace blobs {
+namespace io {
+
+ostream &operator <<(ostream &out, const SDL_Event &evt) {
+ switch (evt.type) {
+#if SDL_VERSION_ATLEAST(2, 0, 4)
+ case SDL_AUDIODEVICEADDED:
+ out << "audio device added: " << evt.adevice;
+ break;
+ case SDL_AUDIODEVICEREMOVED:
+ out << "audio device removed: " << evt.adevice;
+ break;
+#endif
+ case SDL_CONTROLLERAXISMOTION:
+ out << "controller axis motion: " << evt.caxis;
+ break;
+ case SDL_CONTROLLERBUTTONDOWN:
+ out << "controller button down: " << evt.cbutton;
+ break;
+ case SDL_CONTROLLERBUTTONUP:
+ out << "controller button up: " << evt.cbutton;
+ break;
+ case SDL_CONTROLLERDEVICEADDED:
+ out << "controller device added: " << evt.cdevice;
+ break;
+ case SDL_CONTROLLERDEVICEREMOVED:
+ out << "controller device removed: " << evt.cdevice;
+ break;
+ case SDL_CONTROLLERDEVICEREMAPPED:
+ out << "controller device remapped: " << evt.cdevice;
+ break;
+ case SDL_DOLLARGESTURE:
+ out << "dollar gesture: " << evt.dgesture;
+ break;
+ case SDL_DOLLARRECORD:
+ out << "dollar record: " << evt.dgesture;
+ break;
+ case SDL_DROPFILE:
+ out << "drop file: " << evt.drop;
+ break;
+ case SDL_FINGERMOTION:
+ out << "finger motion: " << evt.tfinger;
+ break;
+ case SDL_FINGERDOWN:
+ out << "finger down: " << evt.tfinger;
+ break;
+ case SDL_FINGERUP:
+ out << "finger up: " << evt.tfinger;
+ break;
+ case SDL_KEYDOWN:
+ out << "key down: " << evt.key;
+ break;
+ case SDL_KEYUP:
+ out << "key up: " << evt.key;
+ break;
+ case SDL_JOYAXISMOTION:
+ out << "joystick axis motion: " << evt.jaxis;
+ break;
+ case SDL_JOYBALLMOTION:
+ out << "joystick ball motion: " << evt.jball;
+ break;
+ case SDL_JOYHATMOTION:
+ out << "joystick hat motion: " << evt.jhat;
+ break;
+ case SDL_JOYBUTTONDOWN:
+ out << "joystick button down: " << evt.jbutton;
+ break;
+ case SDL_JOYBUTTONUP:
+ out << "joystick button up: " << evt.jbutton;
+ break;
+ case SDL_JOYDEVICEADDED:
+ out << "joystick device added: " << evt.jdevice;
+ break;
+ case SDL_JOYDEVICEREMOVED:
+ out << "joystick device removed: " << evt.jdevice;
+ break;
+ case SDL_MOUSEMOTION:
+ out << "mouse motion: " << evt.motion;
+ break;
+ case SDL_MOUSEBUTTONDOWN:
+ out << "mouse button down: " << evt.button;
+ break;
+ case SDL_MOUSEBUTTONUP:
+ out << "mouse button up: " << evt.button;
+ break;
+ case SDL_MOUSEWHEEL:
+ out << "mouse wheel: " << evt.wheel;
+ break;
+ case SDL_MULTIGESTURE:
+ out << "multi gesture: " << evt.mgesture;
+ break;
+ case SDL_QUIT:
+ out << "quit: " << evt.quit;
+ break;
+ case SDL_SYSWMEVENT:
+ out << "sys wm: " << evt.syswm;
+ break;
+ case SDL_TEXTEDITING:
+ out << "text editing: " << evt.edit;
+ break;
+ case SDL_TEXTINPUT:
+ out << "text input: " << evt.text;
+ break;
+ case SDL_USEREVENT:
+ out << "user: " << evt.user;
+ break;
+ case SDL_WINDOWEVENT:
+ out << "window: " << evt.window;
+ break;
+ default:
+ out << "unknown";
+ break;
+ }
+ return out;
+}
+
+ostream &operator <<(ostream &out, const SDL_WindowEvent &evt) {
+ switch (evt.event) {
+ case SDL_WINDOWEVENT_SHOWN:
+ out << "shown, window ID: " << evt.windowID;
+ break;
+ case SDL_WINDOWEVENT_HIDDEN:
+ out << "hidden, window ID: " << evt.windowID;
+ break;
+ case SDL_WINDOWEVENT_EXPOSED:
+ out << "exposed, window ID: " << evt.windowID;
+ break;
+ case SDL_WINDOWEVENT_MOVED:
+ out << "moved, window ID: " << evt.windowID
+ << ", position: " << evt.data1 << ' ' << evt.data2;
+ break;
+ case SDL_WINDOWEVENT_RESIZED:
+ out << "resized, window ID: " << evt.windowID
+ << ", size: " << evt.data1 << 'x' << evt.data2;
+ break;
+ case SDL_WINDOWEVENT_SIZE_CHANGED:
+ out << "size changed, window ID: " << evt.windowID
+ << ", size: " << evt.data1 << 'x' << evt.data2;
+ break;
+ case SDL_WINDOWEVENT_MINIMIZED:
+ out << "minimized, window ID: " << evt.windowID;
+ break;
+ case SDL_WINDOWEVENT_MAXIMIZED:
+ out << "maximized, window ID: " << evt.windowID;
+ break;
+ case SDL_WINDOWEVENT_RESTORED:
+ out << "restored, window ID: " << evt.windowID;
+ break;
+ case SDL_WINDOWEVENT_ENTER:
+ out << "mouse entered, window ID: " << evt.windowID;
+ break;
+ case SDL_WINDOWEVENT_LEAVE:
+ out << "mouse left, window ID: " << evt.windowID;
+ break;
+ case SDL_WINDOWEVENT_FOCUS_GAINED:
+ out << "focus gained, window ID: " << evt.windowID;
+ break;
+ case SDL_WINDOWEVENT_FOCUS_LOST:
+ out << "focus lost, window ID: " << evt.windowID;
+ break;
+ case SDL_WINDOWEVENT_CLOSE:
+ out << "closed, window ID: " << evt.windowID;
+ break;
+ default:
+ out << "unknown";
+ break;
+ }
+ return out;
+}
+
+ostream &operator <<(ostream &out, const SDL_KeyboardEvent &evt) {
+ out << "window ID: " << evt.windowID
+ << ", state: " << (evt.state == SDL_PRESSED ? "pressed" : "released")
+ << ", repeat: " << (evt.repeat ? "yes" : "no")
+ << ", keysym: " << evt.keysym;
+ return out;
+}
+
+ostream &operator <<(ostream &out, const SDL_Keysym &keysym) {
+ out << "scancode: " << int(keysym.scancode)
+ << ", sym: " << int(keysym.sym)
+ << " (\"" << SDL_GetKeyName(keysym.sym) << "\")";
+ if (keysym.mod) {
+ out << ", mod:";
+ if (keysym.mod & KMOD_LSHIFT) {
+ out << " LSHIFT";
+ }
+ if (keysym.mod & KMOD_RSHIFT) {
+ out << " RSHIFT";
+ }
+ if (keysym.mod & KMOD_LCTRL) {
+ out << " LCTRL";
+ }
+ if (keysym.mod & KMOD_RCTRL) {
+ out << " RCTRL";
+ }
+ if (keysym.mod & KMOD_LALT) {
+ out << " LALT";
+ }
+ if (keysym.mod & KMOD_RALT) {
+ out << " RALT";
+ }
+ if (keysym.mod & KMOD_LGUI) {
+ out << " LSUPER";
+ }
+ if (keysym.mod & KMOD_RGUI) {
+ out << " RSUPER";
+ }
+ if (keysym.mod & KMOD_NUM) {
+ out << " NUM";
+ }
+ if (keysym.mod & KMOD_CAPS) {
+ out << " CAPS";
+ }
+ if (keysym.mod & KMOD_MODE) {
+ out << " ALTGR";
+ }
+ }
+ return out;
+}
+
+ostream &operator <<(ostream &out, const SDL_TextEditingEvent &evt) {
+ out << "window ID: " << evt.windowID
+ << ", text: \"" << evt.text
+ << "\", start: " << evt.start
+ << ", length: " << evt.length;
+ return out;
+}
+
+ostream &operator <<(ostream &out, const SDL_TextInputEvent &evt) {
+ out << "window ID: " << evt.windowID
+ << ", text: \"" << evt.text << '"';
+ return out;
+}
+
+ostream &operator <<(ostream &out, const SDL_MouseMotionEvent &evt) {
+ out << "window ID: " << evt.windowID
+ << ", mouse ID: " << evt.which
+ << ", position: " << evt.x << ' ' << evt.y
+ << ", delta: " << evt.xrel << ' ' << evt.yrel;
+ if (evt.state) {
+ out << ", buttons:";
+ if (evt.state & SDL_BUTTON_LMASK) {
+ out << " left";
+ }
+ if (evt.state & SDL_BUTTON_MMASK) {
+ out << " middle";
+ }
+ if (evt.state & SDL_BUTTON_RMASK) {
+ out << " right";
+ }
+ if (evt.state & SDL_BUTTON_X1MASK) {
+ out << " X1";
+ }
+ if (evt.state & SDL_BUTTON_X2MASK) {
+ out << " X2";
+ }
+ }
+ return out;
+}
+
+ostream &operator <<(ostream &out, const SDL_MouseButtonEvent &evt) {
+ out << "window ID: " << evt.windowID
+ << ", mouse ID: " << evt.which
+ << ", button: ";
+ switch (evt.button) {
+ case SDL_BUTTON_LEFT:
+ out << "left";
+ break;
+ case SDL_BUTTON_MIDDLE:
+ out << "middle";
+ break;
+ case SDL_BUTTON_RIGHT:
+ out << "right";
+ break;
+ case SDL_BUTTON_X1:
+ out << "X1";
+ break;
+ case SDL_BUTTON_X2:
+ out << "X2";
+ break;
+ default:
+ out << int(evt.button);
+ break;
+ }
+ out << ", state: " << (evt.state == SDL_PRESSED ? "pressed" : "released")
+ << ", clicks: " << int(evt.clicks)
+ << ", position: " << evt.x << ' ' << evt.y;
+ return out;
+}
+
+ostream &operator <<(ostream &out, const SDL_MouseWheelEvent &evt) {
+ out << "window ID: " << evt.windowID
+ << ", mouse ID: " << evt.which
+ << ", delta: " << evt.x << ' ' << evt.y
+#if SDL_VERSION_ATLEAST(2, 0, 4)
+ << ", direction: " << (evt.direction == SDL_MOUSEWHEEL_NORMAL ? "normal" : "flipped")
+#endif
+ ;
+ return out;
+}
+
+ostream &operator <<(ostream &out, const SDL_JoyAxisEvent &evt) {
+ out << "joystick ID: " << evt.which
+ << ", axis ID: " << int(evt.axis)
+ << ", value: " << (float(evt.value) / 32768.0f);
+ return out;
+}
+
+ostream &operator <<(ostream &out, const SDL_JoyBallEvent &evt) {
+ out << "joystick ID: " << evt.which
+ << ", ball ID: " << int(evt.ball)
+ << ", delta: " << evt.xrel << ' ' << evt.yrel;
+ return out;
+}
+
+ostream &operator <<(ostream &out, const SDL_JoyHatEvent &evt) {
+ out << "joystick ID: " << evt.which
+ << ", hat ID: " << int(evt.hat)
+ << ", value: ";
+ switch (evt.value) {
+ case SDL_HAT_LEFTUP:
+ out << "left up";
+ break;
+ case SDL_HAT_UP:
+ out << "up";
+ break;
+ case SDL_HAT_RIGHTUP:
+ out << "right up";
+ break;
+ case SDL_HAT_LEFT:
+ out << "left";
+ break;
+ case SDL_HAT_CENTERED:
+ out << "center";
+ break;
+ case SDL_HAT_RIGHT:
+ out << "right";
+ break;
+ case SDL_HAT_LEFTDOWN:
+ out << "left down";
+ break;
+ case SDL_HAT_DOWN:
+ out << "down";
+ break;
+ case SDL_HAT_RIGHTDOWN:
+ out << "right down";
+ break;
+ default:
+ out << "unknown";
+ break;
+ }
+ return out;
+}
+
+ostream &operator <<(ostream &out, const SDL_JoyButtonEvent &evt) {
+ out << "joystick ID: " << evt.which
+ << ", button ID: " << int(evt.button)
+ << ", state: " << (evt.state == SDL_PRESSED ? "pressed" : "released");
+ return out;
+}
+
+ostream &operator <<(ostream &out, const SDL_JoyDeviceEvent &evt) {
+ out << "joystick ID: " << evt.which;
+ return out;
+}
+
+ostream &operator <<(ostream &out, const SDL_ControllerAxisEvent &evt) {
+ out << "controller ID: " << evt.which
+ << ", axis ID: " << int(evt.axis)
+ << ", value: " << (float(evt.value) / 32768.0f);
+ return out;
+}
+
+ostream &operator <<(ostream &out, const SDL_ControllerButtonEvent &evt) {
+ out << "controller ID: " << evt.which
+ << ", button ID: " << int(evt.button)
+ << ", state: " << (evt.state == SDL_PRESSED ? "pressed" : "released");
+ return out;
+}
+
+ostream &operator <<(ostream &out, const SDL_ControllerDeviceEvent &evt) {
+ out << "controller ID: " << evt.which;
+ return out;
+}
+
+#if SDL_VERSION_ATLEAST(2, 0, 4)
+ostream &operator <<(ostream &out, const SDL_AudioDeviceEvent &evt) {
+ out << "device ID: " << evt.which
+ << ", capture: " << (evt.iscapture ? "yes" : "no");
+ return out;
+}
+#endif
+
+ostream &operator <<(ostream &out, const SDL_QuitEvent &evt) {
+ out << "quit";
+ return out;
+}
+
+ostream &operator <<(ostream &out, const SDL_UserEvent &evt) {
+ out << "window ID: " << evt.windowID
+ << ", code: " << evt.code
+ << ", data 1: " << evt.data1
+ << ", data 2: " << evt.data2;
+ return out;
+}
+
+ostream &operator <<(ostream &out, const SDL_SysWMEvent &evt) {
+ if (evt.msg) {
+ out << "with message";
+ } else {
+ out << "without message";
+ }
+ return out;
+}
+
+ostream &operator <<(ostream &out, const SDL_TouchFingerEvent &evt) {
+ out << "device ID: " << evt.touchId
+ << ", finger ID: " << evt.fingerId
+ << ", position: " << evt.x << ' ' << evt.y
+ << ", delta: " << evt.dx << ' ' << evt.dy
+ << ", pressure: " << evt.pressure;
+ return out;
+}
+
+ostream &operator <<(ostream &out, const SDL_MultiGestureEvent &evt) {
+ out << "device ID: " << evt.touchId
+ << ", theta: " << evt.dTheta
+ << ", distance: " << evt.dDist
+ << ", position: " << evt.x << ' ' << evt.y
+ << ", fingers: " << evt.numFingers;
+ return out;
+}
+
+ostream &operator <<(ostream &out, const SDL_DollarGestureEvent &evt) {
+ out << "device ID: " << evt.touchId
+ << ", gesture ID: " << evt.gestureId
+ << ", fingers: " << evt.numFingers
+ << ", error: " << evt.error
+ << ", position: " << evt.x << ' ' << evt.y;
+ return out;
+}
+
+ostream &operator <<(ostream &out, const SDL_DropEvent &evt) {
+ out << "file: " << evt.file;
+ return out;
+}
+
+}
+}
--- /dev/null
+#ifndef BLOBS_IO_EVENT_HPP_
+#define BLOBS_IO_EVENT_HPP_
+
+#include <iosfwd>
+#include <SDL.h>
+#include <SDL_version.h>
+
+
+namespace blobs {
+namespace io {
+
+std::ostream &operator <<(std::ostream &, const SDL_Event &);
+
+std::ostream &operator <<(std::ostream &, const SDL_WindowEvent &);
+std::ostream &operator <<(std::ostream &, const SDL_KeyboardEvent &);
+std::ostream &operator <<(std::ostream &, const SDL_TextEditingEvent &);
+std::ostream &operator <<(std::ostream &, const SDL_TextInputEvent &);
+std::ostream &operator <<(std::ostream &, const SDL_MouseMotionEvent &);
+std::ostream &operator <<(std::ostream &, const SDL_MouseButtonEvent &);
+std::ostream &operator <<(std::ostream &, const SDL_MouseWheelEvent &);
+std::ostream &operator <<(std::ostream &, const SDL_JoyAxisEvent &);
+std::ostream &operator <<(std::ostream &, const SDL_JoyBallEvent &);
+std::ostream &operator <<(std::ostream &, const SDL_JoyHatEvent &);
+std::ostream &operator <<(std::ostream &, const SDL_JoyButtonEvent &);
+std::ostream &operator <<(std::ostream &, const SDL_JoyDeviceEvent &);
+std::ostream &operator <<(std::ostream &, const SDL_ControllerAxisEvent &);
+std::ostream &operator <<(std::ostream &, const SDL_ControllerButtonEvent &);
+std::ostream &operator <<(std::ostream &, const SDL_ControllerDeviceEvent &);
+#if SDL_VERSION_ATLEAST(2, 0, 4)
+std::ostream &operator <<(std::ostream &, const SDL_AudioDeviceEvent &);
+#endif
+std::ostream &operator <<(std::ostream &, const SDL_QuitEvent &);
+std::ostream &operator <<(std::ostream &, const SDL_UserEvent &);
+std::ostream &operator <<(std::ostream &, const SDL_SysWMEvent &);
+std::ostream &operator <<(std::ostream &, const SDL_TouchFingerEvent &);
+std::ostream &operator <<(std::ostream &, const SDL_MultiGestureEvent &);
+std::ostream &operator <<(std::ostream &, const SDL_DollarGestureEvent &);
+std::ostream &operator <<(std::ostream &, const SDL_DropEvent &);
+
+std::ostream &operator <<(std::ostream &, const SDL_Keysym &);
+
+}
+}
+
+#endif
--- /dev/null
+#include "filesystem.hpp"
+
+#include <cerrno>
+#include <cstdio>
+#include <cstring>
+#ifdef _WIN32
+# include <conio.h>
+# include <direct.h>
+# include <windows.h>
+#else
+# include <dirent.h>
+# include <sys/types.h>
+#endif
+#include <sys/stat.h>
+
+
+namespace blobs {
+namespace io {
+
+namespace {
+#ifdef _WIN32
+ using Stat = struct _stat;
+ int do_stat(const char *path, Stat &info) {
+ return _stat(path, &info);
+ }
+ bool is_dir(const Stat &info) {
+ return (info.st_mode & _S_IFDIR) != 0;
+ }
+ bool is_file(const Stat &info) {
+ return (info.st_mode & _S_IFEG) != 0;
+ }
+#else
+ using Stat = struct stat;
+ int do_stat(const char *path, Stat &info) {
+ return stat(path, &info);
+ }
+ bool is_dir(const Stat &info) {
+ return S_ISDIR(info.st_mode);
+ }
+ bool is_file(const Stat &info) {
+ return S_ISREG(info.st_mode);
+ }
+#endif
+ std::time_t get_mtime(const Stat &info) {
+#ifdef __APPLE__
+ return info.st_mtimespec.tv_sec;
+#else
+ return info.st_mtime;
+#endif
+ }
+}
+
+bool is_dir(const char *path) {
+ Stat info;
+ if (do_stat(path, info) != 0) {
+ return false;
+ }
+ return is_dir(info);
+}
+
+bool is_file(const char *path) {
+ Stat info;
+ if (do_stat(path, info) != 0) {
+ return false;
+ }
+ return is_file(info);
+}
+
+std::time_t file_mtime(const char *path) {
+ Stat info;
+ if (do_stat(path, info) != 0) {
+ return 0;
+ }
+ return get_mtime(info);
+}
+
+
+bool make_dir(const char *path) {
+#ifdef _WIN32
+ int ret = _mkdir(path);
+#else
+ int ret = mkdir(path, 0777);
+#endif
+ return ret == 0;
+}
+
+
+bool make_dirs(const std::string &path) {
+ if (make_dir(path)) {
+ return true;
+ }
+
+ switch (errno) {
+
+ case ENOENT:
+ // missing component
+ {
+#ifdef _WIN32
+ auto pos = path.find_last_of("\\/");
+#else
+ auto pos = path.find_last_of('/');
+#endif
+ if (pos == std::string::npos) {
+ return false;
+ }
+ if (pos == path.length() - 1) {
+ // trailing separator, would make final make_dir fail
+#ifdef _WIN32
+ pos = path.find_last_of("\\/", pos - 1);
+#else
+ pos = path.find_last_of('/', pos - 1);
+#endif
+ if (pos == std::string::npos) {
+ return false;
+ }
+ }
+ if (!make_dirs(path.substr(0, pos))) {
+ return false;
+ }
+ }
+ // try again
+ return make_dir(path);
+
+ case EEXIST:
+ // something's there, check if it's a dir and we're good
+ return is_dir(path);
+
+ default:
+ // whatever else went wrong, it can't be good
+ return false;
+
+ }
+}
+
+
+bool remove_file(const std::string &path) {
+ return remove(path.c_str()) == 0;
+}
+
+
+bool remove_dir(const std::string &path) {
+#ifdef _WIN32
+
+ // shamelessly stolen from http://www.codeguru.com/forum/showthread.php?t=239271
+ const std::string pattern = path + "\\*.*";
+ WIN32_FIND_DATA info;
+ HANDLE file = FindFirstFile(pattern.c_str(), &info);
+ if (file == INVALID_HANDLE_VALUE) {
+ // already non-existing
+ return true;
+ }
+
+ do {
+ if (
+ strncmp(info.cFileName, ".", 2) == 0 ||
+ strncmp(info.cFileName, "..", 3) == 0
+ ) {
+ continue;
+ }
+ const std::string sub_path = path + '\\' + info.cFileName;
+ if ((info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
+ if (!remove_dir(sub_path)) {
+ return false;
+ }
+ } else {
+ if (!SetFileAttributes(sub_path.c_str(), FILE_ATTRIBUTE_NORMAL)) {
+ return false;
+ }
+ if (!remove_file(sub_path)) {
+ return false;
+ }
+ }
+ } while (FindNextFile(file, &info));
+ FindClose(file);
+
+ DWORD error = GetLastError();
+ if (error != ERROR_NO_MORE_FILES) {
+ return false;
+ }
+ // is this (NORMAL vs DIRECTORY) really correct?
+ if (!SetFileAttributes(path.c_str(), FILE_ATTRIBUTE_NORMAL)) {
+ return false;
+ }
+ return RemoveDirectory(path.c_str());
+
+#else
+
+ DIR *dir = opendir(path.c_str());
+ for (dirent *entry = readdir(dir); entry != nullptr; entry = readdir(dir)) {
+ if (
+ strncmp(entry->d_name, ".", 2) == 0 ||
+ strncmp(entry->d_name, "..", 3) == 0
+ ) {
+ continue;
+ }
+ const std::string sub_path = path + '/' + entry->d_name;
+ if (is_dir(sub_path)) {
+ if (!remove_dir(sub_path)) {
+ return false;
+ }
+ } else {
+ if (!remove_file(sub_path)) {
+ return false;
+ }
+ }
+ }
+ return remove(path.c_str()) == 0;
+
+#endif
+}
+
+}
+}
--- /dev/null
+#ifndef BLOBS_IO_FILESYSTEM_HPP_
+#define BLOBS_IO_FILESYSTEM_HPP_
+
+#include <ctime>
+#include <string>
+
+
+namespace blobs {
+namespace io {
+
+/// check if give path points to an existing directory
+bool is_dir(const char *);
+inline bool is_dir(const std::string &s) {
+ return is_dir(s.c_str());
+}
+/// check if give path points to an existing file
+bool is_file(const char *);
+inline bool is_file(const std::string &s) {
+ return is_file(s.c_str());
+}
+/// get timestamp of last modification
+std::time_t file_mtime(const char *);
+inline std::time_t file_mtime(const std::string &s) {
+ return file_mtime(s.c_str());
+}
+
+/// create given directory
+/// @return true if the directory was created
+/// the directory might already exist, see errno
+bool make_dir(const char *);
+inline bool make_dir(const std::string &s) {
+ return make_dir(s.c_str());
+}
+/// create given directory and all parents
+/// @return true if the directory was created or already exists
+bool make_dirs(const std::string &);
+
+/// remove given file
+/// @return true on success
+bool remove_file(const std::string &);
+/// recursively remove given directory
+/// may leave the directory partially removed on failure
+/// @return true if the directory was completely removed
+bool remove_dir(const std::string &);
+
+}
+}
+
+#endif
--- /dev/null
+#include "Token.hpp"
+#include "Tokenizer.hpp"
+#include "TokenStreamReader.hpp"
+
+#include <cctype>
+#include <istream>
+#include <ostream>
+#include <sstream>
+#include <stdexcept>
+#include <glm/gtc/quaternion.hpp>
+
+using namespace std;
+
+
+namespace blobs {
+namespace io {
+
+ostream &operator <<(ostream &out, Token::Type t) {
+ switch (t) {
+ case Token::ANGLE_BRACKET_OPEN:
+ return out << "ANGLE_BRACKET_OPEN";
+ case Token::ANGLE_BRACKET_CLOSE:
+ return out << "ANGLE_BRACKET_CLOSE";
+ case Token::CHEVRON_OPEN:
+ return out << "CHEVRON_OPEN";
+ case Token::CHEVRON_CLOSE:
+ return out << "CHEVRON_CLOSE";
+ case Token::BRACKET_OPEN:
+ return out << "BRACKET_OPEN";
+ case Token::BRACKET_CLOSE:
+ return out << "BRACKET_CLOSE";
+ case Token::PARENTHESIS_OPEN:
+ return out << "PARENTHESIS_OPEN";
+ case Token::PARENTHESIS_CLOSE:
+ return out << "PARENTHESIS_CLOSE";
+ case Token::COLON:
+ return out << "COLON";
+ case Token::SEMICOLON:
+ return out << "SEMICOLON";
+ case Token::COMMA:
+ return out << "COMMA";
+ case Token::EQUALS:
+ return out << "EQUALS";
+ case Token::NUMBER:
+ return out << "NUMBER";
+ case Token::STRING:
+ return out << "STRING";
+ case Token::IDENTIFIER:
+ return out << "IDENTIFIER";
+ case Token::COMMENT:
+ return out << "COMMENT";
+ default:
+ return out << "UNKNOWN";
+ }
+}
+
+ostream &operator <<(ostream &out, const Token &t) {
+ out << t.type;
+ switch (t.type) {
+ case Token::UNKNOWN:
+ case Token::NUMBER:
+ case Token::STRING:
+ case Token::IDENTIFIER:
+ case Token::COMMENT:
+ return out << '(' << t.value << ')';
+ default:
+ return out;
+ }
+}
+
+Tokenizer::Tokenizer(istream &in)
+: in(in)
+, current() {
+
+}
+
+
+bool Tokenizer::HasMore() {
+ return bool(istream::sentry(in));
+}
+
+const Token &Tokenizer::Next() {
+ ReadToken();
+ return Current();
+}
+
+void Tokenizer::ReadToken() {
+ current.type = Token::UNKNOWN;
+ current.value.clear();
+
+ istream::sentry s(in);
+ if (!s) {
+ throw runtime_error("read past the end of stream");
+ return;
+ }
+
+ istream::char_type c;
+ in.get(c);
+ switch (c) {
+ case '{': case '}':
+ case '<': case '>':
+ case '[': case ']':
+ case '(': case ')':
+ case ';': case ':':
+ case ',': case '=':
+ current.type = Token::Type(c);
+ break;
+ case '+': case '-': case '.':
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ in.putback(c);
+ ReadNumber();
+ break;
+ case '"':
+ ReadString();
+ break;
+ case '#':
+ case '/':
+ in.putback(c);
+ ReadComment();
+ break;
+ default:
+ in.putback(c);
+ ReadIdentifier();
+ break;
+ }
+}
+
+namespace {
+
+bool is_num_char(istream::char_type c) {
+ return isxdigit(c)
+ || c == '.'
+ || c == '-'
+ || c == '+'
+ ;
+}
+
+}
+
+void Tokenizer::ReadNumber() {
+ current.type = Token::NUMBER;
+ istream::char_type c;
+ while (in.get(c)) {
+ if (is_num_char(c)) {
+ current.value += c;
+ } else {
+ in.putback(c);
+ break;
+ }
+ }
+}
+
+void Tokenizer::ReadString() {
+ current.type = Token::STRING;
+ bool escape = false;
+
+ istream::char_type c;
+ while (in.get(c)) {
+ if (escape) {
+ escape = false;
+ switch (c) {
+ case 'n':
+ current.value += '\n';
+ break;
+ case 'r':
+ current.value += '\r';
+ break;
+ case 't':
+ current.value += '\t';
+ break;
+ default:
+ current.value += c;
+ break;
+ }
+ } else if (c == '"') {
+ break;
+ } else if (c == '\\') {
+ escape = true;
+ } else {
+ current.value += c;
+ }
+ }
+}
+
+void Tokenizer::ReadComment() {
+ current.type = Token::COMMENT;
+ istream::char_type c;
+ in.get(c);
+
+ if (c == '#') {
+ while (in.get(c) && c != '\n') {
+ current.value += c;
+ }
+ return;
+ }
+
+ // c is guaranteed to be '/' now
+ if (!in.get(c)) {
+ throw runtime_error("unexpected end of stream");
+ }
+ if (c == '/') {
+ while (in.get(c) && c != '\n') {
+ current.value += c;
+ }
+ return;
+ } else if (c != '*') {
+ throw runtime_error("invalid character after /");
+ }
+
+ while (in.get(c)) {
+ if (c == '*') {
+ istream::char_type c2;
+ if (!in.get(c2)) {
+ throw runtime_error("unexpected end of stream");
+ }
+ if (c2 == '/') {
+ break;
+ } else {
+ current.value += c;
+ current.value += c2;
+ }
+ } else {
+ current.value += c;
+ }
+ }
+}
+
+void Tokenizer::ReadIdentifier() {
+ current.type = Token::IDENTIFIER;
+
+ istream::char_type c;
+ while (in.get(c)) {
+ if (isalnum(c) || c == '_' || c == '.') {
+ current.value += c;
+ } else {
+ in.putback(c);
+ break;
+ }
+ }
+}
+
+
+TokenStreamReader::TokenStreamReader(istream &in)
+: in(in)
+, cached(false) {
+
+}
+
+
+bool TokenStreamReader::HasMore() {
+ if (cached) {
+ return true;
+ }
+ SkipComments();
+ return cached;
+}
+
+const Token &TokenStreamReader::Next() {
+ SkipComments();
+ cached = false;
+ return in.Current();
+}
+
+void TokenStreamReader::SkipComments() {
+ if (cached) {
+ if (in.Current().type == Token::COMMENT) {
+ cached = false;
+ } else {
+ return;
+ }
+ }
+ while (in.HasMore()) {
+ if (in.Next().type != Token::COMMENT) {
+ cached = true;
+ return;
+ }
+ }
+}
+
+const Token &TokenStreamReader::Peek() {
+ if (!cached) {
+ Next();
+ cached = true;
+ }
+ return in.Current();
+}
+
+
+void TokenStreamReader::Assert(Token::Type t) const {
+ if (GetType() != t) {
+ stringstream s;
+ s << "unexpected token in input stream: expected " << t << ", but got " << in.Current();
+ throw runtime_error(s.str());
+ }
+}
+
+Token::Type TokenStreamReader::GetType() const noexcept {
+ return in.Current().type;
+}
+
+const std::string &TokenStreamReader::GetValue() const noexcept {
+ return in.Current().value;
+}
+
+void TokenStreamReader::Skip(Token::Type t) {
+ Next();
+ Assert(t);
+}
+
+
+void TokenStreamReader::ReadBoolean(bool &b) {
+ b = GetBool();
+}
+
+void TokenStreamReader::ReadIdentifier(string &out) {
+ Next();
+ Assert(Token::IDENTIFIER);
+ out = GetValue();
+}
+
+void TokenStreamReader::ReadNumber(float &n) {
+ n = GetFloat();
+}
+
+void TokenStreamReader::ReadNumber(int &n) {
+ n = GetInt();
+}
+
+void TokenStreamReader::ReadNumber(unsigned long &n) {
+ n = GetULong();
+}
+
+void TokenStreamReader::ReadString(string &out) {
+ Next();
+ Assert(Token::STRING);
+ out = GetValue();
+}
+
+
+void TokenStreamReader::ReadVec(glm::vec2 &v) {
+ Skip(Token::BRACKET_OPEN);
+ ReadNumber(v.x);
+ Skip(Token::COMMA);
+ ReadNumber(v.y);
+ Skip(Token::BRACKET_CLOSE);
+}
+
+void TokenStreamReader::ReadVec(glm::vec3 &v) {
+ Skip(Token::BRACKET_OPEN);
+ ReadNumber(v.x);
+ Skip(Token::COMMA);
+ ReadNumber(v.y);
+ Skip(Token::COMMA);
+ ReadNumber(v.z);
+ Skip(Token::BRACKET_CLOSE);
+}
+
+void TokenStreamReader::ReadVec(glm::vec4 &v) {
+ Skip(Token::BRACKET_OPEN);
+ ReadNumber(v.x);
+ Skip(Token::COMMA);
+ ReadNumber(v.y);
+ Skip(Token::COMMA);
+ ReadNumber(v.z);
+ Skip(Token::COMMA);
+ ReadNumber(v.w);
+ Skip(Token::BRACKET_CLOSE);
+}
+
+void TokenStreamReader::ReadVec(glm::ivec2 &v) {
+ Skip(Token::BRACKET_OPEN);
+ ReadNumber(v.x);
+ Skip(Token::COMMA);
+ ReadNumber(v.y);
+ Skip(Token::BRACKET_CLOSE);
+}
+
+void TokenStreamReader::ReadVec(glm::ivec3 &v) {
+ Skip(Token::BRACKET_OPEN);
+ ReadNumber(v.x);
+ Skip(Token::COMMA);
+ ReadNumber(v.y);
+ Skip(Token::COMMA);
+ ReadNumber(v.z);
+ Skip(Token::BRACKET_CLOSE);
+}
+
+void TokenStreamReader::ReadVec(glm::ivec4 &v) {
+ Skip(Token::BRACKET_OPEN);
+ ReadNumber(v.x);
+ Skip(Token::COMMA);
+ ReadNumber(v.y);
+ Skip(Token::COMMA);
+ ReadNumber(v.z);
+ Skip(Token::COMMA);
+ ReadNumber(v.w);
+ Skip(Token::BRACKET_CLOSE);
+}
+
+void TokenStreamReader::ReadQuat(glm::quat &q) {
+ Skip(Token::BRACKET_OPEN);
+ ReadNumber(q.w);
+ Skip(Token::COMMA);
+ ReadNumber(q.x);
+ Skip(Token::COMMA);
+ ReadNumber(q.y);
+ Skip(Token::COMMA);
+ ReadNumber(q.z);
+ Skip(Token::BRACKET_CLOSE);
+}
+
+
+bool TokenStreamReader::GetBool() {
+ Next();
+ return AsBool();
+}
+
+bool TokenStreamReader::AsBool() const {
+ switch (GetType()) {
+ case Token::NUMBER:
+ return AsInt() != 0;
+ case Token::IDENTIFIER:
+ case Token::STRING:
+ if (GetValue() == "true" || GetValue() == "yes" || GetValue() == "on") {
+ return true;
+ } else if (GetValue() == "false" || GetValue() == "no" || GetValue() == "off") {
+ return false;
+ } else {
+ throw runtime_error("unexpected value in input stream: cannot cast " + GetValue() + " to bool");
+ }
+ default:
+ {
+ stringstream s;
+ s << "unexpected token in input stream: cannot cast " << in.Current() << " to bool";
+ throw runtime_error(s.str());
+ }
+ }
+}
+
+float TokenStreamReader::GetFloat() {
+ Next();
+ return AsFloat();
+}
+
+float TokenStreamReader::AsFloat() const {
+ Assert(Token::NUMBER);
+ return stof(GetValue());
+}
+
+int TokenStreamReader::GetInt() {
+ Next();
+ return AsInt();
+}
+
+int TokenStreamReader::AsInt() const {
+ Assert(Token::NUMBER);
+ return stoi(GetValue());
+}
+
+unsigned long TokenStreamReader::GetULong() {
+ Next();
+ return AsULong();
+}
+
+unsigned long TokenStreamReader::AsULong() const {
+ Assert(Token::NUMBER);
+ return stoul(GetValue());
+}
+
+}
+}
--- /dev/null
+#include "EventTest.hpp"
+
+#include "io/event.hpp"
+
+#include <sstream>
+#include <string>
+#include <SDL_syswm.h>
+
+
+CPPUNIT_TEST_SUITE_REGISTRATION(blobs::io::test::EventTest);
+
+using namespace std;
+
+namespace blobs {
+namespace io {
+namespace test {
+
+void EventTest::setUp() {
+
+}
+
+void EventTest::tearDown() {
+
+}
+
+
+namespace {
+
+template<class T>
+string string_cast(const T &val) {
+ stringstream str;
+ str << val;
+ return str.str();
+}
+
+}
+
+#if SDL_VERSION_ATLEAST(2, 0, 4)
+
+void EventTest::testAudioDevice() {
+ SDL_Event event;
+ event.type = SDL_AUDIODEVICEADDED;
+ event.adevice.which = 1;
+ event.adevice.iscapture = false;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL audio device event",
+ string("audio device added: device ID: 1, capture: no"), string_cast(event));
+ event.adevice.which = 2;
+ event.adevice.iscapture = true;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL audio device event",
+ string("audio device added: device ID: 2, capture: yes"), string_cast(event));
+ event.type = SDL_AUDIODEVICEREMOVED;
+ event.adevice.which = 3;
+ event.adevice.iscapture = false;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL audio device event",
+ string("audio device removed: device ID: 3, capture: no"), string_cast(event));
+ event.adevice.which = 4;
+ event.adevice.iscapture = true;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL audio device event",
+ string("audio device removed: device ID: 4, capture: yes"), string_cast(event));
+}
+
+#endif
+
+void EventTest::testController() {
+ SDL_Event event;
+ event.type = SDL_CONTROLLERAXISMOTION;
+ event.caxis.which = 0;
+ event.caxis.axis = 1;
+ event.caxis.value = 16384;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL controller axis event",
+ string("controller axis motion: controller ID: 0, axis ID: 1, value: 0.5"), string_cast(event));
+
+ event.type = SDL_CONTROLLERBUTTONDOWN;
+ event.cbutton.which = 2;
+ event.cbutton.button = 3;
+ event.cbutton.state = SDL_PRESSED;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL controller button event",
+ string("controller button down: controller ID: 2, button ID: 3, state: pressed"), string_cast(event));
+ event.type = SDL_CONTROLLERBUTTONUP;
+ event.cbutton.which = 4;
+ event.cbutton.button = 5;
+ event.cbutton.state = SDL_RELEASED;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL controller button event",
+ string("controller button up: controller ID: 4, button ID: 5, state: released"), string_cast(event));
+
+ event.type = SDL_CONTROLLERDEVICEADDED;
+ event.cdevice.which = 6;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL controller device event",
+ string("controller device added: controller ID: 6"), string_cast(event));
+ event.type = SDL_CONTROLLERDEVICEREMOVED;
+ event.cdevice.which = 7;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL controller device event",
+ string("controller device removed: controller ID: 7"), string_cast(event));
+ event.type = SDL_CONTROLLERDEVICEREMAPPED;
+ event.cdevice.which = 8;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL controller device event",
+ string("controller device remapped: controller ID: 8"), string_cast(event));
+}
+
+void EventTest::testDollar() {
+ SDL_Event event;
+ event.type = SDL_DOLLARGESTURE;
+ event.dgesture.touchId = 0;
+ event.dgesture.gestureId = 1;
+ event.dgesture.numFingers = 2;
+ event.dgesture.error = 3;
+ event.dgesture.x = 4;
+ event.dgesture.y = 5;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL dollar gesture event",
+ string("dollar gesture: device ID: 0, gesture ID: 1, fingers: 2, error: 3, position: 4 5"), string_cast(event));
+
+ event.type = SDL_DOLLARRECORD;
+ event.dgesture.touchId = 6;
+ event.dgesture.gestureId = 7;
+ event.dgesture.numFingers = 8;
+ event.dgesture.error = 9;
+ event.dgesture.x = 10;
+ event.dgesture.y = 11;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL dollar record event",
+ string("dollar record: device ID: 6, gesture ID: 7, fingers: 8, error: 9, position: 10 11"), string_cast(event));
+}
+
+void EventTest::testDrop() {
+ char filename[] = "/dev/random";
+ SDL_Event event;
+ event.type = SDL_DROPFILE;
+ event.drop.file = filename;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL drop file event",
+ string("drop file: file: ") + filename, string_cast(event));
+}
+
+void EventTest::testFinger() {
+ SDL_Event event;
+ event.type = SDL_FINGERMOTION;
+ event.tfinger.touchId = 0;
+ event.tfinger.fingerId = 1;
+ event.tfinger.x = 2;
+ event.tfinger.y = 3;
+ event.tfinger.dx = 4;
+ event.tfinger.dy = 5;
+ event.tfinger.pressure = 6;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL finger motion event",
+ string("finger motion: device ID: 0, finger ID: 1, position: 2 3, delta: 4 5, pressure: 6"), string_cast(event));
+
+ event.type = SDL_FINGERDOWN;
+ event.tfinger.touchId = 7;
+ event.tfinger.fingerId = 8;
+ event.tfinger.x = 9;
+ event.tfinger.y = 10;
+ event.tfinger.dx = 11;
+ event.tfinger.dy = 12;
+ event.tfinger.pressure = 13;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL finger down event",
+ string("finger down: device ID: 7, finger ID: 8, position: 9 10, delta: 11 12, pressure: 13"), string_cast(event));
+
+ event.type = SDL_FINGERUP;
+ event.tfinger.touchId = 14;
+ event.tfinger.fingerId = 15;
+ event.tfinger.x = 16;
+ event.tfinger.y = 17;
+ event.tfinger.dx = 18;
+ event.tfinger.dy = 19;
+ event.tfinger.pressure = 20;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL finger up event",
+ string("finger up: device ID: 14, finger ID: 15, position: 16 17, delta: 18 19, pressure: 20"), string_cast(event));
+}
+
+void EventTest::testKey() {
+ SDL_Event event;
+ event.type = SDL_KEYDOWN;
+ event.key.windowID = 0;
+ event.key.state = SDL_PRESSED;
+ event.key.repeat = 0;
+ event.key.keysym.scancode = SDL_SCANCODE_0;
+ event.key.keysym.sym = SDLK_0;
+ event.key.keysym.mod = KMOD_NONE;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL key down event",
+ string("key down: window ID: 0, state: pressed, repeat: no, keysym: "
+ "scancode: ") + to_string(int(SDL_SCANCODE_0)) + ", sym: "
+ + to_string(int(SDLK_0)) +" (\"0\")", string_cast(event));
+ event.key.windowID = 2;
+ event.key.repeat = 1;
+ event.key.keysym.scancode = SDL_SCANCODE_BACKSPACE;
+ event.key.keysym.sym = SDLK_BACKSPACE;
+ event.key.keysym.mod = KMOD_LCTRL | KMOD_LALT;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL key down event",
+ string("key down: window ID: 2, state: pressed, repeat: yes, keysym: "
+ "scancode: ") + to_string(int(SDL_SCANCODE_BACKSPACE)) + ", sym: "
+ + to_string(int(SDLK_BACKSPACE)) +" (\"Backspace\"), mod: LCTRL LALT", string_cast(event));
+
+ event.type = SDL_KEYUP;
+ event.key.windowID = 1;
+ event.key.state = SDL_RELEASED;
+ event.key.repeat = 0;
+ event.key.keysym.scancode = SDL_SCANCODE_SYSREQ;
+ event.key.keysym.sym = SDLK_SYSREQ;
+ event.key.keysym.mod = KMOD_LSHIFT | KMOD_RALT;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL key up event",
+ string("key up: window ID: 1, state: released, repeat: no, keysym: "
+ "scancode: ") + to_string(int(SDL_SCANCODE_SYSREQ)) + ", sym: "
+ + to_string(int(SDLK_SYSREQ)) +" (\"SysReq\"), mod: LSHIFT RALT", string_cast(event));
+ event.key.windowID = 3;
+ event.key.repeat = 1;
+ event.key.keysym.scancode = SDL_SCANCODE_L;
+ event.key.keysym.sym = SDLK_l;
+ event.key.keysym.mod = KMOD_RSHIFT | KMOD_RCTRL | KMOD_LGUI;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL key up event",
+ string("key up: window ID: 3, state: released, repeat: yes, keysym: "
+ "scancode: ") + to_string(int(SDL_SCANCODE_L)) + ", sym: "
+ + to_string(int(SDLK_l)) +" (\"L\"), mod: RSHIFT RCTRL LSUPER", string_cast(event));
+ event.key.windowID = 4;
+ event.key.repeat = 2;
+ event.key.keysym.scancode = SDL_SCANCODE_VOLUMEUP;
+ event.key.keysym.sym = SDLK_VOLUMEUP;
+ event.key.keysym.mod = KMOD_RGUI | KMOD_NUM | KMOD_CAPS | KMOD_MODE;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL key up event",
+ string("key up: window ID: 4, state: released, repeat: yes, keysym: "
+ "scancode: ") + to_string(int(SDL_SCANCODE_VOLUMEUP)) + ", sym: "
+ + to_string(int(SDLK_VOLUMEUP)) +" (\"VolumeUp\"), mod: RSUPER NUM CAPS ALTGR", string_cast(event));
+}
+
+void EventTest::testJoystick() {
+ SDL_Event event;
+ event.type = SDL_JOYAXISMOTION;
+ event.jaxis.which = 0;
+ event.jaxis.axis = 1;
+ event.jaxis.value = 16384;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL joystick axis motion event",
+ string("joystick axis motion: joystick ID: 0, axis ID: 1, value: 0.5"), string_cast(event));
+
+ event.type = SDL_JOYBALLMOTION;
+ event.jball.which = 2;
+ event.jball.ball = 3;
+ event.jball.xrel = 4;
+ event.jball.yrel = 5;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL joystick ball motion event",
+ string("joystick ball motion: joystick ID: 2, ball ID: 3, delta: 4 5"), string_cast(event));
+
+ event.type = SDL_JOYHATMOTION;
+ event.jhat.which = 6;
+ event.jhat.hat = 7;
+ event.jhat.value = SDL_HAT_LEFTUP;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL joystick hat motion event",
+ string("joystick hat motion: joystick ID: 6, hat ID: 7, value: left up"), string_cast(event));
+ event.jhat.value = SDL_HAT_UP;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL joystick hat motion event",
+ string("joystick hat motion: joystick ID: 6, hat ID: 7, value: up"), string_cast(event));
+ event.jhat.value = SDL_HAT_RIGHTUP;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL joystick hat motion event",
+ string("joystick hat motion: joystick ID: 6, hat ID: 7, value: right up"), string_cast(event));
+ event.jhat.value = SDL_HAT_LEFT;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL joystick hat motion event",
+ string("joystick hat motion: joystick ID: 6, hat ID: 7, value: left"), string_cast(event));
+ event.jhat.value = SDL_HAT_CENTERED;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL joystick hat motion event",
+ string("joystick hat motion: joystick ID: 6, hat ID: 7, value: center"), string_cast(event));
+ event.jhat.value = SDL_HAT_RIGHT;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL joystick hat motion event",
+ string("joystick hat motion: joystick ID: 6, hat ID: 7, value: right"), string_cast(event));
+ event.jhat.value = SDL_HAT_LEFTDOWN;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL joystick hat motion event",
+ string("joystick hat motion: joystick ID: 6, hat ID: 7, value: left down"), string_cast(event));
+ event.jhat.value = SDL_HAT_DOWN;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL joystick hat motion event",
+ string("joystick hat motion: joystick ID: 6, hat ID: 7, value: down"), string_cast(event));
+ event.jhat.value = SDL_HAT_RIGHTDOWN;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL joystick hat motion event",
+ string("joystick hat motion: joystick ID: 6, hat ID: 7, value: right down"), string_cast(event));
+ event.jhat.value = -1;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL joystick hat motion event",
+ string("joystick hat motion: joystick ID: 6, hat ID: 7, value: unknown"), string_cast(event));
+
+ event.type = SDL_JOYBUTTONDOWN;
+ event.jbutton.which = 8;
+ event.jbutton.button = 9;
+ event.jbutton.state = SDL_PRESSED;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL joystick button down event",
+ string("joystick button down: joystick ID: 8, button ID: 9, state: pressed"), string_cast(event));
+ event.type = SDL_JOYBUTTONUP;
+ event.jbutton.which = 10;
+ event.jbutton.button = 11;
+ event.jbutton.state = SDL_RELEASED;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL joystick button up event",
+ string("joystick button up: joystick ID: 10, button ID: 11, state: released"), string_cast(event));
+
+ event.type = SDL_JOYDEVICEADDED;
+ event.jdevice.which = 12;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL joystick device added event",
+ string("joystick device added: joystick ID: 12"), string_cast(event));
+ event.type = SDL_JOYDEVICEREMOVED;
+ event.jdevice.which = 13;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL joystick device removed event",
+ string("joystick device removed: joystick ID: 13"), string_cast(event));
+}
+
+void EventTest::testMouse() {
+ SDL_Event event;
+ event.type = SDL_MOUSEMOTION;
+ event.motion.windowID = 0;
+ event.motion.which = 1;
+ event.motion.x = 2;
+ event.motion.y = 3;
+ event.motion.xrel = 4;
+ event.motion.yrel = 5;
+ event.motion.state = 0;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL mouse motion event",
+ string("mouse motion: window ID: 0, mouse ID: 1, position: 2 3, delta: 4 5"), string_cast(event));
+ event.motion.windowID = 6;
+ event.motion.which = 7;
+ event.motion.x = 8;
+ event.motion.y = 9;
+ event.motion.xrel = 10;
+ event.motion.yrel = 11;
+ event.motion.state = SDL_BUTTON_LMASK | SDL_BUTTON_MMASK | SDL_BUTTON_RMASK;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL mouse motion event",
+ string("mouse motion: window ID: 6, mouse ID: 7, position: 8 9, delta: 10 11, buttons: left middle right"), string_cast(event));
+ event.motion.state = SDL_BUTTON_X1MASK | SDL_BUTTON_X2MASK;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL mouse motion event",
+ string("mouse motion: window ID: 6, mouse ID: 7, position: 8 9, delta: 10 11, buttons: X1 X2"), string_cast(event));
+
+ event.type = SDL_MOUSEBUTTONDOWN;
+ event.button.windowID = 0;
+ event.button.which = 1;
+ event.button.button = SDL_BUTTON_LEFT;
+ event.button.state = SDL_PRESSED;
+ event.button.clicks = 2;
+ event.button.x = 3;
+ event.button.y = 4;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL mouse button down event",
+ string("mouse button down: window ID: 0, mouse ID: 1, button: left, state: pressed, clicks: 2, position: 3 4"), string_cast(event));
+ event.type = SDL_MOUSEBUTTONUP;
+ event.button.windowID = 5;
+ event.button.which = 6;
+ event.button.button = SDL_BUTTON_MIDDLE;
+ event.button.clicks = 7;
+ event.button.x = 8;
+ event.button.y = 9;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL mouse button up event",
+ string("mouse button up: window ID: 5, mouse ID: 6, button: middle, state: pressed, clicks: 7, position: 8 9"), string_cast(event));
+ event.button.button = SDL_BUTTON_RIGHT;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL mouse button up event",
+ string("mouse button up: window ID: 5, mouse ID: 6, button: right, state: pressed, clicks: 7, position: 8 9"), string_cast(event));
+ event.button.button = SDL_BUTTON_X1;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL mouse button up event",
+ string("mouse button up: window ID: 5, mouse ID: 6, button: X1, state: pressed, clicks: 7, position: 8 9"), string_cast(event));
+ event.button.button = SDL_BUTTON_X2;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL mouse button up event",
+ string("mouse button up: window ID: 5, mouse ID: 6, button: X2, state: pressed, clicks: 7, position: 8 9"), string_cast(event));
+ event.button.button = SDL_BUTTON_X2 + 1;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL mouse button up event",
+ string("mouse button up: window ID: 5, mouse ID: 6, button: ") + to_string(int(SDL_BUTTON_X2 + 1)) + ", state: pressed, clicks: 7, position: 8 9", string_cast(event));
+
+ event.type = SDL_MOUSEWHEEL;
+ event.wheel.windowID = 0;
+ event.wheel.which = 1;
+ event.wheel.x = 2;
+ event.wheel.y = 3;
+#if SDL_VERSION_ATLEAST(2, 0, 4)
+ event.wheel.direction = SDL_MOUSEWHEEL_NORMAL;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL mouse wheel event",
+ string("mouse wheel: window ID: 0, mouse ID: 1, delta: 2 3, direction: normal"), string_cast(event));
+ event.wheel.windowID = 4;
+ event.wheel.which = 5;
+ event.wheel.x = 6;
+ event.wheel.y = 7;
+ event.wheel.direction = SDL_MOUSEWHEEL_FLIPPED;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL mouse wheel event",
+ string("mouse wheel: window ID: 4, mouse ID: 5, delta: 6 7, direction: flipped"), string_cast(event));
+#else
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL mouse wheel event",
+ string("mouse wheel: window ID: 0, mouse ID: 1, delta: 2 3"), string_cast(event));
+#endif
+}
+
+void EventTest::testMultiGesture() {
+ SDL_Event event;
+ event.type = SDL_MULTIGESTURE;
+ event.mgesture.touchId = 0;
+ event.mgesture.dTheta = 1;
+ event.mgesture.dDist = 2;
+ event.mgesture.x = 3;
+ event.mgesture.y = 4;
+ event.mgesture.numFingers = 5;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL multi gesture event",
+ string("multi gesture: device ID: 0, theta: 1, distance: 2, position: 3 4, fingers: 5"), string_cast(event));
+}
+
+void EventTest::testQuit() {
+ SDL_Event event;
+ event.type = SDL_QUIT;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL quit event",
+ string("quit: quit"), string_cast(event));
+}
+
+void EventTest::testSysWM() {
+ SDL_Event event;
+ event.type = SDL_SYSWMEVENT;
+ event.syswm.msg = nullptr;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL sys wm event",
+ string("sys wm: without message"), string_cast(event));
+ SDL_SysWMmsg msg;
+ event.syswm.msg = &msg;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL sys wm event",
+ string("sys wm: with message"), string_cast(event));
+}
+
+void EventTest::testText() {
+ SDL_Event event;
+ event.type = SDL_TEXTEDITING;
+ event.edit.windowID = 0;
+ event.edit.text[0] = '\303';
+ event.edit.text[1] = '\244';
+ event.edit.text[2] = '\0';
+ event.edit.start = 1;
+ event.edit.length = 2;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL text editing event",
+ string("text editing: window ID: 0, text: \"ä\", start: 1, length: 2"), string_cast(event));
+
+ event.type = SDL_TEXTINPUT;
+ event.text.windowID = 3;
+ event.text.text[0] = '\0';
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL text input event",
+ string("text input: window ID: 3, text: \"\""), string_cast(event));
+}
+
+void EventTest::testUser() {
+ SDL_Event event;
+ event.type = SDL_USEREVENT;
+ event.user.windowID = 0;
+ event.user.code = 1;
+ event.user.data1 = nullptr;
+ event.user.data2 = reinterpret_cast<void *>(1);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL user event",
+ string("user: window ID: 0, code: 1, data 1: 0, data 2: 0x1"), string_cast(event));
+}
+
+void EventTest::testWindow() {
+ SDL_Event event;
+ event.type = SDL_WINDOWEVENT;
+ event.window.event = SDL_WINDOWEVENT_SHOWN;
+ event.window.windowID = 0;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL window event",
+ string("window: shown, window ID: 0"), string_cast(event));
+
+ event.window.event = SDL_WINDOWEVENT_HIDDEN;
+ event.window.windowID = 1;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL window event",
+ string("window: hidden, window ID: 1"), string_cast(event));
+
+ event.window.event = SDL_WINDOWEVENT_EXPOSED;
+ event.window.windowID = 2;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL window event",
+ string("window: exposed, window ID: 2"), string_cast(event));
+
+ event.window.event = SDL_WINDOWEVENT_MOVED;
+ event.window.windowID = 3;
+ event.window.data1 = 4;
+ event.window.data2 = 5;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL window event",
+ string("window: moved, window ID: 3, position: 4 5"), string_cast(event));
+
+ event.window.event = SDL_WINDOWEVENT_RESIZED;
+ event.window.windowID = 6;
+ event.window.data1 = 7;
+ event.window.data2 = 8;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL window event",
+ string("window: resized, window ID: 6, size: 7x8"), string_cast(event));
+
+ event.window.event = SDL_WINDOWEVENT_SIZE_CHANGED;
+ event.window.windowID = 9;
+ event.window.data1 = 10;
+ event.window.data2 = 11;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL window event",
+ string("window: size changed, window ID: 9, size: 10x11"), string_cast(event));
+
+ event.window.event = SDL_WINDOWEVENT_MINIMIZED;
+ event.window.windowID = 12;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL window event",
+ string("window: minimized, window ID: 12"), string_cast(event));
+
+ event.window.event = SDL_WINDOWEVENT_MAXIMIZED;
+ event.window.windowID = 13;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL window event",
+ string("window: maximized, window ID: 13"), string_cast(event));
+
+ event.window.event = SDL_WINDOWEVENT_RESTORED;
+ event.window.windowID = 14;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL window event",
+ string("window: restored, window ID: 14"), string_cast(event));
+
+ event.window.event = SDL_WINDOWEVENT_ENTER;
+ event.window.windowID = 15;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL window event",
+ string("window: mouse entered, window ID: 15"), string_cast(event));
+
+ event.window.event = SDL_WINDOWEVENT_LEAVE;
+ event.window.windowID = 16;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL window event",
+ string("window: mouse left, window ID: 16"), string_cast(event));
+
+ event.window.event = SDL_WINDOWEVENT_FOCUS_GAINED;
+ event.window.windowID = 17;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL window event",
+ string("window: focus gained, window ID: 17"), string_cast(event));
+
+ event.window.event = SDL_WINDOWEVENT_FOCUS_LOST;
+ event.window.windowID = 18;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL window event",
+ string("window: focus lost, window ID: 18"), string_cast(event));
+
+ event.window.event = SDL_WINDOWEVENT_CLOSE;
+ event.window.windowID = 19;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL window event",
+ string("window: closed, window ID: 19"), string_cast(event));
+
+ event.window.event = SDL_WINDOWEVENT_NONE;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL window event",
+ string("window: unknown"), string_cast(event));
+}
+
+void EventTest::testUnknown() {
+ SDL_Event event;
+ // SDL_LASTEVENT holds the number of entries in the enum and therefore
+ // shouldn't be recognized as any valid event type
+ event.type = SDL_LASTEVENT;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "output format of SDL user event",
+ string("unknown"), string_cast(event));
+}
+
+}
+}
+}
--- /dev/null
+#ifndef BLOBS_TEST_IO_EVENTTEST_HPP
+#define BLOBS_TEST_IO_EVENTTEST_HPP
+
+#include <cppunit/extensions/HelperMacros.h>
+
+#include <SDL_version.h>
+
+namespace blobs {
+namespace io {
+namespace test {
+
+class EventTest
+: public CppUnit::TestFixture {
+
+CPPUNIT_TEST_SUITE(EventTest);
+
+#if SDL_VERSION_ATLEAST(2, 0, 4)
+CPPUNIT_TEST(testAudioDevice);
+#endif
+
+CPPUNIT_TEST(testController);
+CPPUNIT_TEST(testDollar);
+CPPUNIT_TEST(testDrop);
+CPPUNIT_TEST(testFinger);
+CPPUNIT_TEST(testKey);
+CPPUNIT_TEST(testJoystick);
+CPPUNIT_TEST(testMouse);
+CPPUNIT_TEST(testMultiGesture);
+CPPUNIT_TEST(testQuit);
+CPPUNIT_TEST(testSysWM);
+CPPUNIT_TEST(testText);
+CPPUNIT_TEST(testUser);
+CPPUNIT_TEST(testWindow);
+CPPUNIT_TEST(testUnknown);
+
+CPPUNIT_TEST_SUITE_END();
+
+public:
+ void setUp();
+ void tearDown();
+
+#if SDL_VERSION_ATLEAST(2, 0, 4)
+ void testAudioDevice();
+#endif
+
+ void testController();
+ void testDollar();
+ void testDrop();
+ void testFinger();
+ void testKey();
+ void testJoystick();
+ void testMouse();
+ void testMultiGesture();
+ void testQuit();
+ void testSysWM();
+ void testText();
+ void testUser();
+ void testWindow();
+ void testUnknown();
+
+};
+
+}
+}
+}
+
+
+#endif
--- /dev/null
+#include "FilesystemTest.hpp"
+
+#include "io/filesystem.hpp"
+
+#include <algorithm>
+
+CPPUNIT_TEST_SUITE_REGISTRATION(blobs::io::test::FilesystemTest);
+
+using namespace std;
+
+
+namespace blobs {
+namespace io {
+namespace test {
+
+void FilesystemTest::setUp() {
+ test_dir = "test-dir";
+ CPPUNIT_ASSERT_MESSAGE(
+ "failed to create test dir",
+ make_dir(test_dir));
+}
+
+void FilesystemTest::tearDown() {
+ CPPUNIT_ASSERT_MESSAGE(
+ "failed to remove test dir",
+ remove_dir(test_dir));
+}
+
+
+void FilesystemTest::testFile() {
+#ifdef _WIN32
+ const string test_file = test_dir + "\\test-file.txt";
+#else
+ const string test_file = test_dir + "/test-file";
+#endif
+
+ CPPUNIT_ASSERT_MESSAGE(
+ "inexistant file is file",
+ !is_file(test_file));
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "mtime of inexistant file should be zero",
+ time_t(0), file_mtime(test_file));
+ CPPUNIT_ASSERT_MESSAGE(
+ "inexistant file is a directory",
+ !is_dir(test_file));
+
+ { // create file
+ ofstream file(test_file);
+ file << "hello" << endl;
+ }
+ time_t now = time(nullptr);
+ CPPUNIT_ASSERT_MESSAGE(
+ "existing file not a file",
+ is_file(test_file));
+ CPPUNIT_ASSERT_MESSAGE(
+ "mtime of existing file should be somewhere around now",
+ // let's assume that creating the file takes less than five seconds
+ abs(now - file_mtime(test_file) < 5));
+ CPPUNIT_ASSERT_MESSAGE(
+ "regular file is a directory",
+ !is_dir(test_file));
+
+ CPPUNIT_ASSERT_MESSAGE(
+ "failed to remove test file",
+ remove_file(test_file));
+
+ CPPUNIT_ASSERT_MESSAGE(
+ "removed file is still a file",
+ !is_file(test_file));
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "mtime of removed file should be zero",
+ time_t(0), file_mtime(test_file));
+ CPPUNIT_ASSERT_MESSAGE(
+ "removed file became a directory",
+ !is_dir(test_file));
+}
+
+void FilesystemTest::testDirectory() {
+#ifdef _WIN32
+ const string test_subdir = test_dir + "\\a";
+ const string test_subsubdir = test_subdir + "\\b";
+ const string test_file = test_subsubdir + "\\c.txt";
+#else
+ const string test_subdir = test_dir + "/a";
+ const string test_subsubdir = test_subdir + "/b";
+ const string test_file = test_subsubdir + "/c";
+#endif
+
+ CPPUNIT_ASSERT_MESSAGE(
+ "inexistant directory is a file",
+ !is_file(test_subdir));
+ CPPUNIT_ASSERT_MESSAGE(
+ "inexistant directory is a directory",
+ !is_dir(test_subdir));
+
+ CPPUNIT_ASSERT_MESSAGE(
+ "failed to create test subdir",
+ make_dir(test_subdir));
+ CPPUNIT_ASSERT_MESSAGE(
+ "created directory is a file",
+ !is_file(test_subdir));
+ CPPUNIT_ASSERT_MESSAGE(
+ "created directory is not a directory",
+ is_dir(test_subdir));
+
+ CPPUNIT_ASSERT_MESSAGE(
+ "failed to remove test subdir",
+ remove_dir(test_subdir));
+ CPPUNIT_ASSERT_MESSAGE(
+ "removed directory became a file",
+ !is_file(test_subdir));
+ CPPUNIT_ASSERT_MESSAGE(
+ "removed directory is still a directory",
+ !is_dir(test_subdir));
+
+ CPPUNIT_ASSERT_MESSAGE(
+ "failed to create test subdirs",
+ make_dirs(test_subsubdir));
+ CPPUNIT_ASSERT_MESSAGE(
+ "created directory is a file",
+ !is_file(test_subdir));
+ CPPUNIT_ASSERT_MESSAGE(
+ "created directory is not a directory",
+ is_dir(test_subdir));
+ CPPUNIT_ASSERT_MESSAGE(
+ "created directory is a file",
+ !is_file(test_subsubdir));
+ CPPUNIT_ASSERT_MESSAGE(
+ "created directory is not a directory",
+ is_dir(test_subsubdir));
+
+ { // create file
+ ofstream file(test_file);
+ file << "hello" << endl;
+ }
+ CPPUNIT_ASSERT_MESSAGE(
+ "failed to create test file",
+ is_file(test_file));
+
+ CPPUNIT_ASSERT_MESSAGE(
+ "failed to remove test subdir",
+ remove_dir(test_subdir));
+ CPPUNIT_ASSERT_MESSAGE(
+ "removed directory became a file",
+ !is_file(test_subdir));
+ CPPUNIT_ASSERT_MESSAGE(
+ "removed directory is still a directory",
+ !is_dir(test_subdir));
+ CPPUNIT_ASSERT_MESSAGE(
+ "removed directory became a file",
+ !is_file(test_subsubdir));
+ CPPUNIT_ASSERT_MESSAGE(
+ "removed directory is still a directory",
+ !is_dir(test_subsubdir));
+ CPPUNIT_ASSERT_MESSAGE(
+ "removed file became a directory",
+ !is_dir(test_file));
+ CPPUNIT_ASSERT_MESSAGE(
+ "removed file is still a file",
+ !is_file(test_file));
+}
+
+}
+}
+}
--- /dev/null
+#ifndef BLOBS_TEST_IO_FILESYSTEMTEST_HPP
+#define BLOBS_TEST_IO_FILESYSTEMTEST_HPP
+
+#include <string>
+#include <cppunit/extensions/HelperMacros.h>
+
+namespace blobs {
+namespace io {
+namespace test {
+
+class FilesystemTest
+: public CppUnit::TestFixture {
+
+CPPUNIT_TEST_SUITE(FilesystemTest);
+
+CPPUNIT_TEST(testFile);
+CPPUNIT_TEST(testDirectory);
+
+CPPUNIT_TEST_SUITE_END();
+
+public:
+ void setUp();
+ void tearDown();
+
+ void testFile();
+ void testDirectory();
+
+private:
+ std::string test_dir;
+
+};
+
+}
+}
+}
+
+#endif
--- /dev/null
+#include "TokenTest.hpp"
+
+#include "io/TokenStreamReader.hpp"
+
+#include <sstream>
+#include <stdexcept>
+#include <glm/gtx/io.hpp>
+
+CPPUNIT_TEST_SUITE_REGISTRATION(blobs::io::test::TokenTest);
+
+using namespace std;
+
+namespace blobs {
+namespace io {
+namespace test {
+
+void TokenTest::setUp() {
+
+}
+
+void TokenTest::tearDown() {
+
+}
+
+
+void TokenTest::testTypeIO() {
+ AssertStreamOutput(Token::UNKNOWN, "UNKNOWN");
+ AssertStreamOutput(Token::ANGLE_BRACKET_OPEN, "ANGLE_BRACKET_OPEN");
+ AssertStreamOutput(Token::ANGLE_BRACKET_CLOSE, "ANGLE_BRACKET_CLOSE");
+ AssertStreamOutput(Token::CHEVRON_OPEN, "CHEVRON_OPEN");
+ AssertStreamOutput(Token::CHEVRON_CLOSE, "CHEVRON_CLOSE");
+ AssertStreamOutput(Token::BRACKET_OPEN, "BRACKET_OPEN");
+ AssertStreamOutput(Token::BRACKET_CLOSE, "BRACKET_CLOSE");
+ AssertStreamOutput(Token::PARENTHESIS_OPEN, "PARENTHESIS_OPEN");
+ AssertStreamOutput(Token::PARENTHESIS_CLOSE, "PARENTHESIS_CLOSE");
+ AssertStreamOutput(Token::COLON, "COLON");
+ AssertStreamOutput(Token::SEMICOLON, "SEMICOLON");
+ AssertStreamOutput(Token::COMMA, "COMMA");
+ AssertStreamOutput(Token::EQUALS, "EQUALS");
+ AssertStreamOutput(Token::NUMBER, "NUMBER");
+ AssertStreamOutput(Token::STRING, "STRING");
+ AssertStreamOutput(Token::IDENTIFIER, "IDENTIFIER");
+ AssertStreamOutput(Token::COMMENT, "COMMENT");
+}
+
+void TokenTest::testTokenIO() {
+ Token t;
+ t.value = "why oh why";
+ AssertStreamOutput(t, "UNKNOWN(why oh why)");
+ t.type = Token::UNKNOWN;
+ t.value = "do I have no purpose";
+ AssertStreamOutput(t, "UNKNOWN(do I have no purpose)");
+ t.type = Token::ANGLE_BRACKET_OPEN;
+ AssertStreamOutput(t, "ANGLE_BRACKET_OPEN");
+ t.type = Token::ANGLE_BRACKET_CLOSE;
+ AssertStreamOutput(t, "ANGLE_BRACKET_CLOSE");
+ t.type = Token::CHEVRON_OPEN;
+ AssertStreamOutput(t, "CHEVRON_OPEN");
+ t.type = Token::CHEVRON_CLOSE;
+ AssertStreamOutput(t, "CHEVRON_CLOSE");
+ t.type = Token::BRACKET_OPEN;
+ AssertStreamOutput(t, "BRACKET_OPEN");
+ t.type = Token::BRACKET_CLOSE;
+ AssertStreamOutput(t, "BRACKET_CLOSE");
+ t.type = Token::PARENTHESIS_OPEN;
+ AssertStreamOutput(t, "PARENTHESIS_OPEN");
+ t.type = Token::PARENTHESIS_CLOSE;
+ AssertStreamOutput(t, "PARENTHESIS_CLOSE");
+ t.type = Token::COLON;
+ AssertStreamOutput(t, "COLON");
+ t.type = Token::SEMICOLON;
+ AssertStreamOutput(t, "SEMICOLON");
+ t.type = Token::COMMA;
+ AssertStreamOutput(t, "COMMA");
+ t.type = Token::EQUALS;
+ AssertStreamOutput(t, "EQUALS");
+ t.type = Token::NUMBER;
+ t.value = "15";
+ AssertStreamOutput(t, "NUMBER(15)");
+ t.type = Token::STRING;
+ t.value = "hello world";
+ AssertStreamOutput(t, "STRING(hello world)");
+ t.type = Token::IDENTIFIER;
+ t.value = "foo";
+ AssertStreamOutput(t, "IDENTIFIER(foo)");
+ t.type = Token::COMMENT;
+ t.value = "WITHOUT ANY WARRANTY";
+ AssertStreamOutput(t, "COMMENT(WITHOUT ANY WARRANTY)");
+}
+
+void TokenTest::testTokenizer() {
+ stringstream stream;
+ stream << "[{0},<.5>+3=/**\n * test\n */ (-1.5); foo_bar.baz:\"hello\\r\\n\\t\\\"world\\\"\" ] // this line\n#that line";
+ Tokenizer in(stream);
+
+ AssertHasMore(in);
+ Token token(in.Next());
+ AssertToken(token.type, token.value, in.Current());
+ AssertToken(Token::BRACKET_OPEN, token);
+
+ AssertHasMore(in);
+ AssertToken(Token::ANGLE_BRACKET_OPEN, in.Next());
+ AssertHasMore(in);
+ AssertToken(Token::NUMBER, "0", in.Next());
+ AssertHasMore(in);
+ AssertToken(Token::ANGLE_BRACKET_CLOSE, in.Next());
+ AssertHasMore(in);
+ AssertToken(Token::COMMA, in.Next());
+ AssertHasMore(in);
+ AssertToken(Token::CHEVRON_OPEN, in.Next());
+ AssertHasMore(in);
+ AssertToken(Token::NUMBER, ".5", in.Next());
+ AssertHasMore(in);
+ AssertToken(Token::CHEVRON_CLOSE, in.Next());
+ AssertHasMore(in);
+ AssertToken(Token::NUMBER, "+3", in.Next());
+ AssertHasMore(in);
+ AssertToken(Token::EQUALS, in.Next());
+ AssertHasMore(in);
+ AssertToken(Token::COMMENT, "*\n * test\n ", in.Next());
+ AssertHasMore(in);
+ AssertToken(Token::PARENTHESIS_OPEN, in.Next());
+ AssertHasMore(in);
+ AssertToken(Token::NUMBER, "-1.5", in.Next());
+ AssertHasMore(in);
+ AssertToken(Token::PARENTHESIS_CLOSE, in.Next());
+ AssertHasMore(in);
+ AssertToken(Token::SEMICOLON, in.Next());
+ AssertHasMore(in);
+ AssertToken(Token::IDENTIFIER, "foo_bar.baz", in.Next());
+ AssertHasMore(in);
+ AssertToken(Token::COLON, in.Next());
+ AssertHasMore(in);
+ AssertToken(Token::STRING, "hello\r\n\t\"world\"", in.Next());
+ AssertHasMore(in);
+ AssertToken(Token::BRACKET_CLOSE, in.Next());
+ AssertHasMore(in);
+ AssertToken(Token::COMMENT, " this line", in.Next());
+ AssertHasMore(in);
+ AssertToken(Token::COMMENT, "that line", in.Next());
+ CPPUNIT_ASSERT_MESSAGE("expected end of stream", !in.HasMore());
+ CPPUNIT_ASSERT_THROW_MESSAGE(
+ "extracting token after EOS",
+ in.Next(), std::runtime_error);
+}
+
+void TokenTest::testTokenizerBrokenComment() {
+ {
+ stringstream stream;
+ stream << "/* just one more thing…*";
+ Tokenizer in(stream);
+ AssertHasMore(in);
+ CPPUNIT_ASSERT_THROW_MESSAGE(
+ "half-closed comment should throw",
+ in.Next(), std::runtime_error);
+ }
+ {
+ stringstream stream;
+ stream << " /";
+ Tokenizer in(stream);
+ AssertHasMore(in);
+ CPPUNIT_ASSERT_THROW_MESSAGE(
+ "sole '/' at end of stream should throw",
+ in.Next(), std::runtime_error);
+ }
+ {
+ stringstream stream;
+ stream << "/.";
+ Tokenizer in(stream);
+ AssertHasMore(in);
+ CPPUNIT_ASSERT_THROW_MESSAGE(
+ "'/' followed by garbage should throw",
+ in.Next(), std::runtime_error);
+ }
+}
+
+
+namespace {
+
+template<class T>
+void assert_read(std::string message, T expected, T actual, TokenStreamReader &in) {
+ stringstream msg;
+ msg << message << ", current token: " << in.Peek();
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ msg.str(),
+ expected, actual);
+}
+
+}
+
+void TokenTest::testReader() {
+ stringstream ss;
+ ss <<
+ "/* booleans */\n"
+ "true false yes no on off\n"
+ "\"true\" \"false\" \"yes\" \"no\" \"on\" \"off\"\n"
+ "1 0 -1\n"
+ "# identifiers\n"
+ "foo foo_bar vec.y\n"
+ "// numbers\n"
+ "0 1 +2 -3 4.5\n"
+ ".5 1.5 0.25 -1.75 0.625\n"
+ "0 1 -1 2.5\n"
+ // strings
+ "\"hello\" \"\" \"\\r\\n\\t\\\"\"\n"
+ // vectors
+ "[1,0] [ 0.707, 0.707 ] // vec2\n"
+ "[.577,.577 ,0.577] [ 1,-2,3] // vec3\n"
+ "[ 0, 0, 0, 1 ] [1,0,0,-1.0] // vec4\n"
+ "[640, 480] [3, 4, 5] [0, -10, 100, -1000] # ivecs\n"
+ "[ -0.945, 0, -0.326, 0] # quat\n"
+ ;
+ TokenStreamReader in(ss);
+
+ // booleans
+
+ bool value_bool;
+ in.ReadBoolean(value_bool);
+ assert_read("reading boolean true", true, value_bool, in);
+ in.ReadBoolean(value_bool);
+ assert_read("reading boolean false", false, value_bool, in);
+ in.ReadBoolean(value_bool);
+ assert_read("reading boolean yes", true, value_bool, in);
+ in.ReadBoolean(value_bool);
+ assert_read("reading boolean no", false, value_bool, in);
+ in.ReadBoolean(value_bool);
+ assert_read("reading boolean on", true, value_bool, in);
+ in.ReadBoolean(value_bool);
+ assert_read("reading boolean off", false, value_bool, in);
+
+ in.ReadBoolean(value_bool);
+ assert_read("reading boolean \"true\"", true, value_bool, in);
+ in.ReadBoolean(value_bool);
+ assert_read("reading boolean \"false\"", false, value_bool, in);
+ in.ReadBoolean(value_bool);
+ assert_read("reading boolean \"yes\"", true, value_bool, in);
+ in.ReadBoolean(value_bool);
+ assert_read("reading boolean \"no\"", false, value_bool, in);
+ in.ReadBoolean(value_bool);
+ assert_read("reading boolean \"on\"", true, value_bool, in);
+ in.ReadBoolean(value_bool);
+ assert_read("reading boolean \"off\"", false, value_bool, in);
+
+ in.ReadBoolean(value_bool);
+ assert_read("reading boolean 1", true, value_bool, in);
+ in.ReadBoolean(value_bool);
+ assert_read("reading boolean 0", false, value_bool, in);
+ in.ReadBoolean(value_bool);
+ assert_read("reading boolean -1", true, value_bool, in);
+
+ // identifiers
+
+ string value_ident;
+ in.ReadIdentifier(value_ident);
+ assert_read<string>("reading identifier foo", "foo", value_ident, in);
+ in.ReadIdentifier(value_ident);
+ assert_read<string>("reading identifier foo_bar", "foo_bar", value_ident, in);
+ in.ReadIdentifier(value_ident);
+ assert_read<string>("reading identifier vec.y", "vec.y", value_ident, in);
+
+ // numbers
+ int value_int;
+ in.ReadNumber(value_int);
+ assert_read("reading integer 0", 0, value_int, in);
+ in.ReadNumber(value_int);
+ assert_read("reading integer 1", 1, value_int, in);
+ in.ReadNumber(value_int);
+ assert_read("reading integer +2", 2, value_int, in);
+ in.ReadNumber(value_int);
+ assert_read("reading integer -3", -3, value_int, in);
+ in.ReadNumber(value_int);
+ assert_read("reading integer 4.5", 4, value_int, in);
+
+ float value_float;
+ in.ReadNumber(value_float);
+ assert_read("reading float .5", .5f, value_float, in);
+ in.ReadNumber(value_float);
+ assert_read("reading float 1.5", 1.5f, value_float, in);
+ in.ReadNumber(value_float);
+ assert_read("reading float 0.25", .25f, value_float, in);
+ in.ReadNumber(value_float);
+ assert_read("reading float -1.75", -1.75f, value_float, in);
+ in.ReadNumber(value_float);
+ assert_read("reading float 0.625", 0.625f, value_float, in);
+
+ unsigned long value_uint;
+ in.ReadNumber(value_uint);
+ assert_read("reading unsigned integer 0", 0ul, value_uint, in);
+ in.ReadNumber(value_uint);
+ assert_read("reading unsigned integer 1", 1ul, value_uint, in);
+ in.ReadNumber(value_uint);
+ assert_read("reading unsigned integer -1", -1ul, value_uint, in);
+ in.ReadNumber(value_uint);
+ assert_read("reading unsigned integer 2.5", 2ul, value_uint, in);
+
+ // strings
+
+ string value_string;
+ in.ReadString(value_string);
+ assert_read<string>(
+ "reading string \"hello\"",
+ "hello", value_string, in);
+ in.ReadString(value_string);
+ assert_read<string>(
+ "reading string \"\"",
+ "", value_string, in);
+ in.ReadString(value_string);
+ assert_read<string>(
+ "reading string \"\\r\\n\\t\\\"\"",
+ "\r\n\t\"", value_string, in);
+
+ // vectors
+
+ glm::vec2 value_vec2;
+ in.ReadVec(value_vec2);
+ assert_read(
+ "reading vector [1,0]",
+ glm::vec2(1, 0), value_vec2, in);
+ in.ReadVec(value_vec2);
+ assert_read(
+ "reading vector [ 0.707, 0.707 ]",
+ glm::vec2(.707, .707), value_vec2, in);
+
+ glm::vec3 value_vec3;
+ in.ReadVec(value_vec3);
+ assert_read(
+ "reading vector [.577,.577 ,0.577]",
+ glm::vec3(.577, .577, .577), value_vec3, in);
+ in.ReadVec(value_vec3);
+ assert_read(
+ "reading vector [ 1,-2,3]",
+ glm::vec3(1, -2, 3), value_vec3, in);
+
+ glm::vec4 value_vec4;
+ in.ReadVec(value_vec4);
+ assert_read(
+ "reading vector [ 0, 0, 0, 1 ]",
+ glm::vec4(0, 0, 0, 1), value_vec4, in);
+ in.ReadVec(value_vec4);
+ assert_read(
+ "reading vector [1,0,0,-1.0]",
+ glm::vec4(1, 0, 0, -1), value_vec4, in);
+
+ glm::ivec2 value_ivec2;
+ in.ReadVec(value_ivec2);
+ assert_read(
+ "reading integer vector [640, 480]",
+ glm::ivec2(640, 480), value_ivec2, in);
+ glm::ivec3 value_ivec3;
+ in.ReadVec(value_ivec3);
+ assert_read(
+ "reading integer vector [3, 4, 5]",
+ glm::ivec3(3, 4, 5), value_ivec3, in);
+ glm::ivec4 value_ivec4;
+ in.ReadVec(value_ivec4);
+ assert_read(
+ "reading integer vector [0, -10, 100, -1000]",
+ glm::ivec4(0, -10, 100, -1000), value_ivec4, in);
+
+ glm::quat value_quat;
+ in.ReadQuat(value_quat);
+ assert_read(
+ "reading quaternion [ -0.945, 0, -0.326, 0]",
+ glm::quat(-0.945, 0, -0.326, 0), value_quat, in);
+ // TODO: comment at end of stream makes it think there's more?
+ //CPPUNIT_ASSERT_MESSAGE("expected end of stream", !in.HasMore());
+ // TODO: and it even works??
+ //CPPUNIT_ASSERT_THROW_MESSAGE(
+ // "extracting token after EOS",
+ // in.Next(), std::runtime_error);
+}
+
+void TokenTest::testReaderEmpty() {
+ { // zero length stream
+ stringstream ss;
+ ss << "";
+ TokenStreamReader in(ss);
+ CPPUNIT_ASSERT_MESSAGE(
+ "empty stream shouldn't have tokens",
+ !in.HasMore());
+ }
+ { // stream consisting solely of comments
+ stringstream ss;
+ ss <<
+ "/*\n"
+ " * hello\n"
+ " */\n"
+ "#hello\n"
+ "// is there anybody out there\n"
+ ;
+ TokenStreamReader in(ss);
+ CPPUNIT_ASSERT_MESSAGE(
+ "comment stream shouldn't have tokens",
+ !in.HasMore());
+ }
+}
+
+void TokenTest::testReaderMalformed() {
+ {
+ stringstream ss;
+ ss << "a";
+ TokenStreamReader in(ss);
+ CPPUNIT_ASSERT_THROW_MESSAGE(
+ "unexpected token type should throw",
+ in.GetInt(), std::runtime_error);
+ }
+ {
+ stringstream ss;
+ ss << ":";
+ TokenStreamReader in(ss);
+ CPPUNIT_ASSERT_THROW_MESSAGE(
+ "casting ':' to bool should throw",
+ in.GetBool(), std::runtime_error);
+ }
+ {
+ stringstream ss;
+ ss << "hello";
+ TokenStreamReader in(ss);
+ CPPUNIT_ASSERT_THROW_MESSAGE(
+ "casting \"hello\" to bool should throw",
+ in.GetBool(), std::runtime_error);
+ }
+}
+
+
+void TokenTest::AssertStreamOutput(
+ Token::Type t,
+ string expected
+) {
+ stringstream conv;
+ conv << t;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "unexpected std::ostream << Token::Type result",
+ expected, conv.str());
+}
+
+void TokenTest::AssertStreamOutput(
+ const Token &t,
+ string expected
+) {
+ stringstream conv;
+ conv << t;
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "unexpected std::ostream << Token result",
+ expected, conv.str());
+}
+
+void TokenTest::AssertHasMore(Tokenizer &in) {
+ CPPUNIT_ASSERT_MESSAGE("unexpected end of stream", in.HasMore());
+}
+
+void TokenTest::AssertToken(
+ Token::Type expected_type,
+ const Token &actual_token
+) {
+ AssertToken(expected_type, "", actual_token);
+}
+
+void TokenTest::AssertToken(
+ Token::Type expected_type,
+ string expected_value,
+ const Token &actual_token
+) {
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "unexpected token type",
+ expected_type, actual_token.type);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ "unexpected token value",
+ expected_value, actual_token.value);
+}
+
+}
+}
+}
--- /dev/null
+#ifndef BLOBS_TEST_IO_TOKENTEST_HPP
+#define BLOBS_TEST_IO_TOKENTEST_HPP
+
+#include "io/Token.hpp"
+#include "io/Tokenizer.hpp"
+
+#include <string>
+#include <cppunit/extensions/HelperMacros.h>
+
+
+namespace blobs {
+namespace io {
+namespace test {
+
+class TokenTest
+: public CppUnit::TestFixture {
+
+CPPUNIT_TEST_SUITE(TokenTest);
+
+CPPUNIT_TEST(testTypeIO);
+CPPUNIT_TEST(testTokenIO);
+CPPUNIT_TEST(testTokenizer);
+CPPUNIT_TEST(testTokenizerBrokenComment);
+CPPUNIT_TEST(testReader);
+CPPUNIT_TEST(testReaderEmpty);
+CPPUNIT_TEST(testReaderMalformed);
+
+CPPUNIT_TEST_SUITE_END();
+
+public:
+ void setUp();
+ void tearDown();
+
+ void testTypeIO();
+ void testTokenIO();
+ void testTokenizer();
+ void testTokenizerBrokenComment();
+
+ void testReader();
+ void testReaderEmpty();
+ void testReaderMalformed();
+
+ static void AssertStreamOutput(
+ Token::Type, std::string expected);
+ static void AssertStreamOutput(
+ const Token &, std::string expected);
+
+ static void AssertHasMore(Tokenizer &);
+ static void AssertToken(
+ Token::Type expected_type, const Token &actual_token);
+ static void AssertToken(
+ Token::Type expected_type, std::string expected_value,
+ const Token &actual_token);
+
+};
+
+}
+}
+}
+
+#endif
#include "const.hpp"
#include "world/Orbit.hpp"
-CPPUNIT_TEST_SUITE_REGISTRATION(blobs::test::world::OrbitTest);
+CPPUNIT_TEST_SUITE_REGISTRATION(blobs::world::test::OrbitTest);
-using blobs::world::Orbit;
+using blobs::test::AssertEqual;
namespace blobs {
-namespace test {
namespace world {
+namespace test {
void OrbitTest::setUp() {
}
namespace blobs {
-namespace test {
namespace world {
+namespace test {
class OrbitTest
: public CppUnit::TestFixture {