diff --git a/internal/frontend/bridge-gui/bridgepp/CMakeLists.txt b/internal/frontend/bridge-gui/bridgepp/CMakeLists.txt index 9c5eb38f..fafd1c25 100644 --- a/internal/frontend/bridge-gui/bridgepp/CMakeLists.txt +++ b/internal/frontend/bridge-gui/bridgepp/CMakeLists.txt @@ -186,9 +186,9 @@ enable_testing() # Tests #***************************************************************************************************************************************************** add_executable(bridgepp-test - Test/Exception/TestBridgeUtils.cpp - Test/Exception/TestException.cpp - ) + Test/TestBridgeUtils.cpp + Test/TestException.cpp + Test/TestWorker.cpp Test/TestWorker.h) add_dependencies(bridgepp-test bridgepp) target_precompile_headers(bridgepp-test PRIVATE Pch.h) target_link_libraries(bridgepp-test diff --git a/internal/frontend/bridge-gui/bridgepp/Test/Exception/TestBridgeUtils.cpp b/internal/frontend/bridge-gui/bridgepp/Test/TestBridgeUtils.cpp similarity index 100% rename from internal/frontend/bridge-gui/bridgepp/Test/Exception/TestBridgeUtils.cpp rename to internal/frontend/bridge-gui/bridgepp/Test/TestBridgeUtils.cpp diff --git a/internal/frontend/bridge-gui/bridgepp/Test/Exception/TestException.cpp b/internal/frontend/bridge-gui/bridgepp/Test/TestException.cpp similarity index 100% rename from internal/frontend/bridge-gui/bridgepp/Test/Exception/TestException.cpp rename to internal/frontend/bridge-gui/bridgepp/Test/TestException.cpp diff --git a/internal/frontend/bridge-gui/bridgepp/Test/TestWorker.cpp b/internal/frontend/bridge-gui/bridgepp/Test/TestWorker.cpp new file mode 100644 index 00000000..6f6eb5bb --- /dev/null +++ b/internal/frontend/bridge-gui/bridgepp/Test/TestWorker.cpp @@ -0,0 +1,215 @@ +// Copyright (c) 2023 Proton AG +// +// This file is part of Proton Mail Bridge. +// +// Proton Mail Bridge is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Proton Mail Bridge is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Proton Mail Bridge. If not, see . + +// clazy:excludeall=lambda-in-connect + +#include "TestWorker.h" +#include +#include + + +using namespace bridgepp; + + +namespace { + + +qint32 dummyArgc = 1; ///< A dummy int value because QCoreApplication constructor requires a reference to it. + + +} + + +//**************************************************************************************************************************************************** +// +//**************************************************************************************************************************************************** +Workers::Workers() + : testing::Test() + , app_(dummyArgc, nullptr) { +} + + +//**************************************************************************************************************************************************** +// +//**************************************************************************************************************************************************** +void Workers::SetUp() { + Test::SetUp(); + + EXPECT_NO_THROW(worker_ = new TestWorker); + + QObject::connect(worker_, &TestWorker::started, [&]() { results_.started = true; }); + QObject::connect(worker_, &TestWorker::finished, [&]() { results_.finished = true; }); + QObject::connect(worker_, &TestWorker::finished, &loop_, &QEventLoop::quit); + QObject::connect(worker_, &TestWorker::error, [&] { results_.error = true; }); + QObject::connect(worker_, &TestWorker::error, &loop_, &QEventLoop::quit); + QObject::connect(worker_, &TestWorker::error, [&] { results_.error = true; }); + QObject::connect(worker_, &TestWorker::error, &loop_, &QEventLoop::quit); + QObject::connect(worker_, &TestWorker::cancelled, [&] { results_.cancelled = true; }); + QObject::connect(worker_, &TestWorker::cancelled, &loop_, &QEventLoop::quit); + + overseer_ = std::make_unique(worker_, nullptr); +} + + +//**************************************************************************************************************************************************** +// +//**************************************************************************************************************************************************** +void Workers::TearDown() { + EXPECT_NO_FATAL_FAILURE(overseer_.reset()); + Test::TearDown(); +} + + +//**************************************************************************************************************************************************** +/// \param[in] lifetimeMs The lifetime of the worker in milliseconds. +/// \param[in] willSucceed Will the worker succeed (emit finished) or fail (emit error). +//**************************************************************************************************************************************************** +TestWorker::TestWorker() + : Worker(nullptr) { +} + + +//**************************************************************************************************************************************************** +/// \param[in] lifetimeMs The lifetime of the worker in milliseconds. +//**************************************************************************************************************************************************** +void TestWorker::setLifetime(qint64 lifetimeMs) { + lifetimeMs_ = lifetimeMs; +} + + +//**************************************************************************************************************************************************** +/// \param[in] willSucceed Will the worker succeed? +//**************************************************************************************************************************************************** +void TestWorker::setWillSucceed(bool willSucceed) { + willSucceed_ = willSucceed; +} + + +//**************************************************************************************************************************************************** +// +//**************************************************************************************************************************************************** +void TestWorker::run() { + emit started(); + + QElapsedTimer timer; + timer.start(); + while (true) { + if (cancelled_.loadRelaxed()) { + emit cancelled(); + return; + } + if (timer.elapsed() >= lifetimeMs_) { + break; + } + } + + if (willSucceed_) { + emit finished(); + } else { + emit error(QString()); + } +} + + +//**************************************************************************************************************************************************** +// +//**************************************************************************************************************************************************** +void TestWorker::cancel() { + cancelled_.storeRelaxed(1); +} + + +//**************************************************************************************************************************************************** +// +//**************************************************************************************************************************************************** +TEST_F(Workers, SuccessfulWorker) { + worker_->setLifetime(10); + worker_->setWillSucceed(true); + + EXPECT_NO_THROW(overseer_->startWorker(false)); + EXPECT_NO_THROW(loop_.exec()); + + EXPECT_TRUE(results_.started); + EXPECT_TRUE(results_.finished); + EXPECT_FALSE(results_.error); + EXPECT_FALSE(results_.cancelled); + + EXPECT_TRUE(overseer_->worker() != nullptr); // overseer started without autorelease. +} + + +//**************************************************************************************************************************************************** +// +//**************************************************************************************************************************************************** +TEST_F(Workers, ErrorWorker) { + worker_->setLifetime(10); + worker_->setWillSucceed(false); + + EXPECT_NO_THROW(overseer_->startWorker(true)); + EXPECT_NO_THROW(loop_.exec()); + + EXPECT_TRUE(results_.started); + EXPECT_FALSE(results_.finished); + EXPECT_TRUE(results_.error); + EXPECT_FALSE(results_.cancelled); + + EXPECT_TRUE(overseer_->worker() == nullptr); // overseer started with autorelease. +} + +//**************************************************************************************************************************************************** +// +//**************************************************************************************************************************************************** +TEST_F(Workers, CancelledWorker) { + worker_->setLifetime(10000); + worker_->setWillSucceed(true); + EXPECT_NO_THROW(overseer_->startWorker(false)); + EXPECT_NO_THROW(QTimer::singleShot(10, [&]() { worker_->cancel(); })); + + EXPECT_NO_THROW(loop_.exec()); + + EXPECT_TRUE(results_.started); + EXPECT_FALSE(results_.finished); + EXPECT_FALSE(results_.error); + EXPECT_TRUE(results_.cancelled); +} + + +//**************************************************************************************************************************************************** +// +//**************************************************************************************************************************************************** +TEST_F(Workers, Wait) { + worker_->setLifetime(10000); + worker_->setWillSucceed(true); + overseer_->startWorker(true); + + bool isFinished = false; + EXPECT_NO_THROW(isFinished = overseer_->isFinished()); + EXPECT_FALSE(isFinished); + + EXPECT_NO_THROW(isFinished = overseer_->wait(10)); + EXPECT_FALSE(isFinished); + + worker_->cancel(); + + EXPECT_NO_THROW(isFinished = overseer_->wait(10000)); + EXPECT_TRUE(isFinished); + + EXPECT_NO_THROW(isFinished = overseer_->isFinished()); + EXPECT_TRUE(isFinished); +} + + diff --git a/internal/frontend/bridge-gui/bridgepp/Test/TestWorker.h b/internal/frontend/bridge-gui/bridgepp/Test/TestWorker.h new file mode 100644 index 00000000..abfde90c --- /dev/null +++ b/internal/frontend/bridge-gui/bridgepp/Test/TestWorker.h @@ -0,0 +1,88 @@ +// Copyright (c) 2023 Proton AG +// +// This file is part of Proton Mail Bridge. +// +// Proton Mail Bridge is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Proton Mail Bridge is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Proton Mail Bridge. If not, see . + + +#ifndef BRIDGE_GUI_TEST_WORKER_H +#define BRIDGE_GUI_TEST_WORKER_H + + +#include +#include + + +//**************************************************************************************************************************************************** +/// \brief Test worker class. +/// +/// This worker simply waits: +/// - For a specified amount of time and will succeed (emit finished()) or fail (emit error()) based on its parameters. +/// - to be cancelled (and will emit cancelled in that case). +//**************************************************************************************************************************************************** +class TestWorker : public bridgepp::Worker { +Q_OBJECT +public: // member functions. + TestWorker(); ///< Default constructor. + TestWorker(TestWorker const &) = delete; ///< Disabled copy-constructor. + TestWorker(TestWorker &&) = delete; ///< Disabled assignment copy-constructor. + ~TestWorker() override = default; ///< Destructor. + TestWorker &operator=(TestWorker const &) = delete; ///< Disabled assignment operator. + TestWorker &operator=(TestWorker &&) = delete; ///< Disabled move assignment operator. + void setLifetime(qint64 lifetimeMs); ///< Set the lifetime of the worker. + void setWillSucceed(bool willSucceed); ///< Set if the worker will succeed. + void run() override; ///< Run the worker. + void cancel(); ///< Cancel the worker. + +private: // data members + qint64 lifetimeMs_ { 10 }; ///< The lifetime of the worker in milliseconds. + bool willSucceed_ { true }; ///< Will the worker succeed? + QAtomicInteger cancelled_; ///< Has the worker been cancelled. +}; + + +//**************************************************************************************************************************************************** +/// \brief Fixture class for worker tests. +//**************************************************************************************************************************************************** +class Workers : public testing::Test { +public: // member functions. + Workers(); ///< Default constructor. + Workers(Workers const &) = delete; ///< Disabled copy-constructor. + Workers(Workers &&) = delete; ///< Disabled assignment copy-constructor. + ~Workers() = default; ///< Destructor. + Workers &operator=(Workers const &) = delete; ///< Disabled assignment operator. + Workers &operator=(Workers &&) = delete; ///< Disabled move assignment operator. + +protected: // member functions. + void SetUp() override; ///< Setup the fixture. + void TearDown() override; ///< Tear down the fixture. + +protected: // data type + struct Results { + bool started { false }; + bool finished { false }; + bool error { false }; + bool cancelled { false }; + }; ///< Test results data type + +protected: // data members + QCoreApplication app_; ///< The Qt application required for event loop. + bridgepp::UPOverseer overseer_; ///< The overseer for the worker. + TestWorker *worker_ { nullptr }; ///< The worker. + QEventLoop loop_; ///< The event loop. + Results results_; ///< The test results. +}; + + +#endif //BRIDGE_GUI_TEST_WORKER_H