File indexing completed on 2024-11-24 04:44:01

0001 /*
0002    SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
0003    SPDX-FileContributor: Kevin Ottens <kevin@kdab.com>
0004 
0005    SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #include "imaptestbase.h"
0009 
0010 #include "highestmodseqattribute.h"
0011 #include "retrieveitemstask.h"
0012 #include "uidnextattribute.h"
0013 #include "uidvalidityattribute.h"
0014 
0015 #include <Akonadi/MessageParts>
0016 
0017 #include <Akonadi/CachePolicy>
0018 #include <Akonadi/CollectionStatistics>
0019 
0020 #include <QTest>
0021 
0022 class TestRetrieveItemsTask : public ImapTestBase
0023 {
0024     Q_OBJECT
0025 
0026 private Q_SLOTS:
0027     void shouldIntrospectCollection_data()
0028     {
0029         QTest::addColumn<Akonadi::Collection>("collection");
0030         QTest::addColumn<QList<QByteArray>>("scenario");
0031         QTest::addColumn<QStringList>("callNames");
0032 
0033         Akonadi::Collection collection;
0034         QList<QByteArray> scenario;
0035         QStringList callNames;
0036 
0037         collection = createCollectionChain(QStringLiteral("/INBOX/Foo"));
0038         collection.attribute<UidValidityAttribute>(Akonadi::Collection::AddIfMissing)->setUidValidity(1149151135);
0039 
0040         scenario.clear();
0041         scenario << defaultPoolConnectionScenario() << "C: A000003 SELECT \"INBOX/Foo\""
0042                  << "S: A000003 OK select done"
0043                  << "C: A000004 EXPUNGE"
0044                  << "S: A000004 OK expunge done"
0045                  << "C: A000005 SELECT \"INBOX/Foo\""
0046                  << R"(S: * FLAGS (\Answered \Flagged \Draft \Deleted \Seen))"
0047                  << R"(S: * OK [ PERMANENTFLAGS (\Answered \Flagged \Draft \Deleted \Seen) ])"
0048                  << "S: * 1 EXISTS"
0049                  << "S: * 0 RECENT"
0050                  << "S: * OK [ UIDVALIDITY 1149151135  ]"
0051                  << "S: * OK [ UIDNEXT 9  ]"
0052                  << "S: A000005 OK select done"
0053                  << "C: A000006 UID SEARCH UID 1:9"
0054                  << "S: * SEARCH 1 2 3 4 5 6 7 8 9"
0055                  << "S: A000006 OK search done"
0056                  << "C: A000007 UID FETCH 1:9 (RFC822.SIZE INTERNALDATE "
0057                     "BODY.PEEK[HEADER] "
0058                     "FLAGS UID)"
0059                  << "S: * 1 FETCH ( FLAGS (\\Seen) UID 7 INTERNALDATE \"29-Jun-2010 15:26:42 +0200\" "
0060                     "RFC822.SIZE 75 BODY[HEADER] {69}\r\n"
0061                     "From: Foo <foo@kde.org>\r\n"
0062                     "To: Bar <bar@kde.org>\r\n"
0063                     "Subject: Test Mail\r\n"
0064                     "\r\n"
0065                     " )"
0066                  << "S: A000007 OK fetch done";
0067 
0068         callNames.clear();
0069         callNames << QStringLiteral("itemsRetrieved") << QStringLiteral("applyCollectionChanges") << QStringLiteral("itemsRetrievalDone");
0070 
0071         QTest::newRow("first listing, connected IMAP") << collection << scenario << callNames;
0072 
0073         scenario.clear();
0074         scenario << defaultPoolConnectionScenario() << "C: A000003 SELECT \"INBOX/Foo\""
0075                  << R"(S: * FLAGS (\Answered \Flagged \Draft \Deleted \Seen))"
0076                  << R"(S: * OK [ PERMANENTFLAGS (\Answered \Flagged \Draft \Deleted \Seen) ])"
0077                  << "S: * 1 EXISTS"
0078                  << "S: * 0 RECENT"
0079                  << "S: * OK [ UIDVALIDITY 1149151135  ]"
0080                  << "S: * OK [ UIDNEXT 9  ]"
0081                  << "S: A000003 OK [READ-ONLY] select done"
0082                  << "C: A000004 UID SEARCH UID 1:9"
0083                  << "S: * SEARCH 1 2 3 4 5 6 7 8 9"
0084                  << "S: A000004 OK search done"
0085                  << "C: A000005 UID FETCH 1:9 (RFC822.SIZE INTERNALDATE "
0086                     "BODY.PEEK[HEADER] "
0087                     "FLAGS UID)"
0088                  << "S: * 1 FETCH ( FLAGS (\\Seen) UID 7 INTERNALDATE \"29-Jun-2010 15:26:42 +0200\" "
0089                     "RFC822.SIZE 75 BODY[HEADER] {69}\r\n"
0090                     "From: Foo <foo@kde.org>\r\n"
0091                     "To: Bar <bar@kde.org>\r\n"
0092                     "Subject: Test Mail\r\n"
0093                     "\r\n"
0094                     " )"
0095                  << "S: A000005 OK fetch done";
0096         callNames.clear();
0097         callNames << QStringLiteral("itemsRetrieved") << QStringLiteral("applyCollectionChanges") << QStringLiteral("itemsRetrievalDone");
0098 
0099         QTest::newRow("retrieval from read-only mailbox (no expunge)") << collection << scenario << callNames;
0100 
0101         Akonadi::CachePolicy policy;
0102         policy.setLocalParts(QStringList() << QLatin1StringView(Akonadi::MessagePart::Envelope) << QLatin1StringView(Akonadi::MessagePart::Header)
0103                                            << QLatin1StringView(Akonadi::MessagePart::Body));
0104 
0105         collection = createCollectionChain(QStringLiteral("/INBOX/Foo"));
0106         collection.attribute<UidValidityAttribute>(Akonadi::Collection::AddIfMissing)->setUidValidity(1149151135);
0107         collection.setCachePolicy(policy);
0108 
0109         scenario.clear();
0110         scenario << defaultPoolConnectionScenario() << "C: A000003 SELECT \"INBOX/Foo\""
0111                  << "S: A000003 OK select done"
0112                  << "C: A000004 EXPUNGE"
0113                  << "S: A000004 OK expunge done"
0114                  << "C: A000005 SELECT \"INBOX/Foo\""
0115                  << R"(S: * FLAGS (\Answered \Flagged \Draft \Deleted \Seen))"
0116                  << R"(S: * OK [ PERMANENTFLAGS (\Answered \Flagged \Draft \Deleted \Seen) ])"
0117                  << "S: * 1 EXISTS"
0118                  << "S: * 0 RECENT"
0119                  << "S: * OK [ UIDVALIDITY 1149151135  ]"
0120                  << "S: * OK [ UIDNEXT 9  ]"
0121                  << "S: A000005 OK select done"
0122                  << "C: A000006 UID SEARCH UID 1:9"
0123                  << "S: * SEARCH 1 2 3 4 5 6 7 8 9"
0124                  << "S: A000006 OK search done"
0125                  << "C: A000007 UID FETCH 1:9 (RFC822.SIZE INTERNALDATE BODY.PEEK[] FLAGS UID)"
0126                  << "S: * 1 FETCH ( FLAGS (\\Seen) UID 7 INTERNALDATE \"29-Jun-2010 15:26:42 +0200\" "
0127                     "RFC822.SIZE 75 BODY[] {75}\r\n"
0128                     "From: Foo <foo@kde.org>\r\n"
0129                     "To: Bar <bar@kde.org>\r\n"
0130                     "Subject: Test Mail\r\n"
0131                     "\r\n"
0132                     "Test\r\n"
0133                     " )"
0134                  << "S: A000007 OK fetch done";
0135 
0136         callNames.clear();
0137         callNames << QStringLiteral("itemsRetrieved") << QStringLiteral("applyCollectionChanges") << QStringLiteral("itemsRetrievalDone");
0138 
0139         QTest::newRow("first listing, disconnected IMAP") << collection << scenario << callNames;
0140 
0141         Akonadi::CollectionStatistics stats;
0142         stats.setCount(1);
0143 
0144         collection = createCollectionChain(QStringLiteral("/INBOX/Foo"));
0145         collection.attribute<UidValidityAttribute>(Akonadi::Collection::AddIfMissing)->setUidValidity(1149151135);
0146         collection.attribute<UidNextAttribute>(Akonadi::Collection::AddIfMissing)->setUidNext(9);
0147         collection.setCachePolicy(policy);
0148         collection.setStatistics(stats);
0149 
0150         scenario.clear();
0151         scenario << defaultPoolConnectionScenario() << "C: A000003 SELECT \"INBOX/Foo\""
0152                  << "S: A000003 OK select done"
0153                  << "C: A000004 EXPUNGE"
0154                  << "S: A000004 OK expunge done"
0155                  << "C: A000005 SELECT \"INBOX/Foo\""
0156                  << R"(S: * FLAGS (\Answered \Flagged \Draft \Deleted \Seen))"
0157                  << R"(S: * OK [ PERMANENTFLAGS (\Answered \Flagged \Draft \Deleted \Seen) ])"
0158                  << "S: * 1 EXISTS"
0159                  << "S: * 0 RECENT"
0160                  << "S: * OK [ UIDVALIDITY 1149151135  ]"
0161                  << "S: * OK [ UIDNEXT 9  ]"
0162                  << "S: A000005 OK select done"
0163                  << "C: A000006 UID SEARCH UID 1:9"
0164                  << "S: * SEARCH 1 2 3 4 5 6 7 8 9"
0165                  << "S: A000006 OK search done"
0166                  << "C: A000007 UID FETCH 1:9 (FLAGS UID)"
0167                  << "S: * 1 FETCH ( FLAGS (\\Seen) UID 7 )"
0168                  << "S: A000007 OK fetch done";
0169 
0170         callNames.clear();
0171         callNames << QStringLiteral("itemsRetrievedIncremental") << QStringLiteral("applyCollectionChanges") << QStringLiteral("itemsRetrievedIncremental")
0172                   << QStringLiteral("itemsRetrievalDone");
0173 
0174         // Disabled test since the flag sync is disabled if CONDSTORE is not supported
0175         //     QTest::newRow( "second listing, checking for flag changes" ) << collection << scenario << callNames;
0176 
0177         collection = createCollectionChain(QStringLiteral("/INBOX/Foo"));
0178         collection.attribute<UidValidityAttribute>(Akonadi::Collection::AddIfMissing)->setUidValidity(1149151135);
0179         collection.setCachePolicy(policy);
0180         stats.setCount(1);
0181         collection.setStatistics(stats);
0182         scenario.clear();
0183         scenario << defaultPoolConnectionScenario() << "C: A000003 SELECT \"INBOX/Foo\""
0184                  << "S: A000003 OK select done"
0185                  << "C: A000004 EXPUNGE"
0186                  << "S: A000004 OK expunge done"
0187                  << "C: A000005 SELECT \"INBOX/Foo\""
0188                  << R"(S: * FLAGS (\Answered \Flagged \Draft \Deleted \Seen))"
0189                  << R"(S: * OK [ PERMANENTFLAGS (\Answered \Flagged \Draft \Deleted \Seen) ])"
0190                  << "S: * 0 EXISTS"
0191                  << "S: * 0 RECENT"
0192                  << "S: * OK [ UIDVALIDITY 1149151135  ]"
0193                  << "S: * OK [ UIDNEXT 9  ]"
0194                  << "S: A000005 OK select done";
0195 
0196         callNames.clear();
0197         callNames << QStringLiteral("itemsRetrieved") << QStringLiteral("applyCollectionChanges") << QStringLiteral("itemsRetrievalDone");
0198 
0199         QTest::newRow("third listing, full sync, empty folder") << collection << scenario << callNames;
0200 
0201         collection.attribute<UidNextAttribute>(Akonadi::Collection::AddIfMissing)->setUidNext(8);
0202         stats.setCount(4);
0203         collection.setStatistics(stats);
0204         collection.attribute<HighestModSeqAttribute>(Akonadi::Collection::AddIfMissing)->setHighestModSeq(123456788);
0205         scenario.clear();
0206         scenario << defaultPoolConnectionScenario() << "C: A000003 SELECT \"INBOX/Foo\""
0207                  << "S: A000003 OK select done"
0208                  << "C: A000004 EXPUNGE"
0209                  << "S: A000004 OK expunge done"
0210                  << "C: A000005 SELECT \"INBOX/Foo\""
0211                  << R"(S: * FLAGS (\Answered \Flagged \Draft \Deleted \Seen))"
0212                  << R"(S: * OK [ PERMANENTFLAGS (\Answered \Flagged \Draft \Deleted \Seen) ])"
0213                  << "S: * 5 EXISTS"
0214                  << "S: * 0 RECENT"
0215                  << "S: * OK [ UIDVALIDITY 1149151135  ]"
0216                  << "S: * OK [ UIDNEXT 9  ]"
0217                  << "S: * OK [ HIGHESTMODSEQ 123456789 ]"
0218                  << "S: A000005 OK select done"
0219                  << "C: A000006 UID SEARCH UID 8:9"
0220                  << "S: * SEARCH 8 9"
0221                  << "S: A000006 OK search done"
0222                  << "C: A000007 UID FETCH 8:9 (RFC822.SIZE INTERNALDATE BODY.PEEK[] FLAGS UID)"
0223                  << "S: * 5 FETCH ( FLAGS (\\Seen) UID 9 INTERNALDATE \"29-Jun-2010 15:26:42 +0200\" "
0224                     "RFC822.SIZE 75 BODY[] {75}\r\n"
0225                     "From: Foo <foo@kde.org>\r\n"
0226                     "To: Bar <bar@kde.org>\r\n"
0227                     "Subject: Test Mail\r\n"
0228                     "\r\n"
0229                     "Test\r\n"
0230                     " )"
0231                  << "S: A000007 OK fetch done"
0232                  << "C: A000008 UID SEARCH UID 1:7"
0233                  << "S: * SEARCH 1 2 3 4 5 6 7"
0234                  << "S: A000008 OK search done"
0235                  << "C: A000009 UID FETCH 1:7 (FLAGS UID)"
0236                  << "S: * 1 FETCH"
0237                  << "S: * 2 FETCH"
0238                  << "S: * 3 FETCH"
0239                  << "S: * 4 FETCH"
0240                  << "S: A000009 OK fetch done";
0241 
0242         callNames.clear();
0243         callNames << QStringLiteral("itemsRetrievedIncremental") << QStringLiteral("applyCollectionChanges") << QStringLiteral("itemsRetrievedIncremental")
0244                   << QStringLiteral("itemsRetrievalDone");
0245 
0246         // We know no messages have been removed, so we can do an incremental update
0247         QTest::newRow("uidnext changed, fetch new messages incrementally") << collection << scenario << callNames;
0248 
0249         collection.attribute<UidNextAttribute>(Akonadi::Collection::AddIfMissing)->setUidNext(8);
0250         stats.setCount(5);
0251         collection.setStatistics(stats);
0252         scenario.clear();
0253         scenario << defaultPoolConnectionScenario() << "C: A000003 SELECT \"INBOX/Foo\""
0254                  << "S: A000003 OK select done"
0255                  << "C: A000004 EXPUNGE"
0256                  << "S: A000004 OK expunge done"
0257                  << "C: A000005 SELECT \"INBOX/Foo\""
0258                  << R"(S: * FLAGS (\Answered \Flagged \Draft \Deleted \Seen))"
0259                  << R"(S: * OK [ PERMANENTFLAGS (\Answered \Flagged \Draft \Deleted \Seen) ])"
0260                  << "S: * 5 EXISTS"
0261                  << "S: * 0 RECENT"
0262                  << "S: * OK [ UIDVALIDITY 1149151135  ]"
0263                  << "S: * OK [ UIDNEXT 9  ]"
0264                  << "S: * OK [ HIGHESTMODSEQ 123456789 ]"
0265                  << "S: A000005 OK select done"
0266                  << "C: A000006 UID SEARCH UID 8:9"
0267                  << "S: * SEARCH 8 9"
0268                  << "S: A000006 OK search done"
0269                  << "C: A000007 UID FETCH 8:9 (RFC822.SIZE INTERNALDATE BODY.PEEK[] FLAGS UID)"
0270                  << "S: * 4 FETCH ( FLAGS (\\Seen) UID 8 INTERNALDATE \"29-Jun-2010 15:26:42 +0200\" "
0271                     "RFC822.SIZE 75 BODY[] {75}\r\n"
0272                     "From: Foo <foo@kde.org>\r\n"
0273                     "To: Bar <bar@kde.org>\r\n"
0274                     "Subject: Test Mail\r\n"
0275                     "\r\n"
0276                     "Test\r\n"
0277                     " )"
0278                  << "S: * 5 FETCH ( FLAGS (\\Seen) UID 9 INTERNALDATE \"29-Jun-2010 15:26:42 +0200\" "
0279                     "RFC822.SIZE 75 BODY[] {75}\r\n"
0280                     "From: Foo <foo@kde.org>\r\n"
0281                     "To: Bar <bar@kde.org>\r\n"
0282                     "Subject: Test Mail\r\n"
0283                     "\r\n"
0284                     "Test\r\n"
0285                     " )"
0286                  << "S: A000007 OK fetch done"
0287                  << "C: A000008 UID SEARCH UID 1:7"
0288                  << "S: * SEARCH 1 2 3 4 5 6 7"
0289                  << "S: A000008 OK search done"
0290                  << "C: A000009 UID FETCH 1:7 (FLAGS UID)"
0291                  << "S: * 1 FETCH"
0292                  << "S: * 2 FETCH"
0293                  << "S: * 3 FETCH"
0294                  << "S: A000009 OK fetch done";
0295 
0296         callNames.clear();
0297         callNames << QStringLiteral("itemsRetrieved") << QStringLiteral("applyCollectionChanges") << QStringLiteral("itemsRetrievalDone");
0298 
0299         // A new message has been added and an old one removed, we can't do an incremental update
0300         QTest::newRow("uidnext changed, fetch new messages and list flags") << collection << scenario << callNames;
0301 
0302         collection = createCollectionChain(QStringLiteral("/INBOX/Foo"));
0303         collection.attribute<UidValidityAttribute>(Akonadi::Collection::AddIfMissing)->setUidValidity(1149151135);
0304         collection.setCachePolicy(policy);
0305         collection.attribute<UidNextAttribute>(Akonadi::Collection::AddIfMissing)->setUidNext(9);
0306         collection.attribute<HighestModSeqAttribute>(Akonadi::Collection::AddIfMissing)->setHighestModSeq(123456789);
0307         stats.setCount(5);
0308         collection.setStatistics(stats);
0309         scenario.clear();
0310         scenario << defaultPoolConnectionScenario(QList<QByteArray>() << "CONDSTORE") << "C: A000003 SELECT \"INBOX/Foo\" (CONDSTORE)"
0311                  << "S: A000003 OK select done"
0312                  << "C: A000004 EXPUNGE"
0313                  << "S: A000004 OK expunge DONE"
0314                  << "C: A000005 SELECT \"INBOX/Foo\" (CONDSTORE)"
0315                  << R"(S: * FLAGS (\Answered \Flagged \Draft \Deleted \Seen))"
0316                  << R"(S: * OK [ PERMANENTFLAGS (\Answered \Flagged \Draft \Deleted \Seen) ])"
0317                  << "S: * 5 EXISTS"
0318                  << "S: * 0 RECENT"
0319                  << "S: * OK [ UIDVALIDITY 1149151135 ]"
0320                  << "S: * OK [ UIDNEXT 9 ]"
0321                  << "S: * OK [ HIGHESTMODSEQ 123456789 ]"
0322                  << "S: A000005 OK select done";
0323         callNames.clear();
0324         callNames << QStringLiteral("applyCollectionChanges") << QStringLiteral("itemsRetrievedIncremental") << QStringLiteral("itemsRetrievalDone");
0325 
0326         // No flags have changed
0327         QTest::newRow("highestmodseq test") << collection << scenario << callNames;
0328 
0329         collection = createCollectionChain(QStringLiteral("/INBOX/Foo"));
0330         collection.attribute<UidValidityAttribute>(Akonadi::Collection::AddIfMissing)->setUidValidity(1149151135);
0331         collection.setCachePolicy(policy);
0332         collection.attribute<UidNextAttribute>(Akonadi::Collection::AddIfMissing)->setUidNext(9);
0333         collection.attribute<HighestModSeqAttribute>(Akonadi::Collection::AddIfMissing)->setHighestModSeq(123456788);
0334         stats.setCount(5);
0335         collection.setStatistics(stats);
0336         scenario.clear();
0337         scenario << defaultPoolConnectionScenario(QList<QByteArray>() << "CONDSTORE") << "C: A000003 SELECT \"INBOX/Foo\" (CONDSTORE)"
0338                  << "S: A000003 OK select done"
0339                  << "C: A000004 EXPUNGE"
0340                  << "S: A000004 OK expunge DONE"
0341                  << "C: A000005 SELECT \"INBOX/Foo\" (CONDSTORE)"
0342                  << R"(S: * FLAGS (\Answered \Flagged \Draft \Deleted \Seen))"
0343                  << R"(S: * OK [ PERMANENTFLAGS (\Answered \Flagged \Draft \Deleted \Seen) ])"
0344                  << "S: * 5 EXISTS"
0345                  << "S: * 0 RECENT"
0346                  << "S: * OK [ UIDVALIDITY 1149151135 ]"
0347                  << "S: * OK [ UIDNEXT 9 ]"
0348                  << "S: * OK [ HIGHESTMODSEQ 123456789 ]"
0349                  << "S: A000005 OK select done"
0350                  << "C: A000006 UID FETCH 1:9 (FLAGS UID) (CHANGEDSINCE 123456788)"
0351                  << "S: * 5 FETCH ( UID 8 FLAGS () )"
0352                  << "S: A000006 OK fetch done";
0353         callNames.clear();
0354         callNames << QStringLiteral("itemsRetrievedIncremental") << QStringLiteral("applyCollectionChanges") << QStringLiteral("itemsRetrievedIncremental")
0355                   << QStringLiteral("itemsRetrievalDone");
0356 
0357         // fetch only changed flags
0358         QTest::newRow("changedsince test") << collection << scenario << callNames;
0359 
0360         collection = createCollectionChain(QStringLiteral("/INBOX/Foo"));
0361         collection.setCachePolicy(policy);
0362         collection.attribute<UidValidityAttribute>(Akonadi::Collection::AddIfMissing)->setUidValidity(1149151135);
0363         collection.attribute<UidNextAttribute>(Akonadi::Collection::AddIfMissing)->setUidNext(9);
0364         collection.attribute<HighestModSeqAttribute>(Akonadi::Collection::AddIfMissing)->setHighestModSeq(123456788);
0365         stats.setCount(5);
0366         collection.setStatistics(stats);
0367         scenario.clear();
0368         scenario << defaultPoolConnectionScenario(QList<QByteArray>() << "XYMHIGHESTMODSEQ") << "C: A000003 SELECT \"INBOX/Foo\""
0369                  << "S: A000003 OK select done"
0370                  << "C: A000004 EXPUNGE"
0371                  << "S: A000004 OK expunge DONE"
0372                  << "C: A000005 SELECT \"INBOX/Foo\""
0373                  << R"(S: * FLAGS (\Answered \Flagged \Draft \Deleted \Seen))"
0374                  << R"(S: * OK [ PERMANENTFLAGS (\Answered \Flagged \Draft \Deleted \Seen) ])"
0375                  << "S: * 5 EXISTS"
0376                  << "S: * 0 RECENT"
0377                  << "S: * OK [ UIDVALIDITY 1149151135 ]"
0378                  << "S: * OK [ UIDNEXT 9 ]"
0379                  << "S: * OK [ HIGHESTMODSEQ 123456789 ]"
0380                  << "S: A000005 OK select done";
0381         // Disabled since the flag sync is disabled if CONDSTORE is not supported
0382         //              << "C: A000006 UID SEARCH UID 1:9"
0383         //              << "S: * SEARCH 1 2 3 4 5 6 7 8 9"
0384         //              << "S: A000006 OK search done"
0385         //              << "C: A000007 UID FETCH 1:9 (FLAGS UID)"
0386         //              << "S: * 5 FETCH ( UID 8 FLAGS () )"
0387         //              << "S: A000007 OK fetch done";
0388         callNames.clear();
0389 
0390         // Disabled since the flag sync is disabled if CONDSTORE is not supported
0391         callNames << /*"itemsRetrievedIncremental" << */ QStringLiteral("applyCollectionChanges") << QStringLiteral("itemsRetrievedIncremental")
0392                   << QStringLiteral("itemsRetrievalDone");
0393 
0394         // Don't rely on yahoos highestmodseq implementation
0395         QTest::newRow("yahoo highestmodseq test") << collection << scenario << callNames;
0396 
0397         collection = createCollectionChain(QStringLiteral("/INBOX/Foo"));
0398         collection.attribute<UidNextAttribute>(Akonadi::Collection::AddIfMissing)->setUidNext(9);
0399         collection.attribute<UidValidityAttribute>(Akonadi::Collection::AddIfMissing)->setUidValidity(3);
0400         collection.setCachePolicy(policy);
0401         stats.setCount(1);
0402         collection.setStatistics(stats);
0403 
0404         scenario.clear();
0405         scenario << defaultPoolConnectionScenario() << "C: A000003 SELECT \"INBOX/Foo\""
0406                  << "S: A000003 OK select done"
0407                  << "C: A000004 EXPUNGE"
0408                  << "S: A000004 OK expunge done"
0409                  << "C: A000005 SELECT \"INBOX/Foo\""
0410                  << R"(S: * FLAGS (\Answered \Flagged \Draft \Deleted \Seen))"
0411                  << R"(S: * OK [ PERMANENTFLAGS (\Answered \Flagged \Draft \Deleted \Seen) ])"
0412                  << "S: * 1 EXISTS"
0413                  << "S: * 0 RECENT"
0414                  << "S: * OK [ UIDVALIDITY 1149151135  ]"
0415                  << "S: * OK [ UIDNEXT 9  ]"
0416                  << "S: A000005 OK select done"
0417                  << "C: A000006 UID SEARCH UID 1:9"
0418                  << "S: * SEARCH 1 2 3 4 5 6 7 8 9"
0419                  << "S: A000006 OK search done"
0420                  << "C: A000007 UID FETCH 1:9 (RFC822.SIZE INTERNALDATE BODY.PEEK[] FLAGS UID)"
0421                  << "S: * 1 FETCH ( FLAGS (\\Seen) UID 2321 INTERNALDATE \"29-Jun-2010 15:26:42 +0200\" "
0422                     "RFC822.SIZE 75 BODY[] {75}\r\n"
0423                     "From: Foo <foo@kde.org>\r\n"
0424                     "To: Bar <bar@kde.org>\r\n"
0425                     "Subject: Test Mail\r\n"
0426                     "\r\n"
0427                     "Test\r\n"
0428                     " )"
0429                  << "S: A000007 OK fetch done";
0430 
0431         callNames.clear();
0432         callNames << QStringLiteral("itemsRetrieved") << QStringLiteral("applyCollectionChanges") << QStringLiteral("itemsRetrievalDone");
0433 
0434         QTest::newRow("uidvalidity changed") << collection << scenario << callNames;
0435 
0436         collection = createCollectionChain(QStringLiteral("/INBOX/Foo"));
0437         collection.attribute<UidNextAttribute>(Akonadi::Collection::AddIfMissing)->setUidNext(105);
0438         collection.attribute<UidValidityAttribute>(Akonadi::Collection::AddIfMissing)->setUidValidity(1149151135);
0439         collection.setCachePolicy(policy);
0440         stats.setCount(104);
0441         collection.setStatistics(stats);
0442 
0443         scenario.clear();
0444         scenario << defaultPoolConnectionScenario() << "C: A000003 SELECT \"INBOX/Foo\""
0445                  << "S: A000003 OK select done"
0446                  << "C: A000004 EXPUNGE"
0447                  << "S: A000004 OK expunge done"
0448                  << "C: A000005 SELECT \"INBOX/Foo\""
0449                  << R"(S: * FLAGS (\Answered \Flagged \Draft \Deleted \Seen))"
0450                  << R"(S: * OK [ PERMANENTFLAGS (\Answered \Flagged \Draft \Deleted \Seen) ])"
0451                  << "S: * 119 EXISTS"
0452                  << "S: * 0 RECENT"
0453                  << "S: * OK [ UIDVALIDITY 1149151135  ]"
0454                  << "S: * OK [ UIDNEXT 120  ]"
0455                  << "S: A000005 OK select done"
0456                  << "C: A000006 UID SEARCH UID 105:120"
0457                  // We asked for until 120 but only 119 is available (120 is uidnext)
0458                  << "S: * SEARCH 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119"
0459                  << "S: A000006 OK search done"
0460                  << "C: A000007 UID FETCH 105:114 (RFC822.SIZE INTERNALDATE BODY.PEEK[] FLAGS UID)"
0461                  << "S: * 1 FETCH ( FLAGS (\\Seen) UID 105 INTERNALDATE \"29-Jun-2010 15:26:42 +0200\" "
0462                     "RFC822.SIZE 75 BODY[] {75}\r\n"
0463                     "From: Foo <foo@kde.org>\r\n"
0464                     "To: Bar <bar@kde.org>\r\n"
0465                     "Subject: Test Mail\r\n"
0466                     "\r\n"
0467                     "Test\r\n"
0468                     " )"
0469                  // 9 more would follow but are excluded for clarity
0470                  << "S: A000007 OK fetch done"
0471                  << "C: A000008 UID FETCH 115:119 (RFC822.SIZE INTERNALDATE BODY.PEEK[] FLAGS UID)"
0472                  << "S: * 1 FETCH ( FLAGS (\\Seen) UID 115 INTERNALDATE \"29-Jun-2010 15:26:42 +0200\" "
0473                     "RFC822.SIZE 75 BODY[] {75}\r\n"
0474                     "From: Foo <foo@kde.org>\r\n"
0475                     "To: Bar <bar@kde.org>\r\n"
0476                     "Subject: Test Mail\r\n"
0477                     "\r\n"
0478                     "Test\r\n"
0479                     " )"
0480                  // 4 more would follow but are excluded for clarity
0481                  << "S: A000008 OK fetch done"
0482                  << "C: A000009 UID SEARCH UID 1:104"
0483                  << "S: * SEARCH 1 2 99 100"
0484                  << "S: A000009 OK search done"
0485                  << "C: A000010 UID FETCH 1:2,99:100 (FLAGS UID)"
0486                  << "S: * 1 FETCH ( FLAGS (\\Seen) UID 1 )"
0487                  // 3 more would follow but are excluded for clarity
0488                  << "S: A000010 OK fetch done";
0489 
0490         callNames.clear();
0491         callNames << QStringLiteral("itemsRetrievedIncremental") << QStringLiteral("itemsRetrievedIncremental") << QStringLiteral("itemsRetrievedIncremental")
0492                   << QStringLiteral("applyCollectionChanges") << QStringLiteral("itemsRetrievedIncremental") << QStringLiteral("itemsRetrievalDone");
0493 
0494         QTest::newRow("test batch processing") << collection << scenario << callNames;
0495 
0496         collection = createCollectionChain(QStringLiteral("/INBOX/Foo"));
0497         collection.attribute<UidValidityAttribute>(Akonadi::Collection::AddIfMissing)->setUidValidity(1149151135);
0498         collection.setCachePolicy(policy);
0499         collection.attribute<UidNextAttribute>(Akonadi::Collection::AddIfMissing)->setUidNext(9);
0500         collection.attribute<HighestModSeqAttribute>(Akonadi::Collection::AddIfMissing)->setHighestModSeq(123456789);
0501         stats.setCount(5);
0502         collection.setStatistics(stats);
0503         scenario.clear();
0504         scenario << defaultPoolConnectionScenario(QList<QByteArray>() << "CONDSTORE") << "C: A000003 SELECT \"INBOX/Foo\" (CONDSTORE)"
0505                  << "S: A000003 OK select done"
0506                  << "C: A000004 EXPUNGE"
0507                  << "S: A000004 OK expunge DONE"
0508                  << "C: A000005 SELECT \"INBOX/Foo\" (CONDSTORE)"
0509                  << R"(S: * FLAGS (\Answered \Flagged \Draft \Deleted \Seen))"
0510                  << R"(S: * OK [ PERMANENTFLAGS (\Answered \Flagged \Draft \Deleted \Seen) ])"
0511                  << "S: * 4 EXISTS"
0512                  << "S: * 0 RECENT"
0513                  << "S: * OK [ UIDVALIDITY 1149151135 ]"
0514                  << "S: * OK [ UIDNEXT 9 ]"
0515                  << "S: * OK [ HIGHESTMODSEQ 123456789 ]"
0516                  << "S: A000005 OK select done"
0517                  << "C: A000006 UID SEARCH UID 1:9"
0518                  << "S: * SEARCH 1 2 3 4"
0519                  << "S: A000006 OK search done"
0520                  << "C: A000007 UID FETCH 1:4 (FLAGS UID)"
0521                  << "S: * 1 FETCH ( FLAGS (\\Seen) UID 1 )"
0522                  << "S: * 2 FETCH ( FLAGS (\\Seen) UID 2 )"
0523                  << "S: * 3 FETCH ( FLAGS (\\Seen) UID 3 )"
0524                  << "S: * 4 FETCH ( FLAGS (\\Seen) UID 4 )"
0525                  << "S: A000007 OK fetch done";
0526         callNames.clear();
0527         callNames << QStringLiteral("itemsRetrieved") << QStringLiteral("applyCollectionChanges") << QStringLiteral("itemsRetrievalDone");
0528 
0529         // fetch only changed flags
0530         QTest::newRow("remote message deleted") << collection << scenario << callNames;
0531 
0532         collection = createCollectionChain(QStringLiteral("/INBOX/Foo"));
0533         collection.attribute<UidValidityAttribute>(Akonadi::Collection::AddIfMissing)->setUidValidity(1149151135);
0534         collection.setCachePolicy(policy);
0535         collection.attribute<UidNextAttribute>(Akonadi::Collection::AddIfMissing)->setUidNext(-1);
0536         collection.attribute<HighestModSeqAttribute>(Akonadi::Collection::AddIfMissing)->setHighestModSeq(123456789);
0537         stats.setCount(0);
0538         collection.setStatistics(stats);
0539         scenario.clear();
0540         scenario << defaultPoolConnectionScenario() << "C: A000003 SELECT \"INBOX/Foo\""
0541                  << "S: A000003 OK select done"
0542                  << "C: A000004 EXPUNGE"
0543                  << "S: A000004 OK expunge done"
0544                  << "C: A000005 SELECT \"INBOX/Foo\""
0545                  << R"(S: * FLAGS (\Answered \Flagged \Draft \Deleted \Seen))"
0546                  << R"(S: * OK [ PERMANENTFLAGS (\Answered \Flagged \Draft \Deleted \Seen) ])"
0547                  << "S: * 1 EXISTS"
0548                  << "S: * 0 RECENT"
0549                  << "S: * OK [ UIDVALIDITY 1149151135  ]"
0550                  << "S: A000005 OK select done"
0551                  << "C: A000006 STATUS \"INBOX/Foo\" (UIDNEXT)"
0552                  << "S: * STATUS \"INBOX/Foo\" (UIDNEXT 10)"
0553                  << "S: A000006 OK status done"
0554                  << "C: A000007 UID SEARCH UID 1:10"
0555                  << "S: * SEARCH 1 2 3 4 5 6 7 8 9"
0556                  << "S: A000007 OK search done"
0557                  << "C: A000008 UID FETCH 1:9 (RFC822.SIZE INTERNALDATE BODY.PEEK[] FLAGS UID)"
0558                  << "S: * 1 FETCH ( FLAGS (\\Seen) UID 2321 INTERNALDATE \"29-Jun-2010 15:26:42 +0200\" "
0559                     "RFC822.SIZE 75 BODY[] {75}\r\n"
0560                     "From: Foo <foo@kde.org>\r\n"
0561                     "To: Bar <bar@kde.org>\r\n"
0562                     "Subject: Test Mail\r\n"
0563                     "\r\n"
0564                     "Test\r\n"
0565                     " )"
0566                  << "S: A000008 OK fetch done";
0567 
0568         callNames.clear();
0569         callNames << QStringLiteral("itemsRetrieved") << QStringLiteral("applyCollectionChanges") << QStringLiteral("itemsRetrievalDone");
0570 
0571         QTest::newRow("missing uidnext") << collection << scenario << callNames;
0572     }
0573 
0574     void shouldIntrospectCollection()
0575     {
0576         QFETCH(Akonadi::Collection, collection);
0577         QFETCH(QList<QByteArray>, scenario);
0578         QFETCH(QStringList, callNames);
0579 
0580         FakeServer server;
0581         server.setScenario(scenario);
0582         server.startAndWait();
0583 
0584         SessionPool pool(1);
0585 
0586         pool.setPasswordRequester(createDefaultRequester());
0587         QVERIFY(pool.connect(createDefaultAccount()));
0588         QVERIFY(waitForSignal(&pool, SIGNAL(connectDone(int, QString))));
0589 
0590         DummyResourceState::Ptr state = DummyResourceState::Ptr(new DummyResourceState);
0591         state->setServerCapabilities(pool.serverCapabilities());
0592         state->setCollection(collection);
0593 
0594         auto task = new RetrieveItemsTask(state);
0595         task->setFetchMissingItemBodies(false);
0596         task->start(&pool);
0597 
0598         QTRY_COMPARE(state->calls().count(), callNames.size());
0599         qDebug() << state->calls();
0600         for (int i = 0; i < callNames.size(); i++) {
0601             QString command = QString::fromUtf8(state->calls().at(i).first);
0602             QVariant parameter = state->calls().at(i).second;
0603 
0604             if (command == QLatin1StringView("cancelTask") && callNames[i] != QLatin1StringView("cancelTask")) {
0605                 qDebug() << "Got a cancel:" << parameter.toString();
0606             }
0607 
0608             QCOMPARE(command, callNames[i]);
0609 
0610             if (command == QLatin1StringView("cancelTask")) {
0611                 QVERIFY(!parameter.toString().isEmpty());
0612             }
0613         }
0614 
0615         QVERIFY(server.isAllScenarioDone());
0616 
0617         server.quit();
0618     }
0619 };
0620 
0621 QTEST_GUILESS_MAIN(TestRetrieveItemsTask)
0622 
0623 #include "testretrieveitemstask.moc"