/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ /* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "helpers.hpp" #include "lokassert.hpp" #include "testlog.hpp" #include "Unit.hpp" #include "Util.hpp" #include #include #include #include /// Send a command message to WSD from a UnitWSDClient instance on the given connection. #define WSD_CMD_BY_CONNECTION_INDEX(INDEX, MSG) \ do \ { \ LOG_TST("Sending from #" << INDEX << ": " << MSG); \ sendCommand(INDEX, MSG); \ } while (false) /// Send a command message to WSD from a UnitWSDClient instance on the primary connection. #define WSD_CMD(MSG) WSD_CMD_BY_CONNECTION_INDEX(0, MSG) /// A WebSocketSession wrapper to help with testing. class UnitWebSocket final { std::shared_ptr _httpSocket; public: /// Get a websocket connected for a given URL. UnitWebSocket(const std::shared_ptr& socketPoll, const std::string& documentURL, const std::string& testname = "UnitWebSocket ") { Poco::URI uri(helpers::getTestServerURI()); _httpSocket = helpers::connectLOKit(socketPoll, uri, documentURL, testname); } /// Destroy the WS. /// Here, we can't do IO as we don't own the socket (SocketPoll does). /// In fact, we can't destroy it (it's referenced by SocketPoll). /// Instead, we can only flag for shutting down. ~UnitWebSocket() { _httpSocket->asyncShutdown(); } const std::shared_ptr& getWebSocket() { return _httpSocket; } }; /// A WSD unit-test base class with support /// to manage client connections. /// This cannot be in UnitWSD or UnitBase because /// we use test code that isn't availabe in COOLWSD. class UnitWSDClient : public UnitWSD { public: UnitWSDClient(const std::string& name) : UnitWSD(name) { } protected: const std::string& getWopiSrc() const { return _wopiSrc; } const std::unique_ptr& getWs() const { return _wsList.at(0); } const std::unique_ptr& getWsAt(int index) { return _wsList.at(index); } void deleteSocketAt(int index) { // Don't remove from the container, because the // indexes are how the test refers to them. std::unique_ptr& socket = _wsList.at(index); socket.reset(); } std::string initWebsocket(const std::string& wopiName) { const Poco::URI wopiURL(helpers::getTestServerURI() + wopiName + "&testname=" + getTestname()); _wopiSrc = Util::encodeURIComponent(wopiURL.toString()); // This is just a client connection that is used from the tests. LOG_TST("Connecting test client to COOL (#" << (_wsList.size() + 1) << " connection): /cool/" << _wopiSrc << "/ws"); // Insert at the front. const auto& _ws = _wsList.emplace( _wsList.begin(), std::make_unique( socketPoll(), "/cool/" + _wopiSrc + "/ws", getTestname())); assert((*_ws).get()); return _wopiSrc; } std::string addWebSocket(const std::string& wopiName) { const Poco::URI wopiURL(helpers::getTestServerURI() + wopiName + "&testname=" + getTestname()); std::string wopiSrc = Util::encodeURIComponent(wopiURL.toString()); // This is just a client connection that is used from the tests. LOG_TST("Connecting test client to COOL (#" << (_wsList.size() + 1) << " connection): /cool/" << wopiSrc << "/ws"); // Insert at the back. const auto& _ws = _wsList.emplace( _wsList.end(), std::make_unique(socketPoll(), "/cool/" + wopiSrc + "/ws", getTestname())); assert((*_ws).get()); return wopiSrc; } void addWebSocket() { // This is just a client connection that is used from the tests. LOG_TST("Connecting test client to COOL (#" << (_wsList.size() + 1) << " connection): /cool/" << _wopiSrc << "/ws"); // Insert at the back. const auto& _ws = _wsList.emplace( _wsList.end(), std::make_unique( socketPoll(), "/cool/" + _wopiSrc + "/ws", getTestname())); assert((*_ws).get()); } void endTest(const std::string& reason) override { LOG_TST("Ending test by disconnecting " << _wsList.size() << " connection(s): " << reason); _wsList.clear(); UnitWSD::endTest(reason); } /// Send a command to WSD. void sendCommand(int index, const std::string& msg) { LOK_ASSERT_SILENT(index >= 0 && static_cast(index) < _wsList.size()); helpers::sendTextFrame(getWsAt(index)->getWebSocket(), msg, getTestname()); SocketPoll::wakeupWorld(); } /// Connect to a local test document (not a fake wopi URL), without loading it. /// Returns the document URL to use for loading. std::string connectToLocalDocument(const std::string& docFilename) { std::string documentPath, documentURL; helpers::getDocumentPathAndURL(docFilename, documentPath, documentURL, getTestname()); LOG_TST("Connecting to local document [" << docFilename << "] with URL: " << documentURL); _wsList.emplace_back( std::make_unique(socketPoll(), documentURL, getTestname())); return documentURL; } /// Connect and load a local test document (not a fake wopi document). void connectAndLoadLocalDocument(const std::string& docFilename) { const std::string documentURL = connectToLocalDocument(docFilename); LOG_TST("Loading local document [" << docFilename << "] with URL: " << documentURL); WSD_CMD("load url=" + documentURL); } private: /// The WOPISrc URL. std::string _wopiSrc; /// Websockets to communicate. std::vector> _wsList; }; /* vim:set shiftwidth=4 softtabstop=4 expandtab: */