aboutsummaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-08-03 16:00:38 +0200
committerDanilo M. <danix@danix.xyz>2026-08-04 12:53:35 +0200
commit98c1c615d147bfee3f17d4f420e1512af5a79436 (patch)
tree82037d456544d6128324d85d5d572208f84c06a2 /tests
parenta5b2151ef4b602350bab6ecfcd05b4bae56fd6af (diff)
downloadqtmaildir-98c1c615d147bfee3f17d4f420e1512af5a79436.tar.gz
qtmaildir-98c1c615d147bfee3f17d4f420e1512af5a79436.zip
feat: persist window, splitter and column widths
Resizing the window, the splitter or a thread-list column was undone by the next launch. State now round-trips through a separate settings file at ~/.local/state/qtmaildir/uistate.conf, written on close and read at startup. The state file is deliberately not the user's config: a base64 geometry blob does not belong in a hand-edited file, and rewriting that file on exit would drop its comments and key order, which QSettings does not preserve. Two details that are easy to get wrong: QStandardPaths::StateLocation appends both the organization and the application name, and both are "qtmaildir" here, so it resolves to ~/.local/state/qtmaildir/qtmaildir. The path is built from GenericStateLocation instead, matching Config::defaultPath(). Restore runs after buildMenus() rather than at the end of buildUi(): QMainWindow::restoreState() matches toolbars by object name and silently drops the position of one that does not exist yet. Every restore is conditional on a non-empty blob, so a missing or rejected state file leaves the built-in defaults instead of producing a zero-size window. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Diffstat (limited to 'tests')
-rw-r--r--tests/test_mainwindow.cpp61
1 files changed, 61 insertions, 0 deletions
diff --git a/tests/test_mainwindow.cpp b/tests/test_mainwindow.cpp
index 6bfa925..628472b 100644
--- a/tests/test_mainwindow.cpp
+++ b/tests/test_mainwindow.cpp
@@ -20,7 +20,9 @@
#include <QAction>
#include <QDir>
+#include <QFile>
#include <QSettings>
+#include <QStandardPaths>
#include <QTemporaryDir>
#include "config.h"
@@ -41,6 +43,9 @@ private slots:
void configuredBindingReachesTheAction();
void cidPrefixesAreBangFree();
void cidPrefixesAreDistinctPerMessage();
+ void uiStateIsNotWrittenIntoTheUserConfig();
+ void uiStateSurvivesARestart();
+ void missingUiStateLeavesTheDefaults();
};
void TestMainWindow::everyKnownActionIsRegistered()
@@ -158,6 +163,62 @@ void TestMainWindow::cidPrefixesAreDistinctPerMessage()
}
}
+void TestMainWindow::uiStateIsNotWrittenIntoTheUserConfig()
+{
+ // The config file is hand-edited and must never gain a base64 geometry
+ // blob, nor be rewritten on exit: QSettings preserves neither comments nor
+ // key order, so writing it would quietly destroy the user's formatting.
+ QVERIFY(MainWindow::uiStatePath() != Config::defaultPath());
+
+ // One qtmaildir component, not two. QStandardPaths::StateLocation appends
+ // both the organization and the application name, and here both are
+ // "qtmaildir", so using it nests the directory inside itself.
+ QCOMPARE(MainWindow::uiStatePath().count(QStringLiteral("/qtmaildir/")), 1);
+ QVERIFY(MainWindow::uiStatePath().endsWith(
+ QStringLiteral("/qtmaildir/uistate.conf")));
+}
+
+void TestMainWindow::uiStateSurvivesARestart()
+{
+ // Test mode redirects QStandardPaths at the process level, so the state
+ // file lands in a scratch directory rather than the real ~/.local/state.
+ QStandardPaths::setTestModeEnabled(true);
+ QFile::remove(MainWindow::uiStatePath());
+
+ const QSize resized(940, 620);
+ {
+ const Config config;
+ MainWindow window(config);
+ window.resize(resized);
+ window.close(); // closeEvent() is what persists the state
+ }
+
+ QVERIFY2(QFile::exists(MainWindow::uiStatePath()),
+ qPrintable(QStringLiteral("no state file at %1")
+ .arg(MainWindow::uiStatePath())));
+
+ const Config config;
+ MainWindow reopened(config);
+ QCOMPARE(reopened.size(), resized);
+
+ QFile::remove(MainWindow::uiStatePath());
+ QStandardPaths::setTestModeEnabled(false);
+}
+
+void TestMainWindow::missingUiStateLeavesTheDefaults()
+{
+ // A restore that silently succeeded on an empty blob would give a
+ // zero-size window on first launch. Absent state must be a no-op.
+ QStandardPaths::setTestModeEnabled(true);
+ QFile::remove(MainWindow::uiStatePath());
+
+ const Config config;
+ MainWindow window(config);
+ QCOMPARE(window.size(), QSize(1200, 800));
+
+ QStandardPaths::setTestModeEnabled(false);
+}
+
// Constructing a MainWindow needs a QApplication and a platform plugin. The
// test has no display under ctest, so it runs offscreen unless the caller
// asked for something else.