File indexing completed on 2024-04-21 03:53:51

0001 /*
0002     SPDX-FileCopyrightText: 2008 Omat Holding B .V. <info@omat.nl>
0003     SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB , a KDAB Group company <info@kdab.com>
0004     SPDX-FileContributor: Kevin Ottens <kevin@kdab.com>
0005     SPDX-FileCopyrightText: 2017 Sandro Kanuß <knauss@kde.org>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 // Own
0011 #include "fakeserver.h"
0012 
0013 // Qt
0014 #include <QDebug>
0015 #include <QFile>
0016 #include <QTest>
0017 
0018 FakeServer::FakeServer(int port, QObject *parent)
0019     : QObject(parent)
0020     , m_thread(new QThread)
0021     , m_port(port)
0022 {
0023     moveToThread(m_thread);
0024 }
0025 
0026 FakeServer::~FakeServer()
0027 {
0028     QMetaObject::invokeMethod(this, &FakeServer::cleanup);
0029     m_thread->quit();
0030     m_thread->wait();
0031     delete m_thread;
0032 }
0033 
0034 void FakeServer::startAndWait()
0035 {
0036     m_thread->start();
0037     // this will block until the init code happens and the event queue starts
0038     QMetaObject::invokeMethod(this, &FakeServer::init, Qt::BlockingQueuedConnection);
0039 }
0040 
0041 void FakeServer::dataAvailable()
0042 {
0043     QMutexLocker locker(&m_mutex);
0044 
0045     QTcpSocket *socket = qobject_cast<QTcpSocket *>(sender());
0046     Q_ASSERT(socket != nullptr);
0047 
0048     int scenarioNumber = -1;
0049     readClientPart(socket, &scenarioNumber);
0050     if (scenarioNumber >= 0) {
0051         Q_ASSERT(scenarioNumber < m_scenarios.count());
0052         writeServerPart(socket, scenarioNumber);
0053     }
0054 }
0055 
0056 void FakeServer::newConnection()
0057 {
0058     QMutexLocker locker(&m_mutex);
0059 
0060     m_clientSockets << m_tcpServer->nextPendingConnection();
0061     connect(m_clientSockets.last(), &QTcpSocket::readyRead, this, &FakeServer::dataAvailable);
0062 }
0063 
0064 void FakeServer::init()
0065 {
0066     m_tcpServer = new QTcpServer();
0067 
0068     if (!m_tcpServer->listen(QHostAddress(QHostAddress::LocalHost), m_port)) {
0069         qFatal("Unable to start the server");
0070     }
0071 
0072     connect(m_tcpServer, &QTcpServer::newConnection, this, &FakeServer::newConnection);
0073 }
0074 
0075 void FakeServer::cleanup()
0076 {
0077     qDeleteAll(m_clientSockets);
0078     delete m_tcpServer;
0079 }
0080 
0081 void FakeServer::setScenario(const QList<QByteArray> &scenario)
0082 {
0083     QMutexLocker locker(&m_mutex);
0084 
0085     m_scenarios.clear();
0086     m_scenarios << scenario;
0087 }
0088 
0089 void FakeServer::addScenario(const QList<QByteArray> &scenario)
0090 {
0091     QMutexLocker locker(&m_mutex);
0092 
0093     m_scenarios << scenario;
0094 }
0095 
0096 void FakeServer::addScenarioFromFile(const QString &fileName)
0097 {
0098     QFile file(fileName);
0099     file.open(QIODevice::ReadOnly | QIODevice::Text);
0100 
0101     QList<QByteArray> scenario;
0102 
0103     while (!file.atEnd()) {
0104         scenario << file.readLine().trimmed();
0105     }
0106 
0107     file.close();
0108 
0109     addScenario(scenario);
0110 }
0111 
0112 bool FakeServer::isScenarioDone(int scenarioNumber) const
0113 {
0114     QMutexLocker locker(&m_mutex);
0115 
0116     if (scenarioNumber < m_scenarios.size()) {
0117         return m_scenarios[scenarioNumber].isEmpty();
0118     } else {
0119         return true; // Non existent hence empty, right?
0120     }
0121 }
0122 
0123 bool FakeServer::isAllScenarioDone() const
0124 {
0125     QMutexLocker locker(&m_mutex);
0126 
0127     for (const QList<QByteArray> &scenario : std::as_const(m_scenarios)) {
0128         if (!scenario.isEmpty()) {
0129             return false;
0130         }
0131     }
0132 
0133     return true;
0134 }
0135 
0136 void FakeServer::writeServerPart(QTcpSocket *clientSocket, int scenarioNumber)
0137 {
0138     QList<QByteArray> scenario = m_scenarios[scenarioNumber];
0139 
0140     while (!scenario.isEmpty() && scenario.first().startsWith("S: ")) {
0141         QByteArray rule = scenario.takeFirst();
0142 
0143         QByteArray payload = rule.mid(3);
0144         clientSocket->write(payload + "\r\n");
0145     }
0146 
0147     QByteArray data;
0148 
0149     while (!scenario.isEmpty() && scenario.first().startsWith("D: ")) {
0150         QByteArray rule = scenario.takeFirst();
0151 
0152         QByteArray payload = rule.mid(3);
0153         data.append(payload + "\r\n");
0154     }
0155 
0156     clientSocket->write(QStringLiteral("Content-Length: %1\r\n\r\n").arg(data.length()).toLatin1());
0157     clientSocket->write(data);
0158 
0159     if (!scenario.isEmpty() && scenario.first().startsWith("X")) {
0160         scenario.takeFirst();
0161         clientSocket->close();
0162     }
0163 
0164     if (!scenario.isEmpty()) {
0165         QVERIFY(scenario.first().startsWith("C: "));
0166     }
0167 
0168     m_scenarios[scenarioNumber] = scenario;
0169 }
0170 
0171 void FakeServer::readClientPart(QTcpSocket *socket, int *scenarioNumber)
0172 {
0173     QByteArray line = socket->readLine();
0174     qDebug() << "Read client request" << line;
0175     const auto it = std::find_if(m_scenarios.begin(), m_scenarios.end(), [&](const QList<QByteArray> &scenario) {
0176         return !scenario.isEmpty() && scenario.at(0).mid(3) + "\r\n" == line;
0177     });
0178     if (it == m_scenarios.end()) {
0179         qWarning() << "No server response available for" << line;
0180         QFAIL("Unexpected request");
0181         return;
0182     }
0183     QList<QByteArray> scenario = *it;
0184     QList<QByteArray> header;
0185 
0186     while (line != "\r\n") {
0187         header << line;
0188         if (socket->bytesAvailable() == 0 && !socket->waitForReadyRead()) {
0189             qDebug() << header;
0190             QFAIL("could not read all headers");
0191             return;
0192         }
0193         line = socket->readLine();
0194     }
0195 
0196     while (!scenario.isEmpty() && scenario.first().startsWith("C: ")) {
0197         QByteArray expected = scenario.takeFirst().mid(3) + "\r\n";
0198 
0199         if (!header.contains(expected)) {
0200             qWarning() << expected << "not found in header. Here's what we got:";
0201             qWarning() << header;
0202             QVERIFY(false);
0203             break;
0204         }
0205     }
0206 
0207     if (!scenario.isEmpty()) {
0208         QVERIFY(scenario.first().startsWith("S: "));
0209     }
0210 
0211     *it = scenario;
0212     *scenarioNumber = std::distance(m_scenarios.begin(), it);
0213     QVERIFY(*scenarioNumber < m_scenarios.count());
0214 }
0215 
0216 int FakeServer::port() const
0217 {
0218     return m_port;
0219 }
0220 
0221 #include "moc_fakeserver.cpp"