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

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