aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/main.cpp10
-rw-r--r--src/mainwindow.cpp17
-rw-r--r--src/mainwindow.h16
-rw-r--r--tests/test_mainwindow.cpp35
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"