diff options
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/test_config.cpp | 33 | ||||
| -rw-r--r-- | tests/test_mailsync.cpp | 52 | ||||
| -rw-r--r-- | tests/test_threadlistmodel.cpp | 43 |
3 files changed, 128 insertions, 0 deletions
diff --git a/tests/test_config.cpp b/tests/test_config.cpp index 4d92891..dc85968 100644 --- a/tests/test_config.cpp +++ b/tests/test_config.cpp @@ -51,6 +51,8 @@ private slots: void extraMimetypesAppendToBuiltins(); void extraMimetypeDescriptionMayContainComma(); void malformedExtraMimetypeIsSkipped(); + void syncChannelDefaultsToTheAccountKey(); + void syncChannelIsActuallyRead(); }; static QString writeIni(const QTemporaryDir &dir, const QString &body) @@ -524,5 +526,36 @@ void TestConfig::malformedExtraMimetypeIsSkipped() QVERIFY(!config.problems().isEmpty()); } +void TestConfig::syncChannelDefaultsToTheAccountKey() +{ + // Most accounts name their mbsync channel exactly as their section key, so + // the common case must need no config edit at all. + QTemporaryDir dir; + Config config; + config.load(writeIni(dir, QStringLiteral( + "[account.work]\n" + "maildir = work\n"))); + + QCOMPARE(config.accounts().size(), 1); + QCOMPARE(config.accounts().at(0).syncChannel(), QStringLiteral("work")); +} + +void TestConfig::syncChannelIsActuallyRead() +{ + // The key exists because the two names genuinely diverge: a QSettings + // section key may carry dots that the mbsync channel does not, and passing + // the section key to mbsync would name a channel that does not exist. + QTemporaryDir dir; + Config config; + config.load(writeIni(dir, QStringLiteral( + "[account.mail-first.last]\n" + "maildir = mail-first.last\n" + "channel = mail-firstlast\n"))); + + QCOMPARE(config.accounts().size(), 1); + QCOMPARE(config.accounts().at(0).syncChannel(), + QStringLiteral("mail-firstlast")); +} + QTEST_MAIN(TestConfig) #include "test_config.moc" diff --git a/tests/test_mailsync.cpp b/tests/test_mailsync.cpp index 6c93e8d..e91de3e 100644 --- a/tests/test_mailsync.cpp +++ b/tests/test_mailsync.cpp @@ -43,6 +43,9 @@ private slots: void missingBinaryReportsFailureNotSilence(); void startDoesNotBlock(); void argumentsAreNotShellInterpreted(); + void channelsAreAppendedToTheCommand(); + void noChannelsMeansNoExtraArguments(); + void channelNamesAreNotShellInterpreted(); void phaseStartsAsMbsync(); void notmuchLineSwitchesPhase(); @@ -262,6 +265,55 @@ void TestMailSync::argumentsAreNotShellInterpreted() QVERIFY(sync.log().contains(QStringLiteral("; touch"))); } +void TestMailSync::channelsAreAppendedToTheCommand() +{ + // Item 49: a sync that knows which accounts were touched passes their + // channel names, and they must reach the script as separate arguments + // after whatever the config line already carries. + const QString script = makeScript(QStringLiteral("chan.sh"), + QStringLiteral("echo \"[$@]\"")); + + MailSync sync(script); + QSignalSpy spy(&sync, &MailSync::finished); + QVERIFY(sync.start({ QStringLiteral("work"), QStringLiteral("personal") })); + QVERIFY(spy.wait(5000)); + + QVERIFY(sync.log().contains(QStringLiteral("[work personal]"))); +} + +void TestMailSync::noChannelsMeansNoExtraArguments() +{ + // Empty means "sync everything", which is the script's own default. It must + // not become an empty string argument: mbsync would read that as a channel + // named "" and fail the run. + const QString script = makeScript(QStringLiteral("nochan.sh"), + QStringLiteral("echo \"count=$#\"")); + + MailSync sync(script); + QSignalSpy spy(&sync, &MailSync::finished); + QVERIFY(sync.start()); + QVERIFY(spy.wait(5000)); + + QVERIFY(sync.log().contains(QStringLiteral("count=0"))); +} + +void TestMailSync::channelNamesAreNotShellInterpreted() +{ + // Channel names are derived from config, same trust boundary as the command + // itself, and reach the same QProcess argument list. The injection test + // above covers the command; this covers the half added for item 49. + const QString script = makeScript(QStringLiteral("chanargs.sh"), + QStringLiteral("echo \"$1\"")); + + MailSync sync(script); + QSignalSpy spy(&sync, &MailSync::finished); + QVERIFY(sync.start({ QStringLiteral("; touch %1/chanpwned") + .arg(m_dir.path()) })); + QVERIFY(spy.wait(5000)); + + QVERIFY(!QFile::exists(m_dir.filePath(QStringLiteral("chanpwned")))); +} + void TestMailSync::phaseStartsAsMbsync() { // A fresh tracker has nothing to report until it is fed, and a run is diff --git a/tests/test_threadlistmodel.cpp b/tests/test_threadlistmodel.cpp index 82686e5..f84bbab 100644 --- a/tests/test_threadlistmodel.cpp +++ b/tests/test_threadlistmodel.cpp @@ -28,6 +28,9 @@ class TestThreadListModel : public QObject Q_OBJECT private slots: void startsEmpty(); + void accountKeysComeFromTheAccountTags(); + void accountKeysCoverAThreadSpanningTwoAccounts(); + void accountKeysAreEmptyForAnUnknownThread(); void appendsBatches(); void appendingEmptyBatchIsNoOp(); void clearResetsModel(); @@ -73,6 +76,46 @@ static ThreadSummary makeThread(const QString &id, const QString &subject) return t; } +void TestThreadListModel::accountKeysComeFromTheAccountTags() +{ + // Item 49 reads this to decide which mbsync channels a sync needs. Only + // account tags count: a functional tag names no mailbox. + ThreadListModel model; + ThreadSummary t = makeThread(QStringLiteral("t1"), QStringLiteral("Hi")); + t.tags.append(TagColors::tagForAccountKey(QStringLiteral("work"))); + model.appendBatch({ t }); + + QCOMPARE(model.accountKeysForThread(QStringLiteral("t1")), + QStringList{ QStringLiteral("work") }); +} + +void TestThreadListModel::accountKeysCoverAThreadSpanningTwoAccounts() +{ + // The row shows one chip, but tagging this thread touches files under both + // mailboxes. Returning only the first would strand the other's edits. + ThreadListModel model; + ThreadSummary t = makeThread(QStringLiteral("t1"), QStringLiteral("Hi")); + t.tags.append(TagColors::tagForAccountKey(QStringLiteral("work"))); + t.tags.append(TagColors::tagForAccountKey(QStringLiteral("personal"))); + model.appendBatch({ t }); + + QStringList keys = model.accountKeysForThread(QStringLiteral("t1")); + keys.sort(); + QCOMPARE(keys, (QStringList{ QStringLiteral("personal"), + QStringLiteral("work") })); +} + +void TestThreadListModel::accountKeysAreEmptyForAnUnknownThread() +{ + // A thread the model no longer holds must yield nothing rather than + // matching some other row. + ThreadListModel model; + model.appendBatch({ makeThread(QStringLiteral("t1"), + QStringLiteral("Hi")) }); + + QVERIFY(model.accountKeysForThread(QStringLiteral("nope")).isEmpty()); +} + void TestThreadListModel::startsEmpty() { ThreadListModel model; |
