QmlProfiler: Add tests for client manager

Change-Id: I9d758857dc65daa564ab6a0394250c02ea006832
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
This commit is contained in:
Ulf Hermann
2016-07-14 10:18:30 +02:00
parent 62b1217818
commit f372f4aa5a
5 changed files with 490 additions and 3 deletions

View File

@@ -92,6 +92,7 @@ QtcPlugin {
"qmlprofilerattachdialog_test.cpp", "qmlprofilerattachdialog_test.h", "qmlprofilerattachdialog_test.cpp", "qmlprofilerattachdialog_test.h",
"qmlprofilerbindingloopsrenderpass_test.cpp", "qmlprofilerbindingloopsrenderpass_test.cpp",
"qmlprofilerbindingloopsrenderpass_test.h", "qmlprofilerbindingloopsrenderpass_test.h",
"qmlprofilerclientmanager_test.cpp", "qmlprofilerclientmanager_test.h",
] ]
} }
} }

View File

@@ -30,6 +30,7 @@
#include "qmlprofilertimelinemodel.h" #include "qmlprofilertimelinemodel.h"
#ifdef WITH_TESTS #ifdef WITH_TESTS
#include "tests/debugmessagesmodel_test.h" #include "tests/debugmessagesmodel_test.h"
#include "tests/flamegraph_test.h" #include "tests/flamegraph_test.h"
#include "tests/flamegraphmodel_test.h" #include "tests/flamegraphmodel_test.h"
@@ -45,7 +46,15 @@
#include "tests/qmlprofileranimationsmodel_test.h" #include "tests/qmlprofileranimationsmodel_test.h"
#include "tests/qmlprofilerattachdialog_test.h" #include "tests/qmlprofilerattachdialog_test.h"
#include "tests/qmlprofilerbindingloopsrenderpass_test.h" #include "tests/qmlprofilerbindingloopsrenderpass_test.h"
#endif #include "tests/qmlprofilerclientmanager_test.h"
// Force QML Debugging to be enabled, so that we can selftest the profiler
#define QT_QML_DEBUG_NO_WARNING
#include <QQmlDebuggingEnabler>
#include <QQmlEngine>
#undef QT_QML_DEBUG_NO_WARNING
#endif // WITH_TESTS
#include <extensionsystem/pluginmanager.h> #include <extensionsystem/pluginmanager.h>
#include <utils/hostosinfo.h> #include <utils/hostosinfo.h>
@@ -107,6 +116,9 @@ QList<QObject *> QmlProfiler::Internal::QmlProfilerPlugin::createTestObjects() c
tests << new QmlProfilerAnimationsModelTest; tests << new QmlProfilerAnimationsModelTest;
tests << new QmlProfilerAttachDialogTest; tests << new QmlProfilerAttachDialogTest;
tests << new QmlProfilerBindingLoopsRenderPassTest; tests << new QmlProfilerBindingLoopsRenderPassTest;
tests << new QmlProfilerClientManagerTest;
tests << new QQmlEngine; // Trigger debug connector to be started
#endif #endif
return tests; return tests;
} }

View File

@@ -0,0 +1,407 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "qmlprofilerclientmanager_test.h"
#include <qmlprofiler/localqmlprofilerrunner.h>
#include <qmldebug/qmldebugcommandlinearguments.h>
#include <projectexplorer/applicationlauncher.h>
#include <QTcpServer>
#include <QTcpSocket>
#include <QLocalSocket>
#include <QQmlDebuggingEnabler>
#include <QtTest>
namespace QmlProfiler {
namespace Internal {
struct MessageHandler {
MessageHandler(QtMessageHandler handler)
{
defaultHandler = qInstallMessageHandler(handler);
}
~MessageHandler()
{
qInstallMessageHandler(defaultHandler);
}
static QtMessageHandler defaultHandler;
};
QtMessageHandler MessageHandler::defaultHandler;
QmlProfilerClientManagerTest::QmlProfilerClientManagerTest(QObject *parent) :
QObject(parent), modelManager(nullptr)
{
clientManager.setRetryParams(10, 10);
}
void QmlProfilerClientManagerTest::testConnectionFailure_data()
{
QTest::addColumn<QmlProfilerModelManager *>("modelManager");
QVarLengthArray<QmlProfilerModelManager *> modelManagers({nullptr, &modelManager});
QTest::addColumn<QmlProfilerStateManager *>("stateManager");
QVarLengthArray<QmlProfilerStateManager *> stateManagers({nullptr, &stateManager});
QString hostName;
Utils::Port port = LocalQmlProfilerRunner::findFreePort(hostName);
QTest::addColumn<QString>("host");
QVarLengthArray<QString> hosts({"", "/-/|\\-\\|/-", hostName});
QTest::addColumn<Utils::Port>("port");
QVarLengthArray<Utils::Port> ports({Utils::Port(), Utils::Port(5), port});
QTest::addColumn<QString>("socket");
QVarLengthArray<QString> sockets({"", "/-/|\\-\\|/-",
LocalQmlProfilerRunner::findFreeSocket()});
foreach (QmlProfilerModelManager *modelManager, modelManagers) {
foreach (QmlProfilerStateManager *stateManager, stateManagers) {
foreach (QString host, hosts) {
foreach (Utils::Port port, ports) {
foreach (QString socket, sockets) {
QString tag = QString::fromLatin1("%1, %2, %3, %4, %5")
.arg(QLatin1String(modelManager ? "modelManager" : "<null>"))
.arg(QLatin1String(stateManager ? "stateManager" : "<null>"))
.arg(host.isEmpty() ? "<empty>" : host)
.arg(port.isValid() ? port.number() : 0)
.arg(socket.isEmpty() ? "<empty>" : socket);
QTest::newRow(tag.toLatin1().constData())
<< modelManager << stateManager << host << port << socket;
}
}
}
}
}
}
void softAssertMessageHandler(QtMsgType type, const QMessageLogContext &context,
const QString &message)
{
if (type != QtDebugMsg || !message.startsWith("SOFT ASSERT: "))
MessageHandler::defaultHandler(type, context, message);
}
void QmlProfilerClientManagerTest::testConnectionFailure()
{
// This triggers a lot of soft asserts. We test that it still doesn't crash and stays in a
// consistent state.
QByteArray fatalAsserts = qgetenv("QTC_FATAL_ASSERTS");
qunsetenv("QTC_FATAL_ASSERTS");
MessageHandler handler(&softAssertMessageHandler);
Q_UNUSED(handler);
QFETCH(QmlProfilerModelManager *, modelManager);
QFETCH(QmlProfilerStateManager *, stateManager);
QFETCH(QString, host);
QFETCH(Utils::Port, port);
QFETCH(QString, socket);
QSignalSpy openedSpy(&clientManager, SIGNAL(connectionOpened()));
QSignalSpy closedSpy(&clientManager, SIGNAL(connectionClosed()));
QSignalSpy failedSpy(&clientManager, SIGNAL(connectionFailed()));
QVERIFY(!clientManager.isConnected());
clientManager.setModelManager(modelManager);
clientManager.setProfilerStateManager(stateManager);
if (socket.isEmpty()) {
clientManager.setTcpConnection(host, port);
} else {
clientManager.setLocalSocket(socket);
}
QVERIFY(!clientManager.isConnected());
clientManager.connectToTcpServer();
QTRY_COMPARE(failedSpy.count(), 1);
QCOMPARE(closedSpy.count(), 0);
QCOMPARE(openedSpy.count(), 0);
QVERIFY(!clientManager.isConnected());
clientManager.startLocalServer();
QTRY_COMPARE(failedSpy.count(), 2);
QCOMPARE(closedSpy.count(), 0);
QCOMPARE(openedSpy.count(), 0);
QVERIFY(!clientManager.isConnected());
clientManager.retryConnect();
QTRY_COMPARE(failedSpy.count(), 3);
QCOMPARE(closedSpy.count(), 0);
QCOMPARE(openedSpy.count(), 0);
QVERIFY(!clientManager.isConnected());
clientManager.clearConnection();
qputenv("QTC_FATAL_ASSERTS", fatalAsserts);
}
void QmlProfilerClientManagerTest::testUnresponsiveTcp()
{
QSignalSpy openedSpy(&clientManager, SIGNAL(connectionOpened()));
QSignalSpy closedSpy(&clientManager, SIGNAL(connectionClosed()));
QSignalSpy failedSpy(&clientManager, SIGNAL(connectionFailed()));
QVERIFY(!clientManager.isConnected());
clientManager.setProfilerStateManager(&stateManager);
clientManager.setModelManager(&modelManager);
QString hostName;
Utils::Port port = LocalQmlProfilerRunner::findFreePort(hostName);
QTcpServer server;
server.listen(QHostAddress(hostName), port.number());
QSignalSpy connectionSpy(&server, SIGNAL(newConnection()));
clientManager.setTcpConnection(hostName, port);
clientManager.connectToTcpServer();
QTRY_COMPARE(connectionSpy.count(), 1);
QTRY_COMPARE(failedSpy.count(), 1);
QCOMPARE(openedSpy.count(), 0);
QCOMPARE(closedSpy.count(), 0);
QVERIFY(!clientManager.isConnected());
clientManager.clearConnection();
}
void QmlProfilerClientManagerTest::testUnresponsiveLocal()
{
QSignalSpy openedSpy(&clientManager, SIGNAL(connectionOpened()));
QSignalSpy closedSpy(&clientManager, SIGNAL(connectionClosed()));
QSignalSpy failedSpy(&clientManager, SIGNAL(connectionFailed()));
QVERIFY(!clientManager.isConnected());
clientManager.setProfilerStateManager(&stateManager);
clientManager.setModelManager(&modelManager);
QString socketFile = LocalQmlProfilerRunner::findFreeSocket();
QLocalSocket socket;
QSignalSpy connectionSpy(&socket, SIGNAL(connected()));
clientManager.setLocalSocket(socketFile);
clientManager.startLocalServer();
socket.connectToServer(socketFile);
QTRY_COMPARE(connectionSpy.count(), 1);
QTRY_COMPARE(failedSpy.count(), 1);
QCOMPARE(openedSpy.count(), 0);
QCOMPARE(closedSpy.count(), 0);
QVERIFY(!clientManager.isConnected());
clientManager.clearConnection();
}
void responsiveTestData()
{
QTest::addColumn<quint32>("flushInterval");
QTest::addColumn<bool>("aggregateTraces");
QTest::newRow("no flush, no aggregate") << 0u << false;
QTest::newRow("no flush, aggregate") << 0u << true;
QTest::newRow("flush, no aggregate") << 1u << false;
QTest::newRow("flush, aggregate") << 1u << true;
}
void QmlProfilerClientManagerTest::testResponsiveTcp_data()
{
responsiveTestData();
}
void QmlProfilerClientManagerTest::testResponsiveTcp()
{
QFETCH(quint32, flushInterval);
QFETCH(bool, aggregateTraces);
QString hostName;
Utils::Port port = LocalQmlProfilerRunner::findFreePort(hostName);
ProjectExplorer::StandardRunnable runnable;
runnable.environment = Utils::Environment::systemEnvironment();
runnable.executable = qApp->applicationFilePath();
runnable.commandLineArguments = "-test QmlProfiler,QQmlEngine "
+ QmlDebug::qmlDebugTcpArguments(QmlDebug::QmlProfilerServices, port);
runnable.runMode = ProjectExplorer::ApplicationLauncher::Gui;
ProjectExplorer::ApplicationLauncher launcher;
launcher.start(runnable);
QSignalSpy openedSpy(&clientManager, SIGNAL(connectionOpened()));
QSignalSpy closedSpy(&clientManager, SIGNAL(connectionClosed()));
QVERIFY(!clientManager.isConnected());
clientManager.setProfilerStateManager(&stateManager);
clientManager.setModelManager(&modelManager);
clientManager.setFlushInterval(flushInterval);
clientManager.setAggregateTraces(aggregateTraces);
QCOMPARE(clientManager.aggregateTraces(), aggregateTraces);
connect(&clientManager, &QmlProfilerClientManager::connectionFailed,
&clientManager, &QmlProfilerClientManager::retryConnect);
clientManager.setTcpConnection(hostName, port);
clientManager.connectToTcpServer();
QTRY_COMPARE(openedSpy.count(), 1);
QCOMPARE(closedSpy.count(), 0);
QVERIFY(clientManager.isConnected());
// Do some nasty things and make sure it doesn't crash
stateManager.setCurrentState(QmlProfilerStateManager::AppRunning);
stateManager.setClientRecording(false);
stateManager.setClientRecording(true);
clientManager.clearBufferedData();
stateManager.setCurrentState(QmlProfilerStateManager::AppStopRequested);
QTRY_VERIFY_WITH_TIMEOUT(!launcher.isRunning(), 10000);
QTRY_COMPARE(closedSpy.count(), 1);
QVERIFY(!clientManager.isConnected());
disconnect(&clientManager, &QmlProfilerClientManager::connectionFailed,
&clientManager, &QmlProfilerClientManager::retryConnect);
stateManager.setCurrentState(QmlProfilerStateManager::Idle);
}
void QmlProfilerClientManagerTest::testResponsiveLocal_data()
{
responsiveTestData();
}
void QmlProfilerClientManagerTest::testResponsiveLocal()
{
QFETCH(quint32, flushInterval);
QFETCH(bool, aggregateTraces);
QString socket = LocalQmlProfilerRunner::findFreeSocket();
QSignalSpy openedSpy(&clientManager, SIGNAL(connectionOpened()));
QSignalSpy closedSpy(&clientManager, SIGNAL(connectionClosed()));
QVERIFY(!clientManager.isConnected());
clientManager.setProfilerStateManager(&stateManager);
clientManager.setModelManager(&modelManager);
clientManager.setFlushInterval(flushInterval);
clientManager.setAggregateTraces(aggregateTraces);
QCOMPARE(clientManager.aggregateTraces(), aggregateTraces);
connect(&clientManager, &QmlProfilerClientManager::connectionFailed,
&clientManager, &QmlProfilerClientManager::retryConnect);
clientManager.setLocalSocket(socket);
clientManager.startLocalServer();
ProjectExplorer::StandardRunnable runnable;
runnable.environment = Utils::Environment::systemEnvironment();
runnable.executable = qApp->applicationFilePath();
runnable.commandLineArguments = "-test QmlProfiler,QQmlEngine "
+ QmlDebug::qmlDebugLocalArguments(QmlDebug::QmlProfilerServices, socket);
runnable.runMode = ProjectExplorer::ApplicationLauncher::Gui;
ProjectExplorer::ApplicationLauncher launcher;
launcher.start(runnable);
QTRY_COMPARE(openedSpy.count(), 1);
QCOMPARE(closedSpy.count(), 0);
QVERIFY(clientManager.isConnected());
// Do some nasty things and make sure it doesn't crash
stateManager.setCurrentState(QmlProfilerStateManager::AppRunning);
stateManager.setClientRecording(false);
stateManager.setClientRecording(true);
clientManager.clearBufferedData();
stateManager.setCurrentState(QmlProfilerStateManager::AppStopRequested);
QTRY_VERIFY_WITH_TIMEOUT(!launcher.isRunning(), 10000);
QTRY_COMPARE(closedSpy.count(), 1);
QVERIFY(!clientManager.isConnected());
disconnect(&clientManager, &QmlProfilerClientManager::connectionFailed,
&clientManager, &QmlProfilerClientManager::retryConnect);
stateManager.setCurrentState(QmlProfilerStateManager::Idle);
}
void invalidHelloMessageHandler(QtMsgType type, const QMessageLogContext &context,
const QString &message)
{
if (type != QtWarningMsg || message != "QML Debug Client: Invalid hello message")
MessageHandler::defaultHandler(type, context, message);
}
void QmlProfilerClientManagerTest::testInvalidData()
{
MessageHandler handler(&invalidHelloMessageHandler);
Q_UNUSED(handler);
QSignalSpy openedSpy(&clientManager, SIGNAL(connectionOpened()));
QSignalSpy closedSpy(&clientManager, SIGNAL(connectionClosed()));
QSignalSpy failedSpy(&clientManager, SIGNAL(connectionFailed()));
QVERIFY(!clientManager.isConnected());
clientManager.setProfilerStateManager(&stateManager);
clientManager.setModelManager(&modelManager);
QString hostName;
Utils::Port port = LocalQmlProfilerRunner::findFreePort(hostName);
bool dataSent = false;
QTcpServer server;
connect(&server, &QTcpServer::newConnection, [&server, &dataSent](){
QTcpSocket *socket = server.nextPendingConnection();
// emulate packet protocol
qint32 sendSize32 = 10;
socket->write((char *)&sendSize32, sizeof(qint32));
socket->write("----------------------- x -----------------------");
dataSent = true;
});
server.listen(QHostAddress(hostName), port.number());
clientManager.setTcpConnection(hostName, port);
clientManager.connectToTcpServer();
QTRY_VERIFY(dataSent);
QTRY_COMPARE(failedSpy.count(), 1);
QCOMPARE(openedSpy.count(), 0);
QCOMPARE(closedSpy.count(), 0);
QVERIFY(!clientManager.isConnected());
clientManager.clearConnection();
}
} // namespace Internal
} // namespace QmlProfiler

View File

@@ -0,0 +1,65 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include <qmlprofiler/qmlprofilerclientmanager.h>
#include <qmlprofiler/qmlprofilermodelmanager.h>
#include <qmlprofiler/qmlprofilerstatemanager.h>
#include <QObject>
namespace QmlProfiler {
namespace Internal {
class QmlProfilerClientManagerTest : public QObject
{
Q_OBJECT
public:
explicit QmlProfilerClientManagerTest(QObject *parent = 0);
private slots:
void testConnectionFailure_data();
void testConnectionFailure();
void testUnresponsiveTcp();
void testUnresponsiveLocal();
void testResponsiveTcp_data();
void testResponsiveTcp();
void testResponsiveLocal_data();
void testResponsiveLocal();
void testInvalidData();
private:
QmlProfilerClientManager clientManager;
QmlProfilerModelManager modelManager;
QmlProfilerStateManager stateManager;
};
} // namespace Internal
} // namespace QmlProfiler

View File

@@ -13,7 +13,8 @@ SOURCES += \
$$PWD/qmlnote_test.cpp \ $$PWD/qmlnote_test.cpp \
$$PWD/qmlprofileranimationsmodel_test.cpp \ $$PWD/qmlprofileranimationsmodel_test.cpp \
$$PWD/qmlprofilerattachdialog_test.cpp \ $$PWD/qmlprofilerattachdialog_test.cpp \
$$PWD/qmlprofilerbindingloopsrenderpass_test.cpp $$PWD/qmlprofilerbindingloopsrenderpass_test.cpp \
$$PWD/qmlprofilerclientmanager_test.cpp
HEADERS += \ HEADERS += \
$$PWD/debugmessagesmodel_test.h \ $$PWD/debugmessagesmodel_test.h \
@@ -30,4 +31,5 @@ HEADERS += \
$$PWD/qmlnote_test.h \ $$PWD/qmlnote_test.h \
$$PWD/qmlprofileranimationsmodel_test.h \ $$PWD/qmlprofileranimationsmodel_test.h \
$$PWD/qmlprofilerattachdialog_test.h \ $$PWD/qmlprofilerattachdialog_test.h \
$$PWD/qmlprofilerbindingloopsrenderpass_test.h $$PWD/qmlprofilerbindingloopsrenderpass_test.h \
$$PWD/qmlprofilerclientmanager_test.h