refs #605, fixed unit test itself

* threaded reader were normal members causing a crash when those were moved in their own thread
* we need own event processing for unit test
* Network request needs to be generated in main thread
* don`t shutdown readers on QCoreApplication::aboutToQuit, let CApplication handle it
* use CApplication in unit test (as in real world)

Unrelated:
* access global setup via application
This commit is contained in:
Klaus Basan
2016-02-27 22:17:07 +01:00
committed by Mathew Sutcliffe
parent 12957f8ec0
commit f124412896
10 changed files with 167 additions and 111 deletions

View File

@@ -18,6 +18,7 @@
#include "blackmisc/project.h"
#include "blackmisc/dbusserver.h"
#include "blackmisc/registermetadata.h"
#include "blackmisc/threadutils.h"
#include "blackmisc/network/networkutils.h"
#include "blackmisc/simulation/aircraftmodellist.h"
#include "blackmisc/verify.h"
@@ -32,6 +33,7 @@ using namespace BlackMisc::Aviation;
using namespace BlackMisc::Simulation;
using namespace BlackMisc::Weather;
using namespace BlackCore;
using namespace BlackCore::Data;
BlackCore::CApplication *sApp = nullptr; // set by constructor
@@ -96,6 +98,13 @@ namespace BlackCore
return QCoreApplication::instance()->applicationName() + " " + CProject::version();
}
Data::CGlobalSetup CApplication::getGlobalSetup() const
{
const CSetupReader *r = this->m_setupReader.data();
if (!r) { return CGlobalSetup(); }
return r->getSetup();
}
bool CApplication::start(bool waitForStart)
{
if (!this->m_parsed)
@@ -130,7 +139,9 @@ namespace BlackCore
const QTime dieTime = QTime::currentTime().addMSecs(5000);
while (QTime::currentTime() < dieTime && !this->m_started && !this->m_startUpCompleted)
{
// Alternative: use QEventLoop, which seemed to make the scenario here more complex
QCoreApplication::instance()->processEvents(QEventLoop::AllEvents, 250);
QThread::msleep(250); // avoid CPU loop overload by "infinite loop"
}
if (!this->m_startUpCompleted)
{
@@ -170,46 +181,75 @@ namespace BlackCore
return this->m_webDataServices.data();
}
QNetworkReply *CApplication::getFromNetwork(const CUrl &url, const BlackMisc::CSlot<void (QNetworkReply *)> &callback)
bool CApplication::isApplicationThread() const
{
return CThreadUtils::isCurrentThreadApplicationThread();
}
QNetworkReply *CApplication::getFromNetwork(const CUrl &url, const CSlot<void(QNetworkReply *)> &callback)
{
if (this->m_shutdown) { return nullptr; }
return getFromNetwork(url.toNetworkRequest(), callback);
}
QNetworkReply *CApplication::getFromNetwork(const QNetworkRequest &request, const BlackMisc::CSlot<void(QNetworkReply *)> &callback)
QNetworkReply *CApplication::getFromNetwork(const QNetworkRequest &request, const CSlot<void(QNetworkReply *)> &callback)
{
if (this->m_shutdown) { return nullptr; }
QNetworkRequest r(request);
CNetworkUtils::ignoreSslVerification(r);
QWriteLocker locker(&m_accessManagerLock);
Q_ASSERT_X(QCoreApplication::instance()->thread() == m_accessManager.thread(), Q_FUNC_INFO, "Network manager supposed to be in main thread");
if (QThread::currentThread() != this->m_accessManager.thread())
{
QTimer::singleShot(0, this, [this, request, callback]() { this->getFromNetwork(request, callback); });
return nullptr; // not yet started
}
Q_ASSERT_X(QThread::currentThread() == m_accessManager.thread(), Q_FUNC_INFO, "Network manager thread mismatch");
QNetworkRequest r(request); // no QObject
CNetworkUtils::ignoreSslVerification(r);
QNetworkReply *reply = this->m_accessManager.get(r);
if (callback)
{
connect(reply, &QNetworkReply::finished, callback.object(), [ = ] { callback(reply); });
connect(reply, &QNetworkReply::finished, callback.object(), [ = ] { callback(reply); }, Qt::QueuedConnection);
}
return reply;
}
QNetworkReply *CApplication::postToNetwork(const QNetworkRequest &request, const QByteArray &data, const BlackMisc::CSlot<void (QNetworkReply *)> &callback)
QNetworkReply *CApplication::postToNetwork(const QNetworkRequest &request, const QByteArray &data, const CSlot<void(QNetworkReply *)> &callback)
{
if (this->m_shutdown) { return nullptr; }
QWriteLocker locker(&m_accessManagerLock);
Q_ASSERT_X(QCoreApplication::instance()->thread() == m_accessManager.thread(), Q_FUNC_INFO, "Network manager supposed to be in main thread");
if (QThread::currentThread() != this->m_accessManager.thread())
{
QTimer::singleShot(0, this, [this, request, data, callback]() { this->postToNetwork(request, data, callback); });
return nullptr; // not yet started
}
Q_ASSERT_X(QThread::currentThread() == m_accessManager.thread(), Q_FUNC_INFO, "Network manager thread mismatch");
QNetworkRequest r(request);
CNetworkUtils::ignoreSslVerification(r);
QWriteLocker locker(&m_accessManagerLock);
QNetworkReply *reply = this->m_accessManager.post(r, data);
if (callback)
{
connect(reply, &QNetworkReply::finished, callback.object(), [ = ] { callback(reply); });
connect(reply, &QNetworkReply::finished, callback.object(), [ = ] { callback(reply); }, Qt::QueuedConnection);
}
return reply;
}
QNetworkReply *CApplication::postToNetwork(const QNetworkRequest &request, QHttpMultiPart *multiPart, const BlackMisc::CSlot<void (QNetworkReply *)> &callback)
QNetworkReply *CApplication::postToNetwork(const QNetworkRequest &request, QHttpMultiPart *multiPart, const CSlot<void(QNetworkReply *)> &callback)
{
if (this->m_shutdown) { return nullptr; }
QWriteLocker locker(&m_accessManagerLock);
Q_ASSERT_X(QCoreApplication::instance()->thread() == m_accessManager.thread(), Q_FUNC_INFO, "Network manager supposed to be in main thread");
if (QThread::currentThread() != this->m_accessManager.thread())
{
QTimer::singleShot(0, this, [this, request, multiPart, callback]() { this->postToNetwork(request, multiPart, callback); });
return nullptr; // not yet started
}
Q_ASSERT_X(QThread::currentThread() == m_accessManager.thread(), Q_FUNC_INFO, "Network manager thread mismatch");
QNetworkRequest r(request);
CNetworkUtils::ignoreSslVerification(r);
QWriteLocker locker(&m_accessManagerLock);
QNetworkReply *reply = this->m_accessManager.post(r, multiPart);
if (callback)
{
@@ -244,6 +284,16 @@ namespace BlackCore
return QCoreApplication::arguments();
}
void CApplication::processEventsFor(int milliseconds)
{
const QTime end = QTime::currentTime().addMSecs(milliseconds);
while (QTime::currentTime() <= end)
{
QCoreApplication::processEvents();
QThread::msleep(100);
}
}
bool CApplication::useContexts(const CCoreFacadeConfig &coreConfig)
{
Q_ASSERT_X(this->m_parsed, Q_FUNC_INFO, "Call this after parsing");
@@ -380,6 +430,7 @@ namespace BlackCore
this->m_started = this->asyncWebAndContextStart();
}
this->m_startUpCompleted = true;
emit this->startUpCompleted(this->m_started);
}
bool CApplication::asyncWebAndContextStart()

View File

@@ -71,31 +71,9 @@ namespace BlackCore
//! Application name and version
QString getApplicationNameAndVersion() const;
//! Start services, if not yet parsed call CApplication::parse
virtual bool start(bool waitForStart = true);
//! Wait for stert by calling the event loop and waiting until everything is ready
bool waitForStart();
//! Request to get network reply
//! Global setup
//! \threadsafe
QNetworkReply *getFromNetwork(const BlackMisc::Network::CUrl &url,
const BlackMisc::CSlot<void(QNetworkReply *)> &callback);
//! Request to get network reply
//! \threadsafe
QNetworkReply *getFromNetwork(const QNetworkRequest &request,
const BlackMisc::CSlot<void(QNetworkReply *)> &callback);
//! Post to network
//! \threadsafe
QNetworkReply *postToNetwork(const QNetworkRequest &request, const QByteArray &data,
const BlackMisc::CSlot<void(QNetworkReply *)> &callback);
//! Post to network
//! \threadsafe
QNetworkReply *postToNetwork(const QNetworkRequest &request, QHttpMultiPart *multiPart,
const BlackMisc::CSlot<void(QNetworkReply *)> &callback);
BlackCore::Data::CGlobalSetup getGlobalSetup() const;
//! Delete all cookies from cookier manager
void deleteAllCookies();
@@ -112,6 +90,9 @@ namespace BlackCore
//! Get the web data services
CWebDataServices *getWebDataServices() const;
//! Currently running in application thread?
bool isApplicationThread() const;
//! Run event loop
static int exec();
@@ -121,6 +102,9 @@ namespace BlackCore
//! Similar to QCoreApplication::arguments
static QStringList arguments();
//! Process all events for some time
static void processEventsFor(int milliseconds);
// ----------------------- parsing ----------------------------------------
//! \name parsing of command line options
@@ -197,10 +181,39 @@ namespace BlackCore
//! Graceful shutdown
virtual void gracefulShutdown();
//! Start services, if not yet parsed call CApplication::parse
virtual bool start(bool waitForStart = true);
//! Wait for stert by calling the event loop and waiting until everything is ready
bool waitForStart();
//! Request to get network reply
//! \threadsafe
QNetworkReply *getFromNetwork(const BlackMisc::Network::CUrl &url,
const BlackMisc::CSlot<void(QNetworkReply *)> &callback);
//! Request to get network reply
//! \threadsafe
QNetworkReply *getFromNetwork(const QNetworkRequest &request,
const BlackMisc::CSlot<void(QNetworkReply *)> &callback);
//! Post to network
//! \threadsafe
QNetworkReply *postToNetwork(const QNetworkRequest &request, const QByteArray &data,
const BlackMisc::CSlot<void(QNetworkReply *)> &callback);
//! Post to network
//! \threadsafe
QNetworkReply *postToNetwork(const QNetworkRequest &request, QHttpMultiPart *multiPart,
const BlackMisc::CSlot<void(QNetworkReply *)> &callback);
signals:
//! Setup syncronized
void setupSyncronized();
//! Startup has been completed
void startUpCompleted(bool success);
//! Facade started
void coreFacadeStarted();
@@ -212,9 +225,6 @@ namespace BlackCore
void ps_setupSyncronized(bool success);
protected:
//! Constructor
CApplication(const QString &applicationName, QCoreApplication *app);
//! Display help message
virtual void cmdLineHelpMessage();

View File

@@ -66,17 +66,9 @@ namespace BlackCore
QString query = params.toString();
const QNetworkRequest request(CNetworkUtils::getNetworkRequest(url, CNetworkUtils::PostUrlEncoded));
QNetworkReply *r = sApp->postToNetwork(request, query.toUtf8(), { this, &CDatabaseAuthenticationService::ps_parseServerResponse});
if (!r)
{
QString rm("Cannot send request to authentication server %1");
msgs.push_back(CStatusMessage(cats, CStatusMessage::SeverityError, rm.arg(url.toQString())));
}
else
{
QString rm("Sent request to authentication server %1");
msgs.push_back(CStatusMessage(cats, CStatusMessage::SeverityInfo, rm.arg(url.toQString())));
}
sApp->postToNetwork(request, query.toUtf8(), { this, &CDatabaseAuthenticationService::ps_parseServerResponse});
QString rm("Sent request to authentication server %1");
msgs.push_back(CStatusMessage(cats, CStatusMessage::SeverityInfo, rm.arg(url.toQString())));
return msgs;
}