File indexing completed on 2024-06-16 05:01:54
0001 /* Copyright (C) 2006 - 2014 Jan Kundrát <jkt@flaska.net> 0002 0003 This file is part of the Trojita Qt IMAP e-mail client, 0004 http://trojita.flaska.net/ 0005 0006 This program is free software; you can redistribute it and/or 0007 modify it under the terms of the GNU General Public License as 0008 published by the Free Software Foundation; either version 2 of 0009 the License or (at your option) version 3 or any later version 0010 accepted by the membership of KDE e.V. (or its successor approved 0011 by the membership of KDE e.V.), which shall act as a proxy 0012 defined in Section 14 of version 3 of the license. 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 0017 GNU 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, see <http://www.gnu.org/licenses/>. 0021 */ 0022 0023 #include <QtTest> 0024 #include "test_Imap_Idle.h" 0025 #include "Streams/FakeSocket.h" 0026 #include "Imap/Tasks/IdleLauncher.h" 0027 #include "Imap/Model/ItemRoles.h" 0028 #include "Imap/Tasks/KeepMailboxOpenTask.h" 0029 #include "Utils/FakeCapabilitiesInjector.h" 0030 0031 /** @short Wait for the IDLE to arrive, or a timeout 0032 0033 The motivation here is to avoid being too fast, as reported in Redmine#275 0034 */ 0035 #define waitForIdle() \ 0036 { \ 0037 QTest::qWait(40); \ 0038 QByteArray written = SOCK->writtenStuff(); \ 0039 int times = 0; \ 0040 while (written.isEmpty() && times < 4) { \ 0041 QTest::qWait(5); \ 0042 written = SOCK->writtenStuff(); \ 0043 ++times; \ 0044 } \ 0045 QCOMPARE(written, t.mk("IDLE\r\n")); \ 0046 } 0047 0048 /** @short Test a NO reply to IDLE command */ 0049 void ImapModelIdleTest::testIdleNo() 0050 { 0051 model->setProperty("trojita-imap-idle-delayedEnter", QVariant(30)); 0052 FakeCapabilitiesInjector injector(model); 0053 injector.injectCapability(QStringLiteral("IDLE")); 0054 existsA = 3; 0055 uidValidityA = 6; 0056 uidMapA << 1 << 7 << 9; 0057 uidNextA = 16; 0058 helperSyncAWithMessagesEmptyState(); 0059 QVERIFY(SOCK->writtenStuff().isEmpty()); 0060 waitForIdle(); 0061 SOCK->fakeReading(t.last("NO you can't idle now\r\n")); 0062 QTest::qWait(40); 0063 QVERIFY(SOCK->writtenStuff().isEmpty()); 0064 helperSyncBNoMessages(); 0065 QVERIFY(errorSpy->isEmpty()); 0066 } 0067 0068 /** @short Test what happens when IDLE terminates by an OK, but without our "DONE" input */ 0069 void ImapModelIdleTest::testIdleImmediateReturn() 0070 { 0071 model->setProperty("trojita-imap-idle-delayedEnter", QVariant(30)); 0072 FakeCapabilitiesInjector injector(model); 0073 injector.injectCapability(QStringLiteral("IDLE")); 0074 existsA = 3; 0075 uidValidityA = 6; 0076 uidMapA << 1 << 7 << 9; 0077 uidNextA = 16; 0078 helperSyncAWithMessagesEmptyState(); 0079 QVERIFY(SOCK->writtenStuff().isEmpty()); 0080 waitForIdle(); 0081 SOCK->fakeReading(QByteArray("+ blah\r\n") + t.last("OK done\r\n")); 0082 waitForIdle(); 0083 } 0084 0085 /** @short Test automatic IDLE renewal */ 0086 void ImapModelIdleTest::testIdleRenewal() 0087 { 0088 model->setProperty("trojita-imap-idle-delayedEnter", QVariant(30)); 0089 model->setProperty("trojita-imap-idle-renewal", QVariant(10)); 0090 FakeCapabilitiesInjector injector(model); 0091 injector.injectCapability(QStringLiteral("IDLE")); 0092 existsA = 3; 0093 uidValidityA = 6; 0094 uidMapA << 1 << 7 << 9; 0095 uidNextA = 16; 0096 helperSyncAWithMessagesEmptyState(); 0097 QVERIFY(SOCK->writtenStuff().isEmpty()); 0098 waitForIdle(); 0099 SOCK->fakeReading(QByteArray("+ blah\r\n")); 0100 QTest::qWait(50); 0101 QCOMPARE( SOCK->writtenStuff(), QByteArray("DONE\r\n") ); 0102 SOCK->fakeReading(t.last("OK done\r\n")); 0103 waitForIdle(); 0104 } 0105 0106 /** @short Test that IDLE gets immediately interrupted by any Task */ 0107 void ImapModelIdleTest::testIdleBreakTask() 0108 { 0109 model->setProperty("trojita-imap-idle-delayedEnter", QVariant(30)); 0110 // Intentionally leave trojita-imap-idle-renewal at its rather high default value 0111 FakeCapabilitiesInjector injector(model); 0112 injector.injectCapability(QStringLiteral("IDLE")); 0113 existsA = 3; 0114 uidValidityA = 6; 0115 uidMapA << 1 << 7 << 9; 0116 uidNextA = 16; 0117 helperSyncAWithMessagesEmptyState(); 0118 QVERIFY(SOCK->writtenStuff().isEmpty()); 0119 waitForIdle(); 0120 SOCK->fakeReading(QByteArray("+ blah\r\n")); 0121 QCOMPARE( msgListA.model()->index(0, 0, msgListA).data(Imap::Mailbox::RoleMessageFrom).toString(), QString() ); 0122 QCoreApplication::processEvents(); 0123 QCoreApplication::processEvents(); 0124 QCoreApplication::processEvents(); 0125 QCoreApplication::processEvents(); 0126 QCOMPARE(SOCK->writtenStuff(), QByteArray(QByteArray("DONE\r\n") + t.mk("UID FETCH 1,7,9 (" FETCH_METADATA_ITEMS ")\r\n"))); 0127 SOCK->fakeReading(t.last("OK done\r\n")); 0128 QTest::qWait(40); 0129 QVERIFY(SOCK->writtenStuff().isEmpty()); 0130 } 0131 0132 /** @short Test automatic IDLE renewal when server gets really slow to respond */ 0133 void ImapModelIdleTest::testIdleSlowResponses() 0134 { 0135 model->setProperty("trojita-imap-idle-delayedEnter", QVariant(30)); 0136 model->setProperty("trojita-imap-idle-renewal", QVariant(10)); 0137 FakeCapabilitiesInjector injector(model); 0138 injector.injectCapability(QStringLiteral("IDLE")); 0139 existsA = 3; 0140 uidValidityA = 6; 0141 uidMapA << 1 << 7 << 9; 0142 uidNextA = 16; 0143 helperSyncAWithMessagesEmptyState(); 0144 QVERIFY(SOCK->writtenStuff().isEmpty()); 0145 0146 waitForIdle(); 0147 // Check what happens if it takes the server a lot of time to issue the initial continuation 0148 QTest::qWait(70); 0149 SOCK->fakeReading(QByteArray("+ blah\r\n")); 0150 QCoreApplication::processEvents(); 0151 QCoreApplication::processEvents(); 0152 QCOMPARE( SOCK->writtenStuff(), QByteArray("DONE\r\n") ); 0153 SOCK->fakeReading(t.last("OK done\r\n")); 0154 0155 waitForIdle(); 0156 SOCK->fakeReading(QByteArray("+ blah\r\n")); 0157 // The client is fast enough... 0158 QTest::qWait(40); 0159 QCOMPARE( SOCK->writtenStuff(), QByteArray("DONE\r\n") ); 0160 // ...but the server is taking its time 0161 QTest::qWait(70); 0162 QVERIFY(SOCK->writtenStuff().isEmpty()); 0163 SOCK->fakeReading(t.last("OK done\r\n")); 0164 QCoreApplication::processEvents(); 0165 QCoreApplication::processEvents(); 0166 QVERIFY(SOCK->writtenStuff().isEmpty()); 0167 //QCOMPARE( SOCK->writtenStuff(), t.mk("DONE\r\n") ); 0168 } 0169 0170 /** @short Test that the automatic IDLE renewal gets disabled when IDLE finishes */ 0171 void ImapModelIdleTest::testIdleNoPerpetuateRenewal() 0172 { 0173 // we shouldn't enter IDLE automatically 0174 model->setProperty("trojita-imap-idle-delayedEnter", QVariant(1000 * 1000 )); 0175 model->setProperty("trojita-imap-idle-renewal", QVariant(10)); 0176 FakeCapabilitiesInjector injector(model); 0177 injector.injectCapability(QStringLiteral("IDLE")); 0178 existsA = 3; 0179 uidValidityA = 6; 0180 uidMapA << 1 << 7 << 9; 0181 uidNextA = 16; 0182 helperSyncAWithMessagesEmptyState(); 0183 QVERIFY(SOCK->writtenStuff().isEmpty()); 0184 0185 // Force manual trigger of the IDLE 0186 model->findTaskResponsibleFor(idxA)->idleLauncher->slotEnterIdleNow(); 0187 QCoreApplication::processEvents(); 0188 QCoreApplication::processEvents(); 0189 // we check it immediately, ie. no via the waitForIdle() 0190 QCOMPARE( SOCK->writtenStuff(), t.mk("IDLE\r\n") ); 0191 SOCK->fakeReading(t.last("NO you can't idle now\r\n")); 0192 // ...make sure it won't try to "break long IDLE" 0193 QTest::qWait(30); 0194 // switch away 0195 helperSyncBNoMessages(); 0196 QVERIFY(errorSpy->isEmpty()); 0197 0198 // Now go back to mailbox A 0199 model->switchToMailbox(idxA); 0200 helperSyncAWithMessagesNoArrivals(); 0201 0202 // Force manual trigger of the IDLE 0203 model->findTaskResponsibleFor(idxA)->idleLauncher->slotEnterIdleNow(); 0204 QCoreApplication::processEvents(); 0205 QCoreApplication::processEvents(); 0206 QCOMPARE( SOCK->writtenStuff(), t.mk("IDLE\r\n") ); 0207 SOCK->fakeReading(QByteArray("+ blah\r\n")); 0208 QCoreApplication::processEvents(); 0209 QCoreApplication::processEvents(); 0210 0211 // so we're in regular IDLE and want to break it 0212 QCOMPARE( msgListA.model()->index(0, 0, msgListA).data(Imap::Mailbox::RoleMessageFrom).toString(), QString() ); 0213 QCoreApplication::processEvents(); 0214 QCoreApplication::processEvents(); 0215 QCoreApplication::processEvents(); 0216 QCoreApplication::processEvents(); 0217 QCOMPARE(SOCK->writtenStuff(), QByteArray(QByteArray("DONE\r\n") + t.mk("UID FETCH 1,7,9 (" FETCH_METADATA_ITEMS ")\r\n"))); 0218 SOCK->fakeReading(t.last("OK done\r\n")); 0219 // Make sure we won't try to "renew" it automatically... 0220 QTest::qWait(30); 0221 QVERIFY(SOCK->writtenStuff().isEmpty()); 0222 } 0223 0224 0225 /** @short Test that the tagged OK for terminating IDLE gets correctly handled when changing mailboxes */ 0226 void ImapModelIdleTest::testIdleMailboxChange() 0227 { 0228 // we shouldn't enter IDLE automatically 0229 model->setProperty("trojita-imap-idle-delayedEnter", QVariant(1000 * 1000 )); 0230 FakeCapabilitiesInjector injector(model); 0231 injector.injectCapability(QStringLiteral("IDLE")); 0232 existsA = 3; 0233 uidValidityA = 6; 0234 uidMapA << 1 << 7 << 9; 0235 uidNextA = 16; 0236 helperSyncAWithMessagesEmptyState(); 0237 QVERIFY(SOCK->writtenStuff().isEmpty()); 0238 0239 // Force manual trigger of the IDLE 0240 model->findTaskResponsibleFor(idxA)->idleLauncher->slotEnterIdleNow(); 0241 QCoreApplication::processEvents(); 0242 QCoreApplication::processEvents(); 0243 // we check it immediately, ie. no via the waitForIdle() 0244 QCOMPARE( SOCK->writtenStuff(), t.mk("IDLE\r\n") ); 0245 SOCK->fakeReading("+ idling\r\n"); 0246 // save the IDLE termination response for later 0247 QByteArray respIdleDone = t.last("OK idle terminated\r\n"); 0248 QCoreApplication::processEvents(); 0249 QCoreApplication::processEvents(); 0250 0251 // Now switch away 0252 // can't use the helperSyncBNoMessages() as we do have to check the IDLE explicitly here 0253 model->switchToMailbox( idxB ); 0254 QCoreApplication::processEvents(); 0255 QCoreApplication::processEvents(); 0256 QCOMPARE(SOCK->writtenStuff(), QByteArray("DONE\r\n")); 0257 // be sure that we wait for the tagged termination of the IDLE command 0258 for (int i = 0; i < 100; ++i) { 0259 QCoreApplication::processEvents(); 0260 } 0261 SOCK->fakeReading(respIdleDone); 0262 QCoreApplication::processEvents(); 0263 QCoreApplication::processEvents(); 0264 QCoreApplication::processEvents(); 0265 QCoreApplication::processEvents(); 0266 QCOMPARE(SOCK->writtenStuff(), t.mk("SELECT b\r\n")); 0267 SOCK->fakeReading(QByteArray("* 0 exists\r\n") 0268 + t.last("ok completed\r\n")); 0269 QCoreApplication::processEvents(); 0270 QCoreApplication::processEvents(); 0271 QCoreApplication::processEvents(); 0272 QCoreApplication::processEvents(); 0273 0274 QVERIFY(errorSpy->isEmpty()); 0275 0276 QTest::qWait(30); 0277 QVERIFY(SOCK->writtenStuff().isEmpty()); 0278 } 0279 0280 0281 QTEST_GUILESS_MAIN( ImapModelIdleTest )