File indexing completed on 2024-05-12 05:15:04
0001 /* 0002 Copyright (C) 2008 Omat Holding B.V. <info@omat.nl> 0003 0004 Copyright (C) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com> 0005 Author: Kevin Ottens <kevin@kdab.com> 0006 0007 Copyright (C) 2017 Sandro Kanuà <knauss@kde.org> 0008 0009 This program is free software; you can redistribute it and/or 0010 modify it under the terms of the GNU General Public 0011 License as published by the Free Software Foundation; either 0012 version 2 of the License, or (at your option) any later version. 0013 0014 This program is distributed in the hope that it will be useful, 0015 but WITHOUT ANY WARRANTY; without even the implied warranty of 0016 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0017 General Public License for more details. 0018 0019 You should have received a copy of the GNU General Public License 0020 along with this program; if not, write to the Free Software 0021 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 0022 */ 0023 0024 #ifndef FAKESERVER_H 0025 #define FAKESERVER_H 0026 0027 #include <QTcpSocket> 0028 #include <QTcpServer> 0029 #include <QThread> 0030 #include <QMutex> 0031 0032 Q_DECLARE_METATYPE(QList<QByteArray>) 0033 0034 /** 0035 * Pretends to be an DAV server for the purposes of unit tests. 0036 * 0037 * FakeServer does not really understand the DAV protocol. Instead, 0038 * you give it a script, or scenario, that lists how an DAV session 0039 * exchange should go. When it receives the client parts of the 0040 * scenario, it will respond with the following server parts. 0041 * 0042 * The server can be furnished with several scenarios. The first 0043 * scenario will be played out to the first client that connects, the 0044 * second scenario to the second client connection and so on. 0045 * 0046 * The fake server runs as a separate thread in the same process it 0047 * is started from, and listens for connections (see port() method) on the 0048 * local machine. 0049 * 0050 * Scenarios are in the form of protocol messages, with a tag at the 0051 * start to indicate whether it is message that will be sent by the 0052 * client ("C: ") or a response that should be sent by the server 0053 * ("S: "). Or ("D: ") for the exchanged data. Content-length header is added 0054 * automatically with the corrent length and also the empty line between Header 0055 * and Content. For example: 0056 * @code 0057 * C: GET /item HTTP/1.1 0058 * S: HTTP/1.0 200 OK 0059 * D: much data 0060 * D: more data 0061 * X 0062 * @endcode 0063 * 0064 * A line starting with X indicates that the connection should be 0065 * closed by the server. This should be the last line in the 0066 * scenario. 0067 0068 * A typical usage is something like 0069 * @code 0070 * QList<QByteArray> scenario; 0071 * scenario << "C: GET /item HTTP/1.1" 0072 * << "S: HTTP/1.0 200 OK" 0073 * << "D: much data" 0074 * << "D: more data" 0075 * << "X"; 0076 * 0077 * FakeServer fakeServer; 0078 * fakeServer.setScenario(scenario); 0079 * fakeServer.startAndWait(); 0080 * 0081 * QUrl url(QStringLiteral("http://localhost/item")); 0082 * url.setPort(fakeServer.port()); 0083 * KDAV2::DavUrl davUrl(url, KDAV2::CardDav); 0084 * KDAV2::DavItem item(davUrl, QString(), QByteArray(), QString()); 0085 * 0086 * auto job = new KDAV2::DavItemFetchJob(item); 0087 * job->exec(); 0088 * fakeServer.quit(); 0089 * QVERIFY(fakeServer.isAllScenarioDone()); 0090 * @endcode 0091 */ 0092 0093 class FakeServer : public QThread 0094 { 0095 Q_OBJECT 0096 0097 public: 0098 FakeServer(QObject *parent = nullptr); 0099 ~FakeServer(); 0100 0101 /** 0102 * Starts the server and waits for it to be ready 0103 * 0104 * You should use this instead of start() to avoid race conditions. 0105 */ 0106 void startAndWait(); 0107 0108 /** 0109 * Starts the fake server 0110 * 0111 * You should not call this directly. Use start() instead. 0112 * 0113 * @reimp 0114 */ 0115 void run() Q_DECL_OVERRIDE; 0116 0117 /** 0118 * Removes any previously-added scenarios, and adds a new one 0119 * 0120 * After this, there will only be one scenario, and so the fake 0121 * server will only be able to service a single request. More 0122 * scenarios can be added with addScenario, though. 0123 * 0124 * @see addScenario()\n 0125 * addScenarioFromFile() 0126 */ 0127 void setScenario(const QList<QByteArray> &scenario); 0128 0129 /** 0130 * Adds a new scenario 0131 * 0132 * Note that scenarios will be used in the order that clients 0133 * connect. If this is the 5th scenario that has been added 0134 * (bearing in mind that setScenario() resets the scenario 0135 * count), it will be used to service the 5th client that 0136 * connects. 0137 * 0138 * @see addScenarioFromFile() 0139 * 0140 * @param scenario the scenario as a list of messages 0141 */ 0142 void addScenario(const QList<QByteArray> &scenario); 0143 /** 0144 * Adds a new scenario from a local file 0145 * 0146 * Note that scenarios will be used in the order that clients 0147 * connect. If this is the 5th scenario that has been added 0148 * (bearing in mind that setScenario() resets the scenario 0149 * count), it will be used to service the 5th client that 0150 * connects. 0151 * 0152 * @see addScenario() 0153 * 0154 * @param fileName the name of the file that contains the 0155 * scenario; it will be split at line 0156 * boundaries, and excess whitespace will 0157 * be trimmed from the start and end of lines 0158 */ 0159 void addScenarioFromFile(const QString &fileName); 0160 0161 /** 0162 * Checks whether a particular scenario has completed 0163 * 0164 * @param scenarioNumber the number of the scenario to check, 0165 * in order of addition/client connection 0166 */ 0167 bool isScenarioDone(int scenarioNumber) const; 0168 /** 0169 * Whether all the scenarios that were added to the fake 0170 * server have been completed. 0171 */ 0172 bool isAllScenarioDone() const; 0173 0174 /** 0175 * Returns the port where the fake server is listening. 0176 */ 0177 int port() const; 0178 0179 private Q_SLOTS: 0180 void newConnection(); 0181 void dataAvailable(); 0182 void started(); 0183 0184 private: 0185 void writeServerPart(int scenarioNumber); 0186 void readClientPart(int scenarioNumber); 0187 0188 QList< QList<QByteArray> > m_scenarios; 0189 QTcpServer *m_tcpServer; 0190 mutable QMutex m_mutex; 0191 QList<QTcpSocket *> m_clientSockets; 0192 int m_port; 0193 }; 0194 0195 #endif