File indexing completed on 2024-11-10 05:11:08
0001 /* 0002 * Copyright 2018 by Marco Martin <mart@kde.org> 0003 * 0004 * Licensed under the Apache License, Version 2.0 (the "License"); 0005 * you may not use this file except in compliance with the License. 0006 * You may obtain a copy of the License at 0007 * 0008 * http://www.apache.org/licenses/LICENSE-2.0 0009 * 0010 * Unless required by applicable law or agreed to in writing, software 0011 * distributed under the License is distributed on an "AS IS" BASIS, 0012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 0013 * See the License for the specific language governing permissions and 0014 * limitations under the License. 0015 * 0016 */ 0017 0018 #include <QtTest> 0019 #include <QWebSocket> 0020 #include <QWebSocketServer> 0021 #include <QAbstractItemModel> 0022 #include <QQuickView> 0023 #include <QQmlEngine> 0024 #include "../import/mycroftcontroller.h" 0025 #include "../import/abstractdelegate.h" 0026 #include "../import/filereader.h" 0027 #include "../import/globalsettings.h" 0028 #include "../import/activeskillsmodel.h" 0029 #include "../import/delegatesmodel.h" 0030 #include "../import/abstractskillview.h" 0031 #include "../import/sessiondatamap.h" 0032 #include "../import/sessiondatamodel.h" 0033 0034 class ServerTest : public QObject 0035 { 0036 Q_OBJECT 0037 0038 public Q_SLOTS: 0039 void initTestCase(); 0040 0041 private Q_SLOTS: 0042 void testGuiConnection(); 0043 void create100Skills(); 0044 void move100Skills(); 0045 void delete100Skills(); 0046 void repeat100(); 0047 0048 0049 private: 0050 AbstractDelegate *delegateForSkill(const QString &skill, const QUrl &url); 0051 QList <AbstractDelegate *>delegatesForSkill(const QString &skill); 0052 0053 //Client 0054 MycroftController *m_controller; 0055 AbstractSkillView *m_view; 0056 0057 QQuickView *m_window; 0058 0059 //Server 0060 QWebSocketServer *m_mainServerSocket; 0061 QWebSocketServer *m_guiServerSocket; 0062 0063 QWebSocket *m_mainWebSocket; 0064 QWebSocket *m_guiWebSocket; 0065 }; 0066 0067 0068 static QObject *fileReaderSingletonProvider(QQmlEngine *engine, QJSEngine *scriptEngine) 0069 { 0070 Q_UNUSED(engine) 0071 Q_UNUSED(scriptEngine) 0072 0073 return new FileReader; 0074 } 0075 0076 static QObject *globalSettingsSingletonProvider(QQmlEngine *engine, QJSEngine *scriptEngine) 0077 { 0078 Q_UNUSED(engine) 0079 Q_UNUSED(scriptEngine) 0080 0081 return new GlobalSettings; 0082 } 0083 0084 static QObject *mycroftControllerSingletonProvider(QQmlEngine *engine, QJSEngine *scriptEngine) 0085 { 0086 Q_UNUSED(engine); 0087 Q_UNUSED(scriptEngine); 0088 0089 return MycroftController::instance(); 0090 } 0091 0092 0093 0094 AbstractDelegate *ServerTest::delegateForSkill(const QString &skill, const QUrl &url) 0095 { 0096 DelegatesModel *delegatesModel = m_view->activeSkills()->delegatesModelForSkill(skill); 0097 if (!delegatesModel) { 0098 return nullptr; 0099 } 0100 0101 for (auto d : delegatesModel->delegates()) { 0102 if (d->qmlUrl() == url) { 0103 return d; 0104 } 0105 } 0106 return nullptr; 0107 } 0108 0109 QList <AbstractDelegate *>ServerTest::delegatesForSkill(const QString &skill) 0110 { 0111 DelegatesModel *delegatesModel = m_view->activeSkills()->delegatesModelForSkill(skill); 0112 0113 if (!delegatesModel) { 0114 return {}; 0115 } 0116 return delegatesModel->delegates(); 0117 } 0118 0119 void ServerTest::initTestCase() 0120 { 0121 m_mainServerSocket = new QWebSocketServer(QStringLiteral("core"), 0122 QWebSocketServer::NonSecureMode, this); 0123 m_mainServerSocket->listen(QHostAddress::Any, 8181); 0124 m_guiServerSocket = new QWebSocketServer(QStringLiteral("gui"), 0125 QWebSocketServer::NonSecureMode, this); 0126 m_guiServerSocket->listen(QHostAddress::Any, 1818); 0127 m_controller = MycroftController::instance(); 0128 //TODO: delete 0129 //m_view = new AbstractSkillView; 0130 m_window = new QQuickView; 0131 0132 bool pluginFound = false; 0133 for (const auto &path : m_window->engine()->importPathList()) { 0134 QDir importDir(path); 0135 if (importDir.entryList().contains(QStringLiteral("Mycroft"))) { 0136 pluginFound = true; 0137 break; 0138 } 0139 } 0140 0141 if (!pluginFound) { 0142 qmlRegisterSingletonType<MycroftController>("Mycroft", 1, 0, "MycroftController", mycroftControllerSingletonProvider); 0143 qmlRegisterSingletonType<GlobalSettings>("Mycroft", 1, 0, "GlobalSettings", globalSettingsSingletonProvider); 0144 qmlRegisterSingletonType<FileReader>("Mycroft", 1, 0, "FileReader", fileReaderSingletonProvider); 0145 qmlRegisterType<AbstractSkillView>("Mycroft", 1, 0, "AbstractSkillView"); 0146 qmlRegisterType<AbstractDelegate>("Mycroft", 1, 0, "AbstractDelegate"); 0147 0148 qmlRegisterType(QUrl(QStringLiteral("qrc:/qml/AudioPlayer.qml")), "Mycroft", 1, 0, "AudioPlayer"); 0149 qmlRegisterType(QUrl(QStringLiteral("qrc:/qml/AutoFitLabel.qml")), "Mycroft", 1, 0, "AutoFitLabel"); 0150 qmlRegisterType(QUrl(QStringLiteral("qrc:/qml/Delegate.qml")), "Mycroft", 1, 0, "Delegate"); 0151 qmlRegisterType(QUrl(QStringLiteral("qrc:/qml/PaginatedText.qml")), "Mycroft", 1, 0, "PaginatedText"); 0152 qmlRegisterType(QUrl(QStringLiteral("qrc:/qml/ProportionalDelegate.qml")), "Mycroft", 1, 0, "ProportionalDelegate"); 0153 qmlRegisterType(QUrl(QStringLiteral("qrc:/qml/ScrollableDelegate.qml")), "Mycroft", 1, 0, "ScrollableDelegate"); 0154 qmlRegisterType(QUrl(QStringLiteral("qrc:/qml/SkillView.qml")), "Mycroft", 1, 0, "SkillView"); 0155 qmlRegisterType(QUrl(QStringLiteral("qrc:/qml/SlideShow.qml")), "Mycroft", 1, 0, "SlideShow"); 0156 qmlRegisterType(QUrl(QStringLiteral("qrc:/qml/SlidingImage.qml")), "Mycroft", 1, 0, "SlidingImage"); 0157 qmlRegisterType(QUrl(QStringLiteral("qrc:/qml/StatusIndicator.qml")), "Mycroft", 1, 0, "StatusIndicator"); 0158 qmlRegisterType(QUrl(QStringLiteral("qrc:/qml/VideoPlayer.qml")), "Mycroft", 1, 0, "VideoPlayer"); 0159 0160 qmlRegisterUncreatableType<ActiveSkillsModel>("Mycroft", 1, 0, "ActiveSkillsModel", QStringLiteral("You cannot instantiate items of type ActiveSkillsModel")); 0161 qmlRegisterUncreatableType<DelegatesModel>("Mycroft", 1, 0, "DelegatesModel", QStringLiteral("You cannot instantiate items of type DelegatesModel")); 0162 qmlRegisterUncreatableType<SessionDataMap>("Mycroft", 1, 0, "SessionDataMap", QStringLiteral("You cannot instantiate items of type SessionDataMap")); 0163 0164 qmlRegisterType(QUrl::fromLocalFile(QFINDTESTDATA(QStringLiteral("../import/qml/Delegate.qml"))), "Mycroft", 1, 0, "Delegate"); 0165 0166 qmlProtectModule("Mycroft", 1); 0167 } 0168 0169 m_window->setResizeMode(QQuickView::SizeRootObjectToView); 0170 m_window->resize(400, 800); 0171 0172 //Load the AbstractSkillview from our specialization in QML 0173 m_window->setSource(QUrl::fromLocalFile(QFINDTESTDATA("../import/qml/SkillView.qml"))); 0174 if (m_window->errors().length() > 0) { 0175 qWarning() << m_window->errors(); 0176 } 0177 m_window->show(); 0178 m_view = qobject_cast<AbstractSkillView *>(m_window->rootObject()); 0179 QVERIFY(m_view); 0180 0181 new QAbstractItemModelTester(m_view->activeSkills(), QAbstractItemModelTester::FailureReportingMode::QtTest, this); 0182 } 0183 0184 //TODO: test a spotty connection 0185 void ServerTest::testGuiConnection() 0186 { 0187 QSignalSpy newConnectionSpy(m_mainServerSocket, &QWebSocketServer::newConnection); 0188 QSignalSpy controllerSocketStatusChangedSpy(m_controller, &MycroftController::socketStatusChanged); 0189 m_controller->start(); 0190 0191 //wait the server received a connection and the client got connected state 0192 newConnectionSpy.wait(); 0193 0194 m_mainWebSocket = m_mainServerSocket->nextPendingConnection(); 0195 QSignalSpy textFromMainSpy(m_mainWebSocket, &QWebSocket::textMessageReceived); 0196 QVERIFY(m_mainWebSocket); 0197 0198 controllerSocketStatusChangedSpy.wait(); 0199 QCOMPARE(m_controller->status(), MycroftController::Open); 0200 0201 textFromMainSpy.wait(); 0202 auto doc = QJsonDocument::fromJson(textFromMainSpy.first().first().toString().toLatin1()); 0203 auto type = doc[QStringLiteral("type")].toString(); 0204 auto guiId = doc[QStringLiteral("data")][QStringLiteral("gui_id")].toString(); 0205 0206 QCOMPARE(type, QStringLiteral("mycroft.gui.connected")); 0207 QVERIFY(guiId.length() > 0); 0208 0209 //Now, connect the gui 0210 QSignalSpy newGuiConnectionSpy(m_guiServerSocket, &QWebSocketServer::newConnection); 0211 m_mainWebSocket->sendTextMessage(QStringLiteral("{\"type\": \"mycroft.gui.port\", \"data\": {\"gui_id\": \"%1\", \"port\": 1818}}").arg(guiId)); 0212 newGuiConnectionSpy.wait(); 0213 m_guiWebSocket = m_guiServerSocket->nextPendingConnection(); 0214 QVERIFY(m_guiWebSocket); 0215 } 0216 0217 void ServerTest::create100Skills() 0218 { 0219 QSignalSpy skillInsertedSpy(m_view->activeSkills(), &ActiveSkillsModel::rowsInserted); 0220 0221 QCOMPARE(m_view->activeSkills()->rowCount(), 0); 0222 0223 const QUrl url(QStringLiteral("file://") + QFINDTESTDATA("delegatewithloader.qml")); 0224 0225 for (int i = 0; i < 10; ++i) { 0226 const QString id = QString::number(i); 0227 0228 m_guiWebSocket->sendTextMessage(QStringLiteral("{\"type\": \"mycroft.session.list.insert\", \"namespace\": \"mycroft.system.active_skills\", \"position\": 0, \"data\": [{\"skill_id\": \"") + id + QStringLiteral("\"}]}")); 0229 0230 skillInsertedSpy.wait(); 0231 SessionDataMap *map = m_view->sessionDataForSkill(id); 0232 QVERIFY(map); 0233 0234 //set data 0235 m_guiWebSocket->sendTextMessage(QStringLiteral("{\"type\": \"mycroft.session.set\", \"namespace\": \"") + id + QStringLiteral("\", \"data\": {\"temperature\": \"24°C\", \"otherproperty\": \"value\"}}")); 0236 0237 m_guiWebSocket->sendTextMessage(QStringLiteral("{\"type\": \"mycroft.gui.list.insert\", \"namespace\": \"") + id + QStringLiteral("\", \"position\": 0, \"data\": [{\"url\": \"") + url.toString() + QStringLiteral("\"}]}")); 0238 0239 m_guiWebSocket->sendTextMessage(QStringLiteral("{\"type\": \"mycroft.session.set\", \"namespace\": \"") + id + QStringLiteral("\", \"data\": {\"temperature\": \"24°C\", \"otherproperty\": \"value\", \"state\": \"subdelegate1\"}}")); 0240 } 0241 0242 for (int i = 0; i < 10; ++i) { 0243 const QString id = QString::number(i); 0244 m_guiWebSocket->sendTextMessage(QStringLiteral("{\"type\": \"mycroft.session.set\", \"namespace\": \"") + id + QStringLiteral("\", \"data\": {\"temperature\": \"24°C\", \"otherproperty\": \"value\", \"state\": \"subdelegate2\"}}")); 0245 } 0246 0247 QCOMPARE(m_view->activeSkills()->rowCount(), 10); 0248 for (int i = 0; i < 10; ++i) { 0249 QCOMPARE(m_view->activeSkills()->data(m_view->activeSkills()->index(9-i,0), ActiveSkillsModel::SkillId).toString(), QString::number(i)); 0250 } 0251 } 0252 0253 void ServerTest::move100Skills() 0254 { 0255 QSignalSpy skillMovedSpy(m_view->activeSkills(), &ActiveSkillsModel::rowsMoved); 0256 0257 // QTest::qWait(3000); 0258 const QUrl url(QStringLiteral("file://") + QFINDTESTDATA("delegatewithloader.qml")); 0259 0260 for (int i = 0; i < 10; ++i) { 0261 0262 m_guiWebSocket->sendTextMessage(QStringLiteral("{\"type\": \"mycroft.session.list.move\", \"namespace\": \"mycroft.system.active_skills\", \"from\": 9, \"to\": 0, \"items_number\": 1}")); 0263 0264 skillMovedSpy.wait(); 0265 } 0266 0267 } 0268 0269 void ServerTest::delete100Skills() 0270 { 0271 QSignalSpy skillRemovedSpy(m_view->activeSkills(), &ActiveSkillsModel::rowsRemoved); 0272 0273 // QTest::qWait(3000); 0274 const QUrl url(QStringLiteral("file://") + QFINDTESTDATA("delegatewithloader.qml")); 0275 0276 for (int i = 0; i < 10; ++i) { 0277 const QString id = QString::number(i); 0278 // Try to do a race condition between setting value and deleting 0279 m_guiWebSocket->sendTextMessage(QStringLiteral("{\"type\": \"mycroft.session.set\", \"namespace\": \"") + id + QStringLiteral("\", \"data\": {\"temperature\": \"25°C\", \"otherproperty\": \"value\", \"state\": \"subdelegate2\"}}")); 0280 0281 m_guiWebSocket->sendTextMessage(QStringLiteral("{\"type\": \"mycroft.session.list.remove\", \"namespace\": \"mycroft.system.active_skills\", \"position\": 0, \"items_number\": 1}")); 0282 0283 m_guiWebSocket->sendTextMessage(QStringLiteral("{\"type\": \"mycroft.session.set\", \"namespace\": \"") + id + QStringLiteral("\", \"data\": {\"temperature\": \"11°C\", \"otherproperty\": \"value\"}}")); 0284 0285 skillRemovedSpy.wait(); 0286 } 0287 0288 } 0289 0290 void ServerTest::repeat100() 0291 { 0292 for (int i = 0; i < 10; ++i) { 0293 create100Skills(); 0294 delete100Skills(); 0295 } 0296 } 0297 0298 QTEST_MAIN(ServerTest); 0299 0300 #include "stresstest.moc"