diff options
| -rw-r--r-- | src/main.cpp | 10 | ||||
| -rw-r--r-- | src/mainwindow.cpp | 17 | ||||
| -rw-r--r-- | src/mainwindow.h | 16 | ||||
| -rw-r--r-- | tests/test_mainwindow.cpp | 35 |
4 files changed, 69 insertions, 9 deletions
diff --git a/src/main.cpp b/src/main.cpp index 231594f..2804196 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -97,5 +97,15 @@ int main(int argc, char *argv[]) MainWindow window(config); window.show(); + // After show(), and out here rather than inside the constructor. A modal + // raised from the constructor cannot be dismissed under the offscreen + // platform, so it hung the test suite with no output (item 84). Showing it + // here also gives the dialog a visible parent to sit on. + const QStringList problems = window.configProblems(); + if (!problems.isEmpty()) { + QMessageBox::warning(&window, QObject::tr("Configuration problems"), + problems.join(QLatin1Char('\n'))); + } + return app.exec(); } diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index bf6fc79..ee559fe 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -384,7 +384,10 @@ MainWindow::MainWindow(const Config &config, QWidget *parent) // object name, so they must already exist or their position is dropped. restoreUiState(); wireWorker(); - showWarnings(); + // Sets the status label only. The modal that used to live here is raised + // by the caller after show(), because a modal in a constructor cannot be + // dismissed under the offscreen platform and hung the whole suite. + applyWarnings(); // No window-wide event filter: QAction shortcuts are dispatched before the // focused widget sees the key, so they beat QAbstractItemView's @@ -1688,7 +1691,7 @@ void MainWindow::runSearchFromPane(const QString &query, runCurrentQuery(); } -void MainWindow::showWarnings() +void MainWindow::applyWarnings() { const QStringList warnings = m_config.warnings() + m_keyMap.warnings(); if (warnings.isEmpty()) @@ -1697,18 +1700,16 @@ void MainWindow::showWarnings() // Non-fatal: the app runs degraded rather than refusing to start. m_statusLabel->setText( tr("%n configuration warning(s)", "", warnings.size())); +} +QStringList MainWindow::configProblems() const +{ // Interrupt startup only for things that are actually wrong. Every KeyMap // warning qualifies (each one means a binding the user wrote is being // ignored), but a Config notice such as "no sync command configured" does // not: nothing is broken, the feature is simply off, and a modal on every // launch teaches the user to dismiss dialogs unread. - const QStringList problems = m_config.problems() + m_keyMap.warnings(); - if (problems.isEmpty()) - return; - - QMessageBox::warning(this, tr("Configuration problems"), - problems.join(QLatin1Char('\n'))); + return m_config.problems() + m_keyMap.warnings(); } void MainWindow::buildSavedQueryRow(QWidget *parent, QVBoxLayout *layout) diff --git a/src/mainwindow.h b/src/mainwindow.h index b41f75d..d844f1e 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -75,6 +75,18 @@ public: /// really registered. QStringList registeredActionNames() const; + /// Configuration problems worth interrupting startup for, empty when there + /// are none. A keybinding the user wrote and that is being ignored counts; + /// a notice such as "no sync command configured" does not. + /// + /// **Returned rather than shown, and that is the point.** This used to + /// raise a `QMessageBox` from the CONSTRUCTOR. A modal cannot be dismissed + /// under the offscreen platform, so `MainWindow` never finished + /// constructing and the whole suite hung with no output, which reads as an + /// infrastructure failure rather than a test one (item 84). The caller + /// raises the dialog after show(); a test asserts on the list. + QStringList configProblems() const; + /// The thread currently shown in the message pane, empty when it is blank. /// /// Empty is what "the pane is blanked" means internally: a late-arriving @@ -460,7 +472,9 @@ private: QList<HtmlBuilder::PlaceholderHelper> placeholderHelpers() const; - void showWarnings(); + /// Puts the warning count in the status bar. Nothing modal: see + /// configProblems() for why the dialog is not raised here. + void applyWarnings(); void showShortcutReference(); void showAbout(); diff --git a/tests/test_mainwindow.cpp b/tests/test_mainwindow.cpp index 0bf8925..be3a28f 100644 --- a/tests/test_mainwindow.cpp +++ b/tests/test_mainwindow.cpp @@ -91,6 +91,7 @@ private slots: void aSearchFromThePaneReplacesTheQuery(); void aSearchFromThePaneCanNarrowTheQuery(); void narrowingAnEmptyQueryBarIsAPlainSearch(); + void aMalformedAccountIsReportedWithoutBlockingTheConstructor(); void autoSyncIsNotArmedWhenDisabledOrWithNothingPending(); void autoSyncSkipsWhileABackgroundSyncIsRunning(); void aSuccessfulSyncRefreshesRatherThanRerunningTheQuery(); @@ -6105,4 +6106,38 @@ void TestMainWindow::renamingReplacesRatherThanDuplicating() QCOMPARE(savedQueryButtonLabels(window), QStringList{ QStringLiteral("New") }); } +void TestMainWindow::aMalformedAccountIsReportedWithoutBlockingTheConstructor() +{ + // The exact shape that hung the suite on 2026-08-14: an account section + // carrying `sent=` and no `maildir=`. Config::load handles it correctly, + // recording a problem and carrying on, but showWarnings() then raised a + // modal FROM THE CONSTRUCTOR, which nothing can dismiss under the + // offscreen platform, so MainWindow never finished constructing. + // + // Constructing the window at all is therefore half the assertion: if the + // modal comes back, this test does not fail, it HANGS. + QTemporaryDir dir; + QVERIFY(dir.isValid()); + + const QString path = dir.filePath(QStringLiteral("qtmaildir.conf")); + { + QFile file(path); + QVERIFY(file.open(QIODevice::WriteOnly | QIODevice::Text)); + QTextStream out(&file); + out << "[account.one]\n" + << "sent=Sent\n"; + } + + Config config; + config.load(path); + + MainWindow window(config); + + // The problem is reported rather than swallowed, and it names the account. + const QStringList problems = window.configProblems(); + QVERIFY2(!problems.isEmpty(), + "a malformed account produced no problem to report"); + QVERIFY(problems.join(QLatin1Char('\n')).contains(QStringLiteral("one"))); +} + #include "test_mainwindow.moc" |
