wsd: test: refactor client connection management

The client connection management had nothing to
do with the WopiTestServer and it was there
for convenience. The issue was that it was
not available to tests that do not need
the custom WopiTestServer and depending
on it would make them unnecessarily
complicated.

This adds a new intermediary class to manage
client connections in tests. The reason
this logic didn't move to UnitWSD or even
UnitBase is simply because the client
connection logic depends on the helpers
namespace and other test-specific code
that isn't available in COOLWSD. In short,
UnitBase and UnitWSD are primarily an
interface that COOLWSD and co depend on
and cannot contain test-specific logic
that isn't linked to coolwsd.

We also move the UnitWebSocket helper class.

Change-Id: I79567774164e137349dc162482529578f150353c
Signed-off-by: Ashod Nakashian <ashod.nakashian@collabora.co.uk>
pull/5351/head
Ashod Nakashian 2022-11-07 20:27:12 -05:00 committed by Ashod Nakashian
parent c25e795bad
commit 5892bcba1e
4 changed files with 122 additions and 101 deletions

View File

@ -338,6 +338,7 @@ noinst_HEADERS = $(wsd_headers) $(shared_headers) $(kit_headers) \
test/testlog.hpp \
test/HttpTestServer.hpp \
test/WOPIUploadConflictCommon.hpp \
test/UnitWSDClient.hpp \
test/helpers.hpp
GIT_BRANCH := $(shell git symbolic-ref --short HEAD)

View File

@ -18,8 +18,6 @@
#include <Poco/Version.h>
#include "Common.hpp"
#include <helpers.hpp>
#include <net/WebSocketSession.hpp>
/// Unit test stub for a server response
class UnitHTTPServerResponse : public Poco::Net::HTTPServerResponse
@ -117,25 +115,4 @@ namespace UnitHTTP
}
}
class UnitWebSocket
{
std::shared_ptr<http::WebSocketSession> _httpSocket;
public:
/// Get a websocket connected for a given URL
UnitWebSocket(const std::shared_ptr<SocketPoll>& socketPoll, const std::string& documentURL)
{
Poco::URI uri(helpers::getTestServerURI());
_httpSocket = helpers::connectLOKit(socketPoll, uri, documentURL, "UnitWebSocket ");
}
/// 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<http::WebSocketSession>& getWebSocket() { return _httpSocket; }
};
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

View File

@ -0,0 +1,118 @@
/* -*- 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 "testlog.hpp"
#include "Unit.hpp"
#include "Util.hpp"
#include <Poco/URI.h>
#include <memory>
#include <string>
#include <vector>
class UnitWebSocket final
{
std::shared_ptr<http::WebSocketSession> _httpSocket;
public:
/// Get a websocket connected for a given URL
UnitWebSocket(const std::shared_ptr<SocketPoll>& socketPoll, const std::string& documentURL)
{
Poco::URI uri(helpers::getTestServerURI());
_httpSocket = helpers::connectLOKit(socketPoll, uri, documentURL, "UnitWebSocket ");
}
/// 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<http::WebSocketSession>& getWebSocket() { return _httpSocket; }
};
/// A WSD unit-test base class with support
/// to manage client connections.
class UnitWSDClient : public UnitWSD
{
public:
UnitWSDClient(const std::string& name)
: UnitWSD(name)
{
}
protected:
const std::string& getWopiSrc() const { return _wopiSrc; }
const std::unique_ptr<UnitWebSocket>& getWs() const { return _wsList.at(0); }
const std::unique_ptr<UnitWebSocket>& 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<UnitWebSocket>& socket = _wsList.at(index);
socket.reset();
}
void initWebsocket(const std::string& wopiName)
{
Poco::URI wopiURL(helpers::getTestServerURI() + wopiName + "&testname=" + getTestname());
_wopiSrc.clear();
Poco::URI::encode(wopiURL.toString(), ":/?", _wopiSrc);
// 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(),
Util::make_unique<UnitWebSocket>(socketPoll(), "/cool/" + _wopiSrc + "/ws"));
assert((*_ws).get());
}
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(),
Util::make_unique<UnitWebSocket>(socketPoll(), "/cool/" + _wopiSrc + "/ws"));
assert((*_ws).get());
}
private:
/// The WOPISrc URL.
std::string _wopiSrc;
/// Websockets to communicate.
std::vector<std::unique_ptr<UnitWebSocket>> _wsList;
};
/// Send a command message to WSD from a WopiTestServer on the given connection.
#define WSD_CMD_BY_CONNECTION_INDEX(INDEX, MSG) \
do \
{ \
LOG_TST("Sending from #" << INDEX << ": " << MSG); \
helpers::sendTextFrame(getWsAt(INDEX)->getWebSocket(), MSG, getTestname()); \
SocketPoll::wakeupWorld(); \
} while (false)
/// Send a command message to WSD from a WopiTestServer on the primary connection.
#define WSD_CMD(MSG) WSD_CMD_BY_CONNECTION_INDEX(0, MSG)
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

View File

@ -11,8 +11,8 @@
#include "helpers.hpp"
#include "Log.hpp"
#include "Unit.hpp"
#include "UnitHTTP.hpp"
#include "Util.hpp"
#include "UnitWSDClient.hpp"
#include <Poco/JSON/Object.h>
#include <Poco/URI.h>
@ -35,7 +35,7 @@ class HTTPRequest;
/// Furthermore, the file URI doesn't contain the
/// real filename (in most tests), instead filenames
/// 1 to 9 are considered special.
class WopiTestServer : public UnitWSD
class WopiTestServer : public UnitWSDClient
{
private:
@ -44,15 +44,6 @@ private:
DocChanged = 1010
};
/// The WOPISrc URL.
std::string _wopiSrc;
/// The SocketPoll thread.
std::shared_ptr<SocketPoll> _socketPoll;
/// Websockets to communicate.
std::vector< std::unique_ptr<UnitWebSocket> > _wsList;
/// Content of the file.
std::string _fileContent;
@ -76,18 +67,6 @@ private:
protected:
const std::string& getWopiSrc() const { return _wopiSrc; }
const std::unique_ptr<UnitWebSocket>& getWs() const { return _wsList.at(0); }
const std::unique_ptr<UnitWebSocket>& getWsAt(int index) { return _wsList.at(index); }
void deleteSocketAt(int index)
{
std::unique_ptr<UnitWebSocket>& socket = _wsList.at(index);
socket.reset();
}
const std::string& getFileContent() const { return _fileContent; }
/// Sets the file content to a given value and update the last file modified time
@ -104,8 +83,7 @@ protected:
}
WopiTestServer(const std::string& name, const std::string& filenameOrContents = "Hello, world")
: UnitWSD(name)
, _socketPoll(std::make_shared<SocketPoll>(name + "ServerPoll"))
: UnitWSDClient(name)
, _filename(DefaultFilename)
, _countCheckFileInfo(0)
, _countGetFile(0)
@ -114,8 +92,6 @@ protected:
{
LOG_TST("WopiTestServer created for [" << getTestname() << ']');
_socketPoll->startThread();
// Read the document data and store as string in memory.
const auto data = helpers::readDataFromFile(filenameOrContents);
if (!data.empty())
@ -144,45 +120,6 @@ protected:
std::size_t getCountPutFile() const { return _countPutFile; }
void resetCountPutFile() { _countPutFile = 0; }
void initWebsocket(const std::string& wopiName)
{
Poco::URI wopiURL(helpers::getTestServerURI() + wopiName + "&testname=" + getTestname());
_wopiSrc.clear();
Poco::URI::encode(wopiURL.toString(), ":/?", _wopiSrc);
// This is just a client connection that is used from the tests.
// It really has nothing to do with this fake WOPI server, exept
// that it manages it since it is the base of WOPI tests, so
// it's a common bit of housekeeping that all WOPI tests must do.
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(),
Util::make_unique<UnitWebSocket>(_socketPoll, "/cool/" + _wopiSrc + "/ws"));
assert((*_ws).get());
}
void addWebSocket()
{
// This is just a client connection that is used from the tests.
// It really has nothing to do with this fake WOPI server, exept
// that it manages it since it is the base of WOPI tests, so
// it's a common bit of housekeeping that all WOPI tests must do.
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(),
Util::make_unique<UnitWebSocket>(_socketPoll, "/cool/" + _wopiSrc + "/ws"));
assert((*_ws).get());
}
virtual void assertCheckFileInfoRequest(const Poco::Net::HTTPRequest& /*request*/)
{
}
@ -511,16 +448,4 @@ protected:
};
/// Send a command message to WSD from a WopiTestServer on the given connection.
#define WSD_CMD_BY_CONNECTION_INDEX(INDEX, MSG) \
do \
{ \
LOG_TST("Sending from #" << INDEX << ": " << MSG); \
helpers::sendTextFrame(getWsAt(INDEX)->getWebSocket(), MSG, getTestname()); \
SocketPoll::wakeupWorld(); \
} while (false)
/// Send a command message to WSD from a WopiTestServer on the primary connection.
#define WSD_CMD(MSG) WSD_CMD_BY_CONNECTION_INDEX(0, MSG)
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */