diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6a2be81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,46 @@ +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf + +# macOS files +.DS_Store + +# build folder +/build \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index c52c2c9..3ece431 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,17 +1,29 @@ -cmake_minimum_required(VERSION 3.11) +cmake_minimum_required(VERSION 3.13) +project(TextShareServer) -project(CodeShare) - -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Werror=pedantic") -set(CMAKE_C_STANDARD 99) +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -pthread") include_directories(project/headers) -enable_testing() +set(SOURCE_DIR project/src/) + +set(LIB_SOURCE_FILES + ${SOURCE_DIR}Server.cpp + ${SOURCE_DIR}Connection.cpp + ${SOURCE_DIR}RequestHandler.cpp +) + +add_library(project_lib STATIC ${LIB_SOURCE_FILES}) + +add_definitions(-DBOOST_ERROR_CODE_HEADER_ONLY) +add_executable(TextShareServer ${SOURCE_DIR}main.cpp) + +find_package(Boost 1.72.0 COMPONENTS system) +if(Boost_FOUND) + include_directories(${Boost_INCLUDE_DIRS}) +endif() -add_executable(CodeShare project/src/main.cpp - project/src/ChatRoom.cpp project/headers/ChatRoom.h - project/src/RoomManager.cpp project/headers/RoomManager.h - project/src/MongoConnector.cpp project/headers/MongoConnector.h) +target_link_libraries(TextShareServer project_lib ${Boost_LIBRARIES}) add_subdirectory(project/tests) diff --git a/project/headers/ChatRoom.h b/project/headers/ChatRoom.h deleted file mode 100644 index 92beefb..0000000 --- a/project/headers/ChatRoom.h +++ /dev/null @@ -1,27 +0,0 @@ -// -// Created by ivan on 4/15/20. -// - -#ifndef CODESHARE_CHATROOM_H -#define CODESHARE_CHATROOM_H - -#include - -#include - -class ChatRoom { -public: - ChatRoom(); - std::string getText(); - std::string addUser(); - -private: - std::string sharedText; - bool createDbTask(); - -protected: - std::string roomUUID; - -}; - -#endif //CODESHARE_CHATROOM_H diff --git a/project/headers/Connection.h b/project/headers/Connection.h new file mode 100644 index 0000000..10f35e0 --- /dev/null +++ b/project/headers/Connection.h @@ -0,0 +1,60 @@ +#ifndef PROJECT_INCLUDE_CONNECTION_H_ +#define PROJECT_INCLUDE_CONNECTION_H_ + +template +class RequestHandler; + +class RoomScheduler; + +#include +#include +#include +#include +#include + +#include "json.hpp" +#include "MsgContext.h" +#include "ConnectionManager.h" +#include "RequestHandler.h" + +using boost::asio::ip::tcp; +using json = nlohmann::json; +typedef std::pair symbolId; + +class Connection : public std::enable_shared_from_this { +public: + explicit Connection(tcp::socket socket); + + int getId() const; + + void setRoomId(int id); + + int getRoomId() const; + + void start(int editorId); + + void stop(); + + void deliver(const MsgContext& msg); + +private: + int id; + int roomId; + tcp::socket socket; + MsgContext messageBuffer; + std::queue messageQueue; + std::string messageBody; + std::shared_ptr> requestHandler; + + virtual void readHeader(); + + virtual void readBody(); + + virtual void writeClient(); + + void sendMsg(const std::string& response); + + void sendMsgAll(const std::string& response, const int& clientId); +}; + +#endif // PROJECT_INCLUDE_CONNECTION_H_ diff --git a/project/headers/ConnectionManager.h b/project/headers/ConnectionManager.h new file mode 100644 index 0000000..af40e1f --- /dev/null +++ b/project/headers/ConnectionManager.h @@ -0,0 +1,65 @@ +#ifndef PROJECT_INCLUDE_CONNECTION_MANAGER_H_ +#define PROJECT_INCLUDE_CONNECTION_MANAGER_H_ + +#include +#include + +#include "MsgContext.h" +#include "Connection.h" + +template +class ConnectionManager { +public: + static ConnectionManager& getInstance() { + static ConnectionManager instance; + return instance; + } + + void start(std::shared_ptr connection, int clientId); + + void stop(std::shared_ptr connection); + + void stopAll(); + + void sendMsgAllExceptMe(MsgContext message, int clientId); + + ConnectionManager(ConnectionManager const&) = delete; + + void operator=(ConnectionManager const&) = delete; +private: + ConnectionManager() = default; + std::set> connections; +}; + +template +void ConnectionManager::start(std::shared_ptr connection, int clientId) { + std::cout << "start in ConManager" << std::endl; + connections.insert(connection); + connection->start(clientId); +} + +template +void ConnectionManager::stop(std::shared_ptr connection) { + connection->stop(); + connections.erase(connection); +} + +template +void ConnectionManager::stopAll() { + for (auto connection : connections) { + connection->stop(); + } + connections.clear(); +} + +template +void ConnectionManager::sendMsgAllExceptMe(MsgContext message, int clientId) { + for (const auto& connection: connections) { + if (connection->getId() == clientId) { + continue; + } + connection->deliver(message); + } +} + +#endif // PROJECT_INCLUDE_CONNECTION_MANAGER_H_ diff --git a/project/headers/MongoConnector.h b/project/headers/MongoConnector.h deleted file mode 100644 index a0d6baa..0000000 --- a/project/headers/MongoConnector.h +++ /dev/null @@ -1,28 +0,0 @@ -// -// Created by ivan on 4/15/20. -// - -#ifndef CODESHARE_MONGOCONNECTOR_H -#define CODESHARE_MONGOCONNECTOR_H - -#include - -class MongoConnector { -public: - MongoConnector(std::string db, std::string coll) : dbName(db), collName(coll); - std::string getCurrText(); - bool saveDiff(); - -private: - std::string dbName; - std::string collName; - - mongocxx::instance instance; - mongocxx::client client; - mongocxx::database database; - mongocxx::collection collection; - - bool createTextTable(std::string tableName); -}; - -#endif //CODESHARE_MONGOCONNECTOR_H diff --git a/project/headers/RequestHandler.h b/project/headers/RequestHandler.h new file mode 100644 index 0000000..350e5b5 --- /dev/null +++ b/project/headers/RequestHandler.h @@ -0,0 +1,135 @@ +#ifndef PROJECT_INCLUDE_REQUEST_HANDLER_H_ +#define PROJECT_INCLUDE_REQUEST_HANDLER_H_ + +#include + +#include "JsonUtility.h" +#include "json.hpp" +#include "Symbol.h" +#include "RoomScheduler.h" +#include "Connection.h" + +class Connection; + +template +class RequestHandler : public std::enable_shared_from_this> { +public: + RequestHandler(RoomSchedulerType& roomScheduler); + std::string handleRequest(const std::string &requestType, const json &jsonBuf, int &clientId) const; + std::string newRoom(std::shared_ptr connection); + std::string joinRoom(const json &jsonBuf, std::shared_ptr connection); +private: + RoomSchedulerType& roomScheduler; +}; + +template +RequestHandler::RequestHandler(RoomSchedulerType& roomScheduler) : roomScheduler(roomScheduler) {} + +template +std::string RequestHandler::handleRequest(const std::string &requestType, const json &jsonBuf, int &clientId) const { + if (requestType == "INSERTION_REQUEST") { + Symbol symbol; + int indexEditor; + int roomId; + JsonUtility::fromJsonInsertion(jsonBuf, symbol, indexEditor, roomId); + + std::cout << "symbol received: " << symbol.getLetter() << "," << "ID: " << symbol.getSymbolId().first << "," << symbol.getSymbolId().second << std::endl; + + auto room = roomScheduler.getRoom(roomId); + int newIndex = room->getSymbolIndex(indexEditor, room->getTextMap(), symbol); + + room->insertTextMap(newIndex, symbol); + + json jsonResponse; + JsonUtility::toJsonInsertion(jsonResponse, "INSERTION_RESPONSE", symbol, indexEditor); + const std::string response = jsonResponse.dump(); + return response; + + } else if (requestType == "REMOVAL_REQUEST") { + std::vector symbolsId; + int roomId; + JsonUtility::fromJsonRemovalRange(jsonBuf, symbolsId, roomId); + int newIndex; + + auto room = roomScheduler.getRoom(roomId); + for(const sId& id : symbolsId) { + newIndex = room->getIndexById(room->getTextMap(), id); + if(newIndex != -1) { + room->eraseTextMap(newIndex); + } + } + + json jsonResponse; + JsonUtility::toJsonRemovalRange(jsonResponse, "REMOVAL_RESPONSE", symbolsId); + const std::string response = jsonResponse.dump(); + return response; + + } else if (requestType == "CURSOR_CHANGE_REQUEST") { + int pos; + JsonUtility::fromJsonCursorChangeReq(jsonBuf, pos); + std::cout << "pos received: " << std::to_string(pos) << std::endl; + + json jsonResponse; + JsonUtility::toJsonCursorChange(jsonResponse, "CURSOR_CHANGE_RESPONSE", pos); + const std::string response = jsonResponse.dump(); + return response; + + } else if (requestType == "INSERTIONRANGE_REQUEST") { + std::vector formattingSymbols; + int startIndex; + int roomId; + JsonUtility::fromJsonInsertionRange(jsonBuf, formattingSymbols, startIndex, roomId); + std::vector symbols = JsonUtility::fromJsonToFormattingSym(formattingSymbols); + int newIndex = startIndex; + + auto room = roomScheduler.getRoom(roomId); + for (const Symbol &s : symbols) { + newIndex = room->getSymbolIndex(newIndex, room->getTextMap(), s); + + room->insertTextMap(newIndex, s); + } + + json jsonResponse; + std::vector jsonSymbols = JsonUtility::fromSymToJson(symbols); + JsonUtility::toJsonInsertionRange(jsonResponse, "INSERTIONRANGE_RESPONSE", startIndex, jsonSymbols, roomId); + const std::string response = jsonResponse.dump(); + return response; + + } + + return std::string(); +} + +template +std::string RequestHandler::newRoom(std::shared_ptr connection) { + auto room = roomScheduler.createRoom(); + room->enterRoom(connection); + + int roomId = room->getRoomId(); + json jsonResponse; + JsonUtility::toJsonRoomId(jsonResponse, "CREATE_ROOM_RESPONSE", roomId); + + const std::string response = jsonResponse.dump(); + return response; +} + +template +std::string RequestHandler::joinRoom(const json &jsonBuf, std::shared_ptr connection) { + int roomId; + JsonUtility::fromJsonRoomId(jsonBuf, roomId); + + auto room = roomScheduler.getRoom(roomId); + room->enterRoom(connection); + + std::vector map = room->getTextMap(); + std::vector symbols = JsonUtility::fromSymToJson(map); + + json jsonResponse; + JsonUtility::toJsonInsertionRange(jsonResponse, "INSERTIONRANGE_RESPONSE", 0, symbols, roomId); + + const std::string response = jsonResponse.dump(); + return response; +} + + +#endif // PROJECT_INCLUDE_REQUEST_HANDLER_H_ diff --git a/project/headers/RoomManager.h b/project/headers/RoomManager.h deleted file mode 100644 index c7fd552..0000000 --- a/project/headers/RoomManager.h +++ /dev/null @@ -1,26 +0,0 @@ -// -// Created by ivan on 4/15/20. -// - -#ifndef CODESHARE_ROOMMANAGER_H -#define CODESHARE_ROOMMANAGER_H - -#include -#include - -#include - - -class RoomManager { -public: - RoomManager(); - ChatRoom* createRoom(); - ChatRoom* getRoom(int id); - bool connectToRoom(int id); - void deleteRoom(int id); - -private: - std::vector roomsContainer; -}; - -#endif //CODESHARE_ROOMMANAGER_H diff --git a/project/headers/Server.h b/project/headers/Server.h new file mode 100644 index 0000000..915095c --- /dev/null +++ b/project/headers/Server.h @@ -0,0 +1,22 @@ +#ifndef PROJECT_INCLUDE_SERVER_H_ +#define PROJECT_INCLUDE_SERVER_H_ + +#include +#include + +#include "ConnectionManager.h" + +using boost::asio::ip::tcp; + +class Server { +public: + Server(boost::asio::io_service& io_service, const tcp::endpoint& endpoint); +private: + tcp::acceptor acceptor; + int clientCounter; + + void startAccept(); +}; + + +#endif // PROJECT_INCLUDE_SERVER_H_ diff --git a/project/src/ChatRoom.cpp b/project/src/ChatRoom.cpp deleted file mode 100644 index 4a9a025..0000000 --- a/project/src/ChatRoom.cpp +++ /dev/null @@ -1,4 +0,0 @@ -// -// Created by ivan on 4/15/20. -// - diff --git a/project/src/Connection.cpp b/project/src/Connection.cpp new file mode 100644 index 0000000..fc2e4dc --- /dev/null +++ b/project/src/Connection.cpp @@ -0,0 +1,166 @@ +#include "Connection.h" + +using boost::asio::ip::tcp; +using json = nlohmann::json; + +Connection::Connection(tcp::socket socket) : socket(std::move(socket)) { + RequestHandler handler(RoomScheduler::getInstance()); + requestHandler = std::make_shared>(handler); +} + +int Connection::getId() const { + return this->id; +} + +void Connection::setRoomId(int id) { + this->roomId = id; +} + +int Connection::getRoomId() const { + return this->roomId; +} + +void Connection::start(int clientId) { + this->id = clientId; + readHeader(); +} + +void Connection::stop() { + socket.close(); +} + +void Connection::deliver(const MsgContext &msg) { + bool isEmpty = messageQueue.empty(); + messageQueue.push(msg); + if (isEmpty) { + writeClient(); + } +} + +void Connection::writeClient() { + auto self(shared_from_this()); + boost::asio::async_write(socket, + boost::asio::buffer(messageQueue.front().data(), messageQueue.front().length() + 1), + [this, self](boost::system::error_code ec, std::size_t) { + if (!ec) { + messageQueue.pop(); + if (!messageQueue.empty()) { + writeClient(); + } + } else { + RoomScheduler::getInstance().eraseUserFromRoom(getRoomId(), shared_from_this()); + ConnectionManager::getInstance().stop(shared_from_this()); + } + }); +} + +void Connection::readHeader() { + memset(messageBuffer.getData(), 0, messageBuffer.length() + 1); + auto self(shared_from_this()); + boost::asio::async_read(socket, + boost::asio::buffer(messageBuffer.getData(), HEADER + 1), + [this, self](boost::system::error_code ec, std::size_t) { + if (!ec) { + messageBuffer.decodeHeader(); + readBody(); + } else { + RoomScheduler::getInstance().eraseUserFromRoom(getRoomId(), shared_from_this()); + ConnectionManager::getInstance().stop(shared_from_this()); + } + }); +} + +void Connection::readBody() { + auto self(shared_from_this()); + boost::asio::async_read(socket, + boost::asio::buffer(messageBuffer.getBody() + 1, messageBuffer.getBodyLength()), + [this, self](boost::system::error_code ec, std::size_t) { + + if (!ec) { + messageBuffer.getData()[messageBuffer.length() + 1] = '\0'; + messageBody.append(messageBuffer.body() + 1); + + if (messageBuffer.isThisLastChunk() == '0') { + readHeader(); + return; + } + + std::string requestType; + json jsonBuf; + try { + jsonBuf = json::parse(messageBody); + JsonUtility::fromJsonOperation(jsonBuf, requestType); + + int clientId = shared_from_this()->getId(); + if (requestType == "CREATE_ROOM_REQUEST") { + const std::string response = requestHandler->newRoom(self); + + std::cout << "Sent:" << response << "END" << std::endl; + this->sendMsg(response); + } else if (requestType == "JOIN_ROOM_REQUEST") { + const std::string response = requestHandler->joinRoom(jsonBuf, self); + + std::cout << "Sent:" << response << "END" << std::endl; + this->sendMsg(response); + } else if (requestType == "INSERTION_REQUEST" || requestType == "REMOVAL_REQUEST" || + requestType == "INSERTIONRANGE_REQUEST" || requestType == "CURSOR_CHANGE_REQUEST") { + + const std::string response = requestHandler->handleRequest(requestType, jsonBuf, clientId); + + std::cout << "Sent:" << response << "END" << std::endl; + this->sendMsgAll(response, clientId); + } + + messageBody.clear(); + readHeader(); + + } catch (json::exception &e) { + std::cerr << "MsgContext: " << e.what() << '\n' << "exception id: " << e.id << std::endl; + messageBody.clear(); + readHeader(); + } + } else { + RoomScheduler::getInstance().eraseUserFromRoom(getRoomId(), shared_from_this()); + ConnectionManager::getInstance().stop(shared_from_this()); + } + }); +} + +void Connection::sendMsg(const std::string &response) { + int mod = (response.length() % MAX_CHUNK_SIZE == 0) ? 1 : 0; + int numChanks = (int) ((response.length() / MAX_CHUNK_SIZE) + 1 - mod); + int chunkSize = MAX_CHUNK_SIZE; + char isLastChunk = '0'; + std::string chunkResponse = response; + + for (int i = 0; i < numChanks; i++) { + if (i == numChanks - 1) { + chunkSize = (int) (response.length() % MAX_CHUNK_SIZE); + isLastChunk = '1'; + } + + MsgContext msg = MsgContext::createMessage(std::string(chunkResponse.begin(), chunkResponse.begin() + chunkSize), isLastChunk); + chunkResponse.erase(0, chunkSize); + shared_from_this()->deliver(msg); + } +} + +void Connection::sendMsgAll(const std::string &response, const int &clientId) { + int mod = (response.length() % MAX_CHUNK_SIZE == 0) ? 1 : 0; + int numChanks = (int) ((response.length() / MAX_CHUNK_SIZE) + 1 - mod); + int chunkSize = MAX_CHUNK_SIZE; + char isLastChunk = '0'; + std::string chunkResponse = response; + + for (int i = 0; i < numChanks; i++) { + if (i == numChanks - 1) { + chunkSize = (int) (response.length() % MAX_CHUNK_SIZE); + isLastChunk = '1'; + } + + MsgContext msg = MsgContext::createMessage(std::string(chunkResponse.begin(), chunkResponse.begin() + chunkSize), isLastChunk); + chunkResponse.erase(0, chunkSize); + std::shared_ptr room = RoomScheduler::getInstance().getRoom(getRoomId()); + room->sendMsgAllExceptMe(msg, clientId); + } +} diff --git a/project/src/MongoConnector.cpp b/project/src/MongoConnector.cpp deleted file mode 100644 index 4a9a025..0000000 --- a/project/src/MongoConnector.cpp +++ /dev/null @@ -1,4 +0,0 @@ -// -// Created by ivan on 4/15/20. -// - diff --git a/project/src/RoomManager.cpp b/project/src/RoomManager.cpp deleted file mode 100644 index 4a9a025..0000000 --- a/project/src/RoomManager.cpp +++ /dev/null @@ -1,4 +0,0 @@ -// -// Created by ivan on 4/15/20. -// - diff --git a/project/src/Server.cpp b/project/src/Server.cpp new file mode 100644 index 0000000..35651bd --- /dev/null +++ b/project/src/Server.cpp @@ -0,0 +1,20 @@ +#include "Server.h" + +using boost::asio::ip::tcp; + +Server::Server(boost::asio::io_service& io_service, const tcp::endpoint& endpoint) + : acceptor(io_service, endpoint), clientCounter(0) { + + startAccept(); +} + +void Server::startAccept() { + std::cout << "Waiting for clients..." << std::endl; + acceptor.async_accept([this](boost::system::error_code ec, tcp::socket socket) { + if (!ec) { + std::shared_ptr newConnection = std::make_shared(std::move(socket)); + ConnectionManager::getInstance().start(newConnection, clientCounter++); + startAccept(); + } + }); +} diff --git a/project/src/main.cpp b/project/src/main.cpp index a84fc6e..07a817f 100644 --- a/project/src/main.cpp +++ b/project/src/main.cpp @@ -1,9 +1,10 @@ -// -// Created by ivan on 4/15/20. -// +#include "Server.h" -#include +int main(int argc, char* argv[]) { + boost::asio::io_service io_service; + tcp::endpoint endpoint(tcp::v4(), std::atoi("63506")); + Server server(io_service, endpoint); + io_service.run(); -int main() { - std::cout << "Welcome to CodeShare project!" << std::endl; + return 0; } diff --git a/project/tests/CMakeLists.txt b/project/tests/CMakeLists.txt index 5426f1c..0e31f36 100644 --- a/project/tests/CMakeLists.txt +++ b/project/tests/CMakeLists.txt @@ -1,26 +1,90 @@ cmake_minimum_required(VERSION 3.11) - project(test) -set(CMAKE_CXX_STANDARD 11) -set(CMAKE_C_STANDARD 99) -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -std=c++0x -Wall -ggdb3") - find_package(GTest REQUIRED) find_package(Threads REQUIRED) include_directories("${GTEST_INCLUDE_DIRS}") -add_executable(Tests - ../src/ChatRoom.cpp - ../src/RoomManager.cpp - ../src/MongoConnector.cpp - ) +# The code below is taken on the Internet to find the GMock +# it's the only way I can solve my problem + +function(_gmock_append_debugs _endvar _library) + if(${_library} AND ${_library}_DEBUG) + set(_output optimized ${${_library}} debug ${${_library}_DEBUG}) + else() + set(_output ${${_library}}) + endif() + set(${_endvar} ${_output} PARENT_SCOPE) +endfunction() + +function(_gmock_find_library _name) + find_library(${_name} + NAMES ${ARGN} + HINTS + $ENV{GMOCK_ROOT} + ${GMOCK_ROOT} + PATH_SUFFIXES ${_gmock_libpath_suffixes} + ) + mark_as_advanced(${_name}) +endfunction() + + +if(NOT DEFINED GMOCK_MSVC_SEARCH) + set(GMOCK_MSVC_SEARCH MD) +endif() + +set(_gmock_libpath_suffixes lib) +if(MSVC) + if(GMOCK_MSVC_SEARCH STREQUAL "MD") + list(APPEND _gmock_libpath_suffixes + msvc/gmock-md/Debug + msvc/gmock-md/Release) + elseif(GMOCK_MSVC_SEARCH STREQUAL "MT") + list(APPEND _gmock_libpath_suffixes + msvc/gmock/Debug + msvc/gmock/Release) + endif() +endif() + +find_path(GMOCK_INCLUDE_DIR gmock/gmock.h + HINTS + $ENV{GMOCK_ROOT}/include + ${GMOCK_ROOT}/include +) +mark_as_advanced(GMOCK_INCLUDE_DIR) + +if(MSVC AND GMOCK_MSVC_SEARCH STREQUAL "MD") + # The provided /MD project files for Google Mock add -md suffixes to the + # library names. + _gmock_find_library(GMOCK_LIBRARY gmock-md gmock) + _gmock_find_library(GMOCK_LIBRARY_DEBUG gmock-mdd gmockd) + _gmock_find_library(GMOCK_MAIN_LIBRARY gmock_main-md gmock_main) + _gmock_find_library(GMOCK_MAIN_LIBRARY_DEBUG gmock_main-mdd gmock_maind) +else() + _gmock_find_library(GMOCK_LIBRARY gmock) + _gmock_find_library(GMOCK_LIBRARY_DEBUG gmockd) + _gmock_find_library(GMOCK_MAIN_LIBRARY gmock_main) + _gmock_find_library(GMOCK_MAIN_LIBRARY_DEBUG gmock_maind) +endif() + +FIND_PACKAGE_HANDLE_STANDARD_ARGS(GMock DEFAULT_MSG GMOCK_LIBRARY GMOCK_INCLUDE_DIR GMOCK_MAIN_LIBRARY) + +if(GMOCK_FOUND) + set(GMOCK_INCLUDE_DIRS ${GMOCK_INCLUDE_DIR}) + _gmock_append_debugs(GMOCK_LIBRARIES GMOCK_LIBRARY) + _gmock_append_debugs(GMOCK_MAIN_LIBRARIES GMOCK_MAIN_LIBRARY) + set(GMOCK_BOTH_LIBRARIES ${GMOCK_LIBRARIES} ${GMOCK_MAIN_LIBRARIES}) +endif() + +# end of search code + +add_executable(tests mock_tests.cpp) -set_target_properties(Tests PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}") +set_target_properties(tests PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}") -target_link_libraries(Tests ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} pc_catalog) +target_link_libraries(tests ${GTEST_BOTH_LIBRARIES} ${GMOCK_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) +target_link_libraries(tests project_lib) add_test( NAME BaseTest diff --git a/project/tests/mock_tests.cpp b/project/tests/mock_tests.cpp new file mode 100644 index 0000000..d925ba5 --- /dev/null +++ b/project/tests/mock_tests.cpp @@ -0,0 +1,120 @@ +#include +#include "gtest/gtest.h" +#include "gmock/gmock.h" +#include "boost/asio.hpp" + +#include "RequestHandler.h" +#include "RoomScheduler.h" +#include "ConnectionManager.h" +#include "Connection.h" + +using ::testing::AtLeast; + +class MockRoomScheduler : public RoomScheduler { +public: + static MockRoomScheduler& getInstance() { + static MockRoomScheduler instance; + return instance; + } + MOCK_METHOD0(createRoom, std::shared_ptr()); + MOCK_METHOD1(getRoom, std::shared_ptr(int id)); +}; + +TEST(RequestHandlerTest, handlerRequestCallGetRoom) { + MockRoomScheduler& scheduler = MockRoomScheduler::getInstance(); + EXPECT_CALL(scheduler, getRoom(0)).Times(AtLeast(1)); + + RequestHandler handler(scheduler); + std::string type = "INSERTION_REQUEST"; + json j = json{ + {"operation", "operation"}, + {"id", std::make_pair(0, 0)}, + {"pos", std::vector()}, + {"letter", 0}, + {"indexInEditor", 0}, + {"room_id", 0} + }; + int id = 0; + scheduler.createRoom(); + handler.handleRequest(type, j, id); +} + +TEST(RequestHandlerTest, newRoomCallCreateRoom) { + MockRoomScheduler& scheduler = MockRoomScheduler::getInstance(); + EXPECT_CALL(scheduler, createRoom()).Times(AtLeast(1)); + + RequestHandler handler(scheduler); + boost::asio::io_service io_service; + boost::asio::ip::tcp::socket socket(io_service); + std::shared_ptr connection = std::make_shared(std::move(socket)); + handler.newRoom(connection); +} + +class MockConnection : public Connection { +public: + using Connection::Connection; + MOCK_METHOD1(start, void(int clientId)); + MOCK_METHOD0(stop, void()); + MOCK_METHOD0(readHeader, void()); + MOCK_METHOD0(writeClient, void()); + + void mockStart() { + Connection::start(0); + } + + void mockDeliver() { + MsgContext msg; + Connection::deliver(msg); + } +}; + +TEST(ConnectionManagerTest, StartCallStart) { + boost::asio::io_service io_service; + boost::asio::ip::tcp::socket socket(io_service); + std::shared_ptr connection = std::make_shared(std::move(socket)); + EXPECT_CALL(*connection, start(0)).Times(AtLeast(1)); + + ConnectionManager::getInstance().start(connection, 0); +} + +TEST(ConnectionManagerTest, StopCallStop) { + boost::asio::io_service io_service; + boost::asio::ip::tcp::socket socket(io_service); + std::shared_ptr connection = std::make_shared(std::move(socket)); + EXPECT_CALL(*connection, stop()).Times(AtLeast(1)); + + ConnectionManager::getInstance().stop(connection); +} + +TEST(ConnectionManagerTest, StopAllCallStop) { + boost::asio::io_service io_service; + boost::asio::ip::tcp::socket socket(io_service); + std::shared_ptr connection = std::make_shared(std::move(socket)); + EXPECT_CALL(*connection, stop()).Times(AtLeast(1)); + + ConnectionManager::getInstance().start(connection, 0); + ConnectionManager::getInstance().stopAll(); +} + +TEST(ConnectionTest, StartCallReadHeader) { + boost::asio::io_service io_service; + boost::asio::ip::tcp::socket socket(io_service); + std::shared_ptr connection = std::make_shared(std::move(socket)); + EXPECT_CALL(*connection, readHeader()).Times(AtLeast(1)); + + connection->mockStart(); +} + +TEST(ConnectionTest, DeliverCallWriteClient) { + boost::asio::io_service io_service; + boost::asio::ip::tcp::socket socket(io_service); + std::shared_ptr connection = std::make_shared(std::move(socket)); + EXPECT_CALL(*connection, writeClient()).Times(AtLeast(1)); + + connection->mockDeliver(); +} + +int main(int argc, char** argv) { + ::testing::InitGoogleMock(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file