From: Daniel Karbach Date: Mon, 13 Nov 2017 19:55:16 +0000 (+0100) Subject: more stuff from blank X-Git-Url: http://git.localhorst.tv/?p=blobs.git;a=commitdiff_plain;h=34e833025f0616ab4b86b808d0ce1cc49cecce5d more stuff from blank --- diff --git a/src/io/Token.hpp b/src/io/Token.hpp new file mode 100644 index 0000000..0c94683 --- /dev/null +++ b/src/io/Token.hpp @@ -0,0 +1,40 @@ +#ifndef BLOBS_IO_TOKEN_HPP_ +#define BLOBS_IO_TOKEN_HPP_ + +#include +#include + + +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 diff --git a/src/io/TokenStreamReader.hpp b/src/io/TokenStreamReader.hpp new file mode 100644 index 0000000..e2945c4 --- /dev/null +++ b/src/io/TokenStreamReader.hpp @@ -0,0 +1,71 @@ +#ifndef BLOBS_IO_TOKENSTREAMREADER_HPP_ +#define BLOBS_IO_TOKENSTREAMREADER_HPP_ + +#include "Token.hpp" +#include "Tokenizer.hpp" +#include "../graphics/glm.hpp" + +#include +#include + + +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 diff --git a/src/io/Tokenizer.hpp b/src/io/Tokenizer.hpp new file mode 100644 index 0000000..c717780 --- /dev/null +++ b/src/io/Tokenizer.hpp @@ -0,0 +1,39 @@ +#ifndef BLOBS_IO_TOKENIZER_HPP_ +#define BLOBS_IO_TOKENIZER_HPP_ + +#include "Token.hpp" + +#include + + +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 diff --git a/src/io/event.cpp b/src/io/event.cpp new file mode 100644 index 0000000..e7ee526 --- /dev/null +++ b/src/io/event.cpp @@ -0,0 +1,458 @@ +#include "event.hpp" + +#include +#include + +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; +} + +} +} diff --git a/src/io/event.hpp b/src/io/event.hpp new file mode 100644 index 0000000..187cfa2 --- /dev/null +++ b/src/io/event.hpp @@ -0,0 +1,45 @@ +#ifndef BLOBS_IO_EVENT_HPP_ +#define BLOBS_IO_EVENT_HPP_ + +#include +#include +#include + + +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 diff --git a/src/io/filesystem.cpp b/src/io/filesystem.cpp new file mode 100644 index 0000000..feb1cac --- /dev/null +++ b/src/io/filesystem.cpp @@ -0,0 +1,213 @@ +#include "filesystem.hpp" + +#include +#include +#include +#ifdef _WIN32 +# include +# include +# include +#else +# include +# include +#endif +#include + + +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 +} + +} +} diff --git a/src/io/filesystem.hpp b/src/io/filesystem.hpp new file mode 100644 index 0000000..154b812 --- /dev/null +++ b/src/io/filesystem.hpp @@ -0,0 +1,49 @@ +#ifndef BLOBS_IO_FILESYSTEM_HPP_ +#define BLOBS_IO_FILESYSTEM_HPP_ + +#include +#include + + +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 diff --git a/src/io/token.cpp b/src/io/token.cpp new file mode 100644 index 0000000..c15d65a --- /dev/null +++ b/src/io/token.cpp @@ -0,0 +1,472 @@ +#include "Token.hpp" +#include "Tokenizer.hpp" +#include "TokenStreamReader.hpp" + +#include +#include +#include +#include +#include +#include + +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()); +} + +} +} diff --git a/tst/io/EventTest.cpp b/tst/io/EventTest.cpp new file mode 100644 index 0000000..e0becd1 --- /dev/null +++ b/tst/io/EventTest.cpp @@ -0,0 +1,604 @@ +#include "EventTest.hpp" + +#include "io/event.hpp" + +#include +#include +#include + + +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 +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(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)); +} + +} +} +} diff --git a/tst/io/EventTest.hpp b/tst/io/EventTest.hpp new file mode 100644 index 0000000..7849063 --- /dev/null +++ b/tst/io/EventTest.hpp @@ -0,0 +1,68 @@ +#ifndef BLOBS_TEST_IO_EVENTTEST_HPP +#define BLOBS_TEST_IO_EVENTTEST_HPP + +#include + +#include + +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 diff --git a/tst/io/FilesystemTest.cpp b/tst/io/FilesystemTest.cpp new file mode 100644 index 0000000..a098c2b --- /dev/null +++ b/tst/io/FilesystemTest.cpp @@ -0,0 +1,165 @@ +#include "FilesystemTest.hpp" + +#include "io/filesystem.hpp" + +#include + +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)); +} + +} +} +} diff --git a/tst/io/FilesystemTest.hpp b/tst/io/FilesystemTest.hpp new file mode 100644 index 0000000..65e203a --- /dev/null +++ b/tst/io/FilesystemTest.hpp @@ -0,0 +1,37 @@ +#ifndef BLOBS_TEST_IO_FILESYSTEMTEST_HPP +#define BLOBS_TEST_IO_FILESYSTEMTEST_HPP + +#include +#include + +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 diff --git a/tst/io/TokenTest.cpp b/tst/io/TokenTest.cpp new file mode 100644 index 0000000..1a38b1e --- /dev/null +++ b/tst/io/TokenTest.cpp @@ -0,0 +1,474 @@ +#include "TokenTest.hpp" + +#include "io/TokenStreamReader.hpp" + +#include +#include +#include + +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 +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("reading identifier foo", "foo", value_ident, in); + in.ReadIdentifier(value_ident); + assert_read("reading identifier foo_bar", "foo_bar", value_ident, in); + in.ReadIdentifier(value_ident); + assert_read("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( + "reading string \"hello\"", + "hello", value_string, in); + in.ReadString(value_string); + assert_read( + "reading string \"\"", + "", value_string, in); + in.ReadString(value_string); + assert_read( + "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); +} + +} +} +} diff --git a/tst/io/TokenTest.hpp b/tst/io/TokenTest.hpp new file mode 100644 index 0000000..3f50050 --- /dev/null +++ b/tst/io/TokenTest.hpp @@ -0,0 +1,61 @@ +#ifndef BLOBS_TEST_IO_TOKENTEST_HPP +#define BLOBS_TEST_IO_TOKENTEST_HPP + +#include "io/Token.hpp" +#include "io/Tokenizer.hpp" + +#include +#include + + +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 diff --git a/tst/world/OrbitTest.cpp b/tst/world/OrbitTest.cpp index 175da83..ce16909 100644 --- a/tst/world/OrbitTest.cpp +++ b/tst/world/OrbitTest.cpp @@ -5,14 +5,14 @@ #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() { } diff --git a/tst/world/OrbitTest.hpp b/tst/world/OrbitTest.hpp index f9c5238..5879050 100644 --- a/tst/world/OrbitTest.hpp +++ b/tst/world/OrbitTest.hpp @@ -5,8 +5,8 @@ namespace blobs { -namespace test { namespace world { +namespace test { class OrbitTest : public CppUnit::TestFixture {