File indexing completed on 2024-05-12 05:17:12

0001 /*
0002     This file is part of the KDE project
0003     SPDX-FileCopyrightText: 2008 Kevin Ottens <ervin@kde.org>
0004 
0005     SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
0006     SPDX-FileContributor: Kevin Ottens <kevin@kdab.com>
0007 
0008     SPDX-License-Identifier: LGPL-2.0-or-later
0009   */
0010 
0011 #include <QEventLoop>
0012 #include <QObject>
0013 #include <QSignalSpy>
0014 #include <QTest>
0015 
0016 #include "job.h"
0017 #include "kimaptest/fakeserver.h"
0018 #include "kimaptest/mockjob.h"
0019 #include "session.h"
0020 
0021 Q_DECLARE_METATYPE(KIMAP::Session::State)
0022 Q_DECLARE_METATYPE(KJob *)
0023 
0024 class SessionTest : public QObject
0025 {
0026     Q_OBJECT
0027 
0028 private Q_SLOTS:
0029 
0030     void initTestCase()
0031     {
0032         qRegisterMetaType<KIMAP::Session::State>();
0033     }
0034 
0035     void shouldStartDisconnected()
0036     {
0037         FakeServer fakeServer;
0038         fakeServer.setScenario(QList<QByteArray>() << FakeServer::greeting());
0039         fakeServer.startAndWait();
0040         KIMAP::Session s(QStringLiteral("127.0.0.1"), 5989);
0041         QSignalSpy spy(&s, &KIMAP::Session::stateChanged);
0042         QCOMPARE((int)s.state(), (int)KIMAP::Session::Disconnected);
0043         QTest::qWait(600);
0044         QCOMPARE((int)s.state(), (int)KIMAP::Session::NotAuthenticated);
0045         QCOMPARE(spy.count(), 1); // NotAuthenticated
0046         QList<QVariant> arguments = spy.takeFirst();
0047         QCOMPARE((int)qvariant_cast<KIMAP::Session::State>(arguments.at(0)), (int)KIMAP::Session::NotAuthenticated);
0048         QCOMPARE((int)qvariant_cast<KIMAP::Session::State>(arguments.at(1)), (int)KIMAP::Session::Disconnected);
0049     }
0050 
0051     void shouldFailForInvalidHosts()
0052     {
0053         KIMAP::Session s(QStringLiteral("0.0.0.0"), 1234);
0054         s.setTimeout(1); // 1 second timeout
0055 
0056         QSignalSpy spyFail(&s, &KIMAP::Session::connectionFailed);
0057         QSignalSpy spyLost(&s, &KIMAP::Session::connectionLost);
0058         QSignalSpy spyState(&s, &KIMAP::Session::stateChanged);
0059 
0060         QCOMPARE((int)s.state(), (int)KIMAP::Session::Disconnected);
0061 
0062         QTest::qWait(500);
0063         QCOMPARE((int)s.state(), (int)KIMAP::Session::Disconnected);
0064         QCOMPARE(spyFail.count(), 1);
0065         QCOMPARE(spyLost.count(), 0);
0066         QCOMPARE(spyState.count(), 0);
0067 
0068         // Wait 800ms more. So now it's 1.3 seconds, check that the socket timeout has correctly been
0069         // disabled, and that it hadn't fired unexpectedly.
0070         QTest::qWait(800);
0071         QCOMPARE(spyFail.count(), 1);
0072     }
0073 
0074     /**
0075       Checks that the timeout works when the connection succeeds, but the server doesn't sends anything
0076       back to the client. This could happen for example if we connected to a non-IMAP server.
0077     */
0078     void shouldTimeoutOnNoGreeting()
0079     {
0080         FakeServer fakeServer;
0081         fakeServer.setScenario(QList<QByteArray>());
0082         fakeServer.startAndWait();
0083 
0084         KIMAP::Session s(QStringLiteral("127.0.0.1"), 5989);
0085         s.setTimeout(2);
0086         QSignalSpy spyFail(&s, &KIMAP::Session::connectionFailed);
0087         QSignalSpy spyLost(&s, &KIMAP::Session::connectionLost);
0088         QSignalSpy spyState(&s, &KIMAP::Session::stateChanged);
0089         QCOMPARE((int)s.state(), (int)KIMAP::Session::Disconnected);
0090 
0091         // Wait 1.8 second. Since the timeout is set to 2 seconds, the socket should be still
0092         // disconnected at this point, yet the connectionFailed() signal shouldn't have been emitted.
0093         QTest::qWait(1800);
0094         QCOMPARE((int)s.state(), (int)KIMAP::Session::Disconnected);
0095         QCOMPARE(spyFail.count(), 0);
0096         QCOMPARE(spyLost.count(), 0);
0097         QCOMPARE(spyState.count(), 0);
0098 
0099         // Wait 0.5 second more. Now we are at 2.3 seconds, the socket should have timed out, and the
0100         // connectionFailed() signal should have been emitted.
0101         QTest::qWait(500);
0102         QCOMPARE((int)s.state(), (int)KIMAP::Session::Disconnected);
0103         QCOMPARE(spyFail.count(), 0);
0104         QCOMPARE(spyLost.count(), 1);
0105         QCOMPARE(spyState.count(), 0);
0106     }
0107 
0108     void shouldSupportPreauth()
0109     {
0110         FakeServer fakeServer;
0111         fakeServer.setScenario(QList<QByteArray>() << FakeServer::preauth());
0112         fakeServer.startAndWait();
0113 
0114         KIMAP::Session s(QStringLiteral("127.0.0.1"), 5989);
0115         QSignalSpy spy(&s, &KIMAP::Session::stateChanged);
0116         QCOMPARE((int)s.state(), (int)KIMAP::Session::Disconnected);
0117         QTest::qWait(500);
0118         QCOMPARE((int)s.state(), (int)KIMAP::Session::Authenticated);
0119         QCOMPARE(spy.count(), 1); // Authenticated
0120         QList<QVariant> arguments = spy.takeFirst();
0121         QCOMPARE((int)qvariant_cast<KIMAP::Session::State>(arguments.at(0)), (int)KIMAP::Session::Authenticated);
0122         QCOMPARE((int)qvariant_cast<KIMAP::Session::State>(arguments.at(1)), (int)KIMAP::Session::Disconnected);
0123     }
0124 
0125     void shouldRespectStartOrder()
0126     {
0127         FakeServer fakeServer;
0128         fakeServer.setScenario(QList<QByteArray>() << FakeServer::greeting());
0129         fakeServer.startAndWait();
0130 
0131         KIMAP::Session s(QStringLiteral("127.0.0.1"), 5989);
0132         auto j1 = new MockJob(&s);
0133         connect(j1, &KJob::result, this, &SessionTest::jobDone);
0134         auto j2 = new MockJob(&s);
0135         connect(j2, &KJob::result, this, &SessionTest::jobDone);
0136         auto j3 = new MockJob(&s);
0137         connect(j3, &KJob::result, this, &SessionTest::jobDone);
0138         auto j4 = new MockJob(&s);
0139         connect(j4, &KJob::result, this, &SessionTest::jobDone);
0140 
0141         j4->start();
0142         j2->start();
0143         j3->start();
0144         j1->start();
0145 
0146         m_expectedCalls = 4;
0147         m_eventLoop.exec();
0148 
0149         QCOMPARE(m_jobs.size(), 4);
0150         QCOMPARE(m_jobs[0], j4);
0151         QCOMPARE(m_jobs[1], j2);
0152         QCOMPARE(m_jobs[2], j3);
0153         QCOMPARE(m_jobs[3], j1);
0154     }
0155 
0156     void shouldManageQueueSize()
0157     {
0158         FakeServer fakeServer;
0159         fakeServer.setScenario(QList<QByteArray>() << FakeServer::greeting());
0160         fakeServer.startAndWait();
0161 
0162         KIMAP::Session s(QStringLiteral("127.0.0.1"), 5989);
0163 
0164         QSignalSpy queueSpy(&s, &KIMAP::Session::jobQueueSizeChanged);
0165 
0166         QCOMPARE(s.jobQueueSize(), 0);
0167 
0168         auto j1 = new MockJob(&s);
0169         auto j2 = new MockJob(&s);
0170         auto j3 = new MockJob(&s);
0171         auto j4 = new MockJob(&s);
0172         connect(j4, &KJob::result, &m_eventLoop, &QEventLoop::quit);
0173 
0174         QCOMPARE(s.jobQueueSize(), 0);
0175 
0176         j1->start();
0177         QCOMPARE(s.jobQueueSize(), 1);
0178         QCOMPARE(queueSpy.size(), 1);
0179         QCOMPARE(queueSpy.at(0).at(0).toInt(), 1);
0180 
0181         j2->start();
0182         QCOMPARE(s.jobQueueSize(), 2);
0183         QCOMPARE(queueSpy.size(), 2);
0184         QCOMPARE(queueSpy.at(1).at(0).toInt(), 2);
0185 
0186         j3->start();
0187         QCOMPARE(s.jobQueueSize(), 3);
0188         QCOMPARE(queueSpy.size(), 3);
0189         QCOMPARE(queueSpy.at(2).at(0).toInt(), 3);
0190 
0191         j4->start();
0192         QCOMPARE(s.jobQueueSize(), 4);
0193         QCOMPARE(queueSpy.size(), 4);
0194         QCOMPARE(queueSpy.at(3).at(0).toInt(), 4);
0195 
0196         queueSpy.clear();
0197         m_eventLoop.exec();
0198 
0199         QCOMPARE(s.jobQueueSize(), 0);
0200 
0201         QCOMPARE(queueSpy.at(0).at(0).toInt(), 3);
0202         QCOMPARE(queueSpy.at(1).at(0).toInt(), 2);
0203         QCOMPARE(queueSpy.at(2).at(0).toInt(), 1);
0204         QCOMPARE(queueSpy.at(3).at(0).toInt(), 0);
0205     }
0206 
0207     void shouldTimeoutOnNoReply()
0208     {
0209         FakeServer fakeServer;
0210         fakeServer.setScenario(QList<QByteArray>() << FakeServer::preauth() << "C: A000001 DUMMY"
0211                                                    << "S: * DUMMY"
0212                                                    << "S: * DUMMY"
0213                                                    << "S: * DUMMY"
0214                                                    << "S: * DUMMY"
0215                                                    << "S: * DUMMY"
0216                                                    << "S: * DUMMY"
0217                                                    << "S: * DUMMY"
0218                                                    << "S: * DUMMY"
0219                                                    << "S: * DUMMY"
0220                                                    << "S: * DUMMY"
0221                                                    << "S: * DUMMY"
0222                                                    << "S: * DUMMY"
0223                                                    << "S: * DUMMY"
0224                                // We never get a OK or anything, so the job can't normally complete
0225         );
0226         fakeServer.startAndWait();
0227 
0228         KIMAP::Session s(QStringLiteral("127.0.0.1"), 5989);
0229 
0230         QSignalSpy spyFail(&s, &KIMAP::Session::connectionFailed);
0231         QSignalSpy spyLost(&s, &KIMAP::Session::connectionLost);
0232         QSignalSpy spyState(&s, &KIMAP::Session::stateChanged);
0233 
0234         auto mock = new MockJob(&s);
0235         mock->setCommand("DUMMY");
0236 
0237         mock->exec();
0238         // We expect to get an error here due to some timeout
0239         QVERIFY(mock->error() != 0);
0240         QCOMPARE(spyFail.count(), 0);
0241         QCOMPARE(spyLost.count(), 1);
0242         QCOMPARE(spyState.count(), 2); // Authenticated, Disconnected
0243     }
0244 
0245     void shouldFailFirstJobOnConnectionFailed()
0246     {
0247         qRegisterMetaType<KJob *>();
0248 
0249         FakeServer fakeServer;
0250         fakeServer.setScenario(QList<QByteArray>());
0251         fakeServer.startAndWait();
0252 
0253         KIMAP::Session s(QStringLiteral("127.0.0.1"), 5989);
0254         s.setTimeout(1);
0255 
0256         auto j1 = new MockJob(&s);
0257         QSignalSpy spyResult1(j1, &KJob::result);
0258         QSignalSpy spyDestroyed1(j1, &QObject::destroyed);
0259 
0260         auto j2 = new MockJob(&s);
0261         QSignalSpy spyResult2(j2, &KJob::result);
0262         QSignalSpy spyDestroyed2(j2, &QObject::destroyed);
0263 
0264         auto j3 = new MockJob(&s);
0265         QSignalSpy spyResult3(j3, &KJob::result);
0266         QSignalSpy spyDestroyed3(j3, &QObject::destroyed);
0267 
0268         j1->start();
0269         j2->start();
0270         j3->start();
0271 
0272         QCOMPARE(s.jobQueueSize(), 3);
0273 
0274         QTest::qWait(1100);
0275 
0276         // Check that only the first job has emitted it's result
0277         QCOMPARE(spyResult1.count(), 1);
0278         QCOMPARE(spyResult2.count(), 0);
0279         QCOMPARE(spyResult3.count(), 0);
0280 
0281         // Check that all jobs have been deleted
0282         QCOMPARE(spyDestroyed1.count(), 1);
0283         QCOMPARE(spyDestroyed2.count(), 1);
0284         QCOMPARE(spyDestroyed3.count(), 1);
0285 
0286         QCOMPARE(s.jobQueueSize(), 0);
0287     }
0288 
0289     void shouldCloseOnInconsistency()
0290     {
0291         for (int count = 0; count < 10; count++) {
0292             FakeServer fakeServer;
0293             fakeServer.setScenario(QList<QByteArray>() << FakeServer::preauth() << "C: A000001 DUMMY"
0294                                                        << "S: * DUMMY %"
0295                                                        << "S: DUMMY)");
0296             fakeServer.startAndWait();
0297 
0298             KIMAP::Session s(QStringLiteral("127.0.0.1"), 5989);
0299 
0300             QSignalSpy spyFail(&s, &KIMAP::Session::connectionFailed);
0301             QSignalSpy spyLost(&s, &KIMAP::Session::connectionLost);
0302             QSignalSpy spyState(&s, &KIMAP::Session::stateChanged);
0303 
0304             auto mock = new MockJob(&s);
0305             mock->setTimeout(5000);
0306             mock->setCommand("DUMMY");
0307 
0308             mock->setAutoDelete(false);
0309             mock->start();
0310             QTest::qWait(250); // Should be plenty
0311 
0312             // We expect to get an error here due to the inconsistency
0313             QVERIFY(mock->error() != 0);
0314             QCOMPARE(spyFail.count(), 0);
0315             QCOMPARE(spyLost.count(), 1);
0316             QCOMPARE(spyState.count(), 2); // Authenticated, Disconnected
0317 
0318             delete mock;
0319         }
0320     }
0321 
0322 public Q_SLOTS:
0323     void jobDone(KJob *job)
0324     {
0325         m_jobs << job;
0326 
0327         if (m_expectedCalls == m_jobs.size()) {
0328             m_eventLoop.quit();
0329         }
0330     }
0331 
0332 private:
0333     QEventLoop m_eventLoop;
0334     int m_expectedCalls;
0335     QList<KJob *> m_jobs;
0336 };
0337 
0338 QTEST_GUILESS_MAIN(SessionTest)
0339 
0340 #include "testsession.moc"