File indexing completed on 2024-11-10 04:40:19
0001 /* 0002 * SPDX-FileCopyrightText: 2014 Daniel Vrátil <dvratil@redhat.com> 0003 * 0004 * SPDX-License-Identifier: LGPL-2.1-or-later 0005 * 0006 */ 0007 0008 #include "fakeclient.h" 0009 0010 #include "private/datastream_p_p.h" 0011 #include "private/protocol_exception_p.h" 0012 #include "private/protocol_p.h" 0013 0014 #include <QBuffer> 0015 #include <QLocalSocket> 0016 #include <QMutexLocker> 0017 #include <QTest> 0018 0019 #define CLIENT_COMPARE(actual, expected, ...) \ 0020 do { \ 0021 if (!QTest::qCompare(actual, expected, #actual, #expected, __FILE__, __LINE__)) { \ 0022 mSocket->disconnectFromServer(); \ 0023 return __VA_ARGS__; \ 0024 } \ 0025 } while (0) 0026 0027 #define CLIENT_VERIFY(statement, ...) \ 0028 do { \ 0029 if (!QTest::qVerify((statement), #statement, "", __FILE__, __LINE__)) { \ 0030 mSocket->disconnectFromServer(); \ 0031 return __VA_ARGS__; \ 0032 } \ 0033 } while (0) 0034 0035 using namespace Akonadi; 0036 using namespace Akonadi::Server; 0037 0038 FakeClient::FakeClient(QObject *parent) 0039 : QThread(parent) 0040 , mMutex() 0041 { 0042 moveToThread(this); 0043 } 0044 0045 FakeClient::~FakeClient() 0046 { 0047 } 0048 0049 void FakeClient::setScenarios(const TestScenario::List &scenarios) 0050 { 0051 mScenarios = scenarios; 0052 } 0053 0054 bool FakeClient::isScenarioDone() const 0055 { 0056 QMutexLocker locker(&mMutex); 0057 return mScenarios.isEmpty(); 0058 } 0059 0060 bool FakeClient::dataAvailable() 0061 { 0062 QMutexLocker locker(&mMutex); 0063 0064 CLIENT_VERIFY(!mScenarios.isEmpty(), false); 0065 0066 readServerPart(); 0067 writeClientPart(); 0068 0069 return true; 0070 } 0071 0072 void FakeClient::readServerPart() 0073 { 0074 while (!mScenarios.isEmpty() && (mScenarios.at(0).action == TestScenario::ServerCmd || mScenarios.at(0).action == TestScenario::Ignore)) { 0075 TestScenario scenario = mScenarios.takeFirst(); 0076 if (scenario.action == TestScenario::Ignore) { 0077 const int count = scenario.data.toInt(); 0078 0079 // Read and throw away all "count" responses. Useful for scenarios 0080 // with thousands of responses 0081 qint64 tag; 0082 for (int i = 0; i < count; ++i) { 0083 mStream >> tag; 0084 Protocol::deserialize(mStream.device()); 0085 } 0086 } else { 0087 QBuffer buffer(&scenario.data); 0088 buffer.open(QIODevice::ReadOnly); 0089 Protocol::DataStream expectedStream(&buffer); 0090 qint64 expectedTag; 0091 qint64 actualTag; 0092 0093 expectedStream >> expectedTag; 0094 const auto expectedCommand = Protocol::deserialize(expectedStream.device()); 0095 try { 0096 while (static_cast<size_t>(mSocket->bytesAvailable()) < sizeof(qint64)) { 0097 Protocol::DataStream::waitForData(mSocket, 5000); 0098 } 0099 } catch (const ProtocolException &e) { 0100 qDebug() << "ProtocolException:" << e.what(); 0101 qDebug() << "Expected response:" << Protocol::debugString(expectedCommand); 0102 CLIENT_VERIFY(false); 0103 } 0104 0105 mStream >> actualTag; 0106 CLIENT_COMPARE(actualTag, expectedTag); 0107 0108 Protocol::CommandPtr actualCommand; 0109 try { 0110 actualCommand = Protocol::deserialize(mStream.device()); 0111 } catch (const ProtocolException &e) { 0112 qDebug() << "Protocol exception:" << e.what(); 0113 qDebug() << "Expected response:" << Protocol::debugString(expectedCommand); 0114 CLIENT_VERIFY(false); 0115 } 0116 0117 if (actualCommand->type() != expectedCommand->type()) { 0118 qDebug() << "Actual command: " << Protocol::debugString(actualCommand); 0119 qDebug() << "Expected Command:" << Protocol::debugString(expectedCommand); 0120 } 0121 CLIENT_COMPARE(actualCommand->type(), expectedCommand->type()); 0122 CLIENT_COMPARE(actualCommand->isResponse(), expectedCommand->isResponse()); 0123 if (*actualCommand != *expectedCommand) { 0124 qDebug() << "Actual command: " << Protocol::debugString(actualCommand); 0125 qDebug() << "Expected Command:" << Protocol::debugString(expectedCommand); 0126 } 0127 0128 CLIENT_COMPARE(*actualCommand, *expectedCommand); 0129 } 0130 } 0131 0132 if (!mScenarios.isEmpty()) { 0133 CLIENT_VERIFY(mScenarios.at(0).action == TestScenario::ClientCmd || mScenarios.at(0).action == TestScenario::Wait 0134 || mScenarios.at(0).action == TestScenario::Quit); 0135 } else { 0136 // Server replied and there's nothing else to send, then quit 0137 mSocket->disconnectFromServer(); 0138 } 0139 } 0140 0141 void FakeClient::writeClientPart() 0142 { 0143 while (!mScenarios.isEmpty() && (mScenarios.at(0).action == TestScenario::ClientCmd || mScenarios.at(0).action == TestScenario::Wait)) { 0144 const TestScenario rule = mScenarios.takeFirst(); 0145 0146 if (rule.action == TestScenario::ClientCmd) { 0147 mSocket->write(rule.data); 0148 CLIENT_VERIFY(mSocket->waitForBytesWritten()); 0149 } else { 0150 const int timeout = rule.data.toInt(); 0151 QTest::qWait(timeout); 0152 } 0153 } 0154 0155 if (!mScenarios.isEmpty() && mScenarios.at(0).action == TestScenario::Quit) { 0156 mSocket->close(); 0157 } 0158 0159 if (!mScenarios.isEmpty()) { 0160 CLIENT_VERIFY(mScenarios.at(0).action == TestScenario::ServerCmd || mScenarios.at(0).action == TestScenario::Ignore); 0161 } 0162 } 0163 0164 void FakeClient::start() 0165 { 0166 QThread::start(); 0167 QMetaObject::invokeMethod(this, &FakeClient::do_connectToServer, Qt::QueuedConnection); 0168 } 0169 0170 void FakeClient::startScenario() 0171 { 0172 QMetaObject::invokeMethod(this, &FakeClient::do_startScenario, Qt::QueuedConnection); 0173 } 0174 0175 void FakeClient::do_connectToServer() 0176 { 0177 mSocket = new QLocalSocket(); 0178 mSocket->connectToServer(FakeAkonadiServer::socketFile()); 0179 connect(mSocket, &QLocalSocket::disconnected, this, &FakeClient::connectionLost); 0180 connect(mSocket, &QLocalSocket::errorOccurred, this, [this]() { 0181 qWarning() << "Client socket error: " << mSocket->errorString(); 0182 connectionLost(); 0183 QVERIFY(false); 0184 }); 0185 if (!mSocket->waitForConnected()) { 0186 qFatal("Failed to connect to FakeAkonadiServer"); 0187 QVERIFY(false); 0188 return; 0189 } 0190 mStream.setDevice(mSocket); 0191 } 0192 0193 void FakeClient::do_startScenario() 0194 { 0195 for (;;) { 0196 if (mSocket->state() != QLocalSocket::ConnectedState) { 0197 connectionLost(); 0198 break; 0199 } 0200 0201 if (mSocket->bytesAvailable() == 0) { 0202 QEventLoop loop; 0203 connect(mSocket, &QLocalSocket::readyRead, &loop, &QEventLoop::quit); 0204 connect(mSocket, &QLocalSocket::disconnected, &loop, &QEventLoop::quit); 0205 loop.exec(); 0206 } 0207 0208 while (mSocket->bytesAvailable() > 0) { 0209 if (mSocket->state() != QLocalSocket::ConnectedState) { 0210 connectionLost(); 0211 break; 0212 } 0213 0214 if (!dataAvailable()) { 0215 break; 0216 } 0217 } 0218 } 0219 0220 mStream.setDevice(nullptr); 0221 mSocket->close(); 0222 delete mSocket; 0223 mSocket = nullptr; 0224 0225 // Quit the thread 0226 quit(); 0227 } 0228 0229 void FakeClient::connectionLost() 0230 { 0231 // Otherwise this is an error on server-side, we expected more talking 0232 CLIENT_VERIFY(isScenarioDone()); 0233 } 0234 0235 #include "moc_fakeclient.cpp"