diff options
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/test_config.cpp | 56 | ||||
| -rw-r--r-- | tests/test_keymap.cpp | 20 | ||||
| -rw-r--r-- | tests/test_mainwindow.cpp | 3 | ||||
| -rw-r--r-- | tests/test_messageview.cpp | 55 |
4 files changed, 134 insertions, 0 deletions
diff --git a/tests/test_config.cpp b/tests/test_config.cpp index e492480..367643c 100644 --- a/tests/test_config.cpp +++ b/tests/test_config.cpp @@ -34,6 +34,8 @@ private slots: void brokenSyncCommandIsAProblem(); void malformedAccountIsAProblem(); void validConfigHasNoProblems(); + void generalSectionKeysAreActuallyRead(); + void messageZoomDefaultsAndValidates(); }; static QString writeIni(const QTemporaryDir &dir, const QString &body) @@ -222,5 +224,59 @@ void TestConfig::validConfigHasNoProblems() QVERIFY(config.warnings().isEmpty()); } +void TestConfig::generalSectionKeysAreActuallyRead() +{ + // QSettings' INI backend treats a section literally named [general] as its + // own fallback section and strips the prefix, so a "general/<key>" lookup + // matches nothing. notmuch_config was read that way and had never worked. + QTemporaryDir dir; + Config config; + config.load(writeIni(dir, QStringLiteral( + "[general]\n" + "notmuch_config=/somewhere/notmuch-config\n" + "\n" + "[sync]\n" + "command=/bin/true\n"))); + + QCOMPARE(config.notmuchConfig(), + QStringLiteral("/somewhere/notmuch-config")); +} + +void TestConfig::messageZoomDefaultsAndValidates() +{ + // A QTemporaryDir per case, not one shared: writeIni() always uses the + // same file name, and QSettings caches by path, so a second load of the + // same path would return the first case's contents. + + // Absent: 1.0, silently. Nothing the user asked for is being ignored. + { + QTemporaryDir dir; + Config config; + config.load(writeIni(dir, QStringLiteral("[general]\n"))); + QCOMPARE(config.messageZoom(), 1.0); + QVERIFY(config.problems().isEmpty()); + } + + { + QTemporaryDir dir; + Config config; + config.load(writeIni(dir, QStringLiteral("[general]\n" + "message_zoom=1.25\n"))); + QCOMPARE(config.messageZoom(), 1.25); + QVERIFY(config.problems().isEmpty()); + } + + // Present but unparseable is a problem: the user asked for something and + // is not getting it, which is the line addProblem() draws. + { + QTemporaryDir dir; + Config config; + config.load(writeIni(dir, QStringLiteral("[general]\n" + "message_zoom=huge\n"))); + QCOMPARE(config.messageZoom(), 1.0); + QCOMPARE(config.problems().size(), 1); + } +} + QTEST_MAIN(TestConfig) #include "test_config.moc" diff --git a/tests/test_keymap.cpp b/tests/test_keymap.cpp index c81eeb0..0fb4f57 100644 --- a/tests/test_keymap.cpp +++ b/tests/test_keymap.cpp @@ -36,8 +36,28 @@ private slots: void userBindingWinsOverDefaultInMenus(); void defaultsDoNotCollide(); void everyDefaultIsAKnownAction(); + void everyDefaultParses(); }; +void TestKeyMap::everyDefaultParses() +{ + // A default that does not parse is a dead binding, the failure mode + // bareCapitalMatchesShiftedPress() covers for user-written keys. + // + // This deliberately does NOT try to decide which keys a keyboard can + // deliver. Whether a symbol needs Shift is a layout property, not a Qt + // one: Ctrl++ is exactly what the '+' key emits on an Italian layout and + // is unreachable on a US one, and QTest::keyClick() cannot reproduce + // either faithfully. A test asserting reachability from synthetic input + // would encode one layout's habits as a rule for all of them. + for (const auto &binding : KeyMap::defaultBindings()) { + const QKeySequence sequence = KeyMap::normalizeSequence(binding.first); + QVERIFY2(!sequence.isEmpty(), + qPrintable(QStringLiteral("default '%1' for %2 does not parse") + .arg(binding.first, binding.second))); + } +} + void TestKeyMap::defaultsAreLoaded() { KeyMap map; diff --git a/tests/test_mainwindow.cpp b/tests/test_mainwindow.cpp index 628472b..b4d9e4a 100644 --- a/tests/test_mainwindow.cpp +++ b/tests/test_mainwindow.cpp @@ -28,6 +28,7 @@ #include "config.h" #include "keymap.h" #include "mainwindow.h" +#include "messageview.h" /// MainWindow is mostly wiring, and the parts that need a real database are /// still verified manually. What is checked here is the action registry: the @@ -190,6 +191,7 @@ void TestMainWindow::uiStateSurvivesARestart() const Config config; MainWindow window(config); window.resize(resized); + window.findChild<MessageView *>()->setZoomFactor(1.4); window.close(); // closeEvent() is what persists the state } @@ -200,6 +202,7 @@ void TestMainWindow::uiStateSurvivesARestart() const Config config; MainWindow reopened(config); QCOMPARE(reopened.size(), resized); + QCOMPARE(reopened.findChild<MessageView *>()->zoomFactor(), 1.4); QFile::remove(MainWindow::uiStatePath()); QStandardPaths::setTestModeEnabled(false); diff --git a/tests/test_messageview.cpp b/tests/test_messageview.cpp index 0c3353d..4b6bc4c 100644 --- a/tests/test_messageview.cpp +++ b/tests/test_messageview.cpp @@ -36,6 +36,8 @@ private slots: void documentActuallyLoads(); void threadContentReachesThePage(); void dataUrlSubResourceStillBlocked(); + void zoomIsClampedToARenderableRange(); + void zoomSurvivesANewDocument(); private: QWebEngineView *webViewOf(MessageView *view) const @@ -175,5 +177,58 @@ void TestMessageView::dataUrlSubResourceStillBlocked() QVERIFY(text.contains(QStringLiteral("visible-text"))); } +void TestMessageView::zoomIsClampedToARenderableRange() +{ + // A factor outside the range leaves the pane unreadable, and the only way + // back is a menu entry the user can no longer read. A corrupt state file + // reaching setZoomFactor() must not be able to do that. + QCOMPARE(MessageView::clampZoom(100.0), MessageView::kMaxZoom); + QCOMPARE(MessageView::clampZoom(0.01), MessageView::kMinZoom); + + // A missing or non-numeric state value converts to 0.0, and a hand-edited + // one can hold NaN or an infinity. None of those may reach the web view. + QCOMPARE(MessageView::clampZoom(0.0), MessageView::kDefaultZoom); + QCOMPARE(MessageView::clampZoom(-2.0), MessageView::kDefaultZoom); + QCOMPARE(MessageView::clampZoom(qQNaN()), MessageView::kDefaultZoom); + QCOMPARE(MessageView::clampZoom(qInf()), MessageView::kDefaultZoom); + + // In-range values pass through untouched. + QCOMPARE(MessageView::clampZoom(1.4), 1.4); + + MessageView view; + view.setZoomFactor(50.0); + QCOMPARE(view.zoomFactor(), MessageView::kMaxZoom); +} + +void TestMessageView::zoomSurvivesANewDocument() +{ + // MainWindow persists whatever zoomFactor() reports and never reapplies it + // per render, which is only correct if the web view keeps the factor + // across setHtml(). Verified rather than assumed. + MessageView view; + QWebEngineView *web = webViewOf(&view); + QVERIFY(web); + + view.setZoomFactor(1.5); + + QSignalSpy loaded(web, &QWebEngineView::loadFinished); + + ParsedMessage message; + message.ok = true; + message.from = QStringLiteral("Sender <sender@example.org>"); + message.subject = QStringLiteral("Zoom"); + message.plainBody = QStringLiteral("body text"); + + ThreadRenderItem item; + item.message = message; + item.cidPrefix = QStringLiteral("m0"); + item.expanded = true; + + view.showThread({ item }); + QVERIFY(loaded.wait(15000)); + + QCOMPARE(view.zoomFactor(), 1.5); +} + QTEST_MAIN(TestMessageView) #include "test_messageview.moc" |
