Files
qt-creator/src/plugins/qmlprofiler/tests/qmlprofilerclientmanager_test.cpp
Ulf Hermann 5d6f5ff2c9 QmlProfiler: Move unrelated code out of QmlProfilerClientManager
The client manager should not be bothered with details of QML events,
but rather just connect the client, the model manager, and the state
manager.

Change-Id: Iec4499f8441a06d4ef5cbcf7bfe23da6f5e7f239
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
Reviewed-by: Alessandro Portale <alessandro.portale@qt.io>
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
2016-08-05 10:44:46 +00:00

452 lines
15 KiB
C++

/****************************************************************************
**
** 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/qpacketprotocol.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_VERIFY(connectionSpy.count() > 0);
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::newRow("no flush") << 0u;
QTest::newRow("flush") << 1u;
}
void QmlProfilerClientManagerTest::testResponsiveTcp_data()
{
responsiveTestData();
}
void fakeDebugServer(QIODevice *socket)
{
QmlDebug::QPacketProtocol *protocol = new QmlDebug::QPacketProtocol(socket, socket);
QObject::connect(protocol, &QmlDebug::QPacketProtocol::readyRead, [protocol]() {
QmlDebug::QPacket packet(QDataStream::Qt_4_7);
const int messageId = 0;
const int protocolVersion = 1;
const QStringList pluginNames({"CanvasFrameRate", "EngineControl", "DebugMessages"});
const QList<float> pluginVersions({1.0f, 1.0f, 1.0f});
packet << QString::fromLatin1("QDeclarativeDebugClient") << messageId << protocolVersion
<< pluginNames << pluginVersions << QDataStream::Qt_DefaultCompiledVersion;
protocol->send(packet.data());
protocol->disconnect();
protocol->deleteLater();
});
}
void QmlProfilerClientManagerTest::testResponsiveTcp()
{
QFETCH(quint32, flushInterval);
QString hostName;
Utils::Port port = LocalQmlProfilerRunner::findFreePort(hostName);
QSignalSpy openedSpy(&clientManager, SIGNAL(connectionOpened()));
QSignalSpy closedSpy(&clientManager, SIGNAL(connectionClosed()));
QVERIFY(!clientManager.isConnected());
{
QTcpServer server;
QScopedPointer<QTcpSocket> socket;
connect(&server, &QTcpServer::newConnection, [&server, &socket]() {
socket.reset(server.nextPendingConnection());
fakeDebugServer(socket.data());
});
server.listen(QHostAddress(hostName), port.number());
clientManager.setProfilerStateManager(&stateManager);
clientManager.setModelManager(&modelManager);
clientManager.setFlushInterval(flushInterval);
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);
QVERIFY(socket);
}
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);
QString socketFile = LocalQmlProfilerRunner::findFreeSocket();
QSignalSpy openedSpy(&clientManager, SIGNAL(connectionOpened()));
QSignalSpy closedSpy(&clientManager, SIGNAL(connectionClosed()));
QVERIFY(!clientManager.isConnected());
clientManager.setProfilerStateManager(&stateManager);
clientManager.setModelManager(&modelManager);
clientManager.setFlushInterval(flushInterval);
connect(&clientManager, &QmlProfilerClientManager::connectionFailed,
&clientManager, &QmlProfilerClientManager::retryConnect);
clientManager.setLocalSocket(socketFile);
clientManager.startLocalServer();
{
QScopedPointer<QLocalSocket> socket(new QLocalSocket(this));
socket->connectToServer(socketFile);
QVERIFY(socket->isOpen());
fakeDebugServer(socket.data());
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_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();
}
void QmlProfilerClientManagerTest::testStopRecording()
{
QString socketFile = LocalQmlProfilerRunner::findFreeSocket();
{
QmlProfilerClientManager clientManager;
clientManager.setRetryParams(10, 10);
QSignalSpy openedSpy(&clientManager, SIGNAL(connectionOpened()));
QSignalSpy closedSpy(&clientManager, SIGNAL(connectionClosed()));
QVERIFY(!clientManager.isConnected());
clientManager.setProfilerStateManager(&stateManager);
clientManager.setModelManager(&modelManager);
connect(&clientManager, &QmlProfilerClientManager::connectionFailed,
&clientManager, &QmlProfilerClientManager::retryConnect);
clientManager.setLocalSocket(socketFile);
clientManager.startLocalServer();
QScopedPointer<QLocalSocket> socket(new QLocalSocket(this));
socket->connectToServer(socketFile);
QVERIFY(socket->isOpen());
fakeDebugServer(socket.data());
QTRY_COMPARE(openedSpy.count(), 1);
QCOMPARE(closedSpy.count(), 0);
QVERIFY(clientManager.isConnected());
// We can't verify that it does anything useful, but at least it doesn't crash
clientManager.stopRecording();
}
// Delete while still connected, for added fun
}
} // namespace Internal
} // namespace QmlProfiler