summaryrefslogtreecommitdiffstats
path: root/tests/test_keymap.cpp
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-08-02 17:02:18 +0200
committerDanilo M. <danix@danix.xyz>2026-08-02 17:02:18 +0200
commitdd7845d847a3d6f961f0302551c33245d1f12bdc (patch)
treef146a0bf84bbd3229e9bf18235314de0bc5f4c5f /tests/test_keymap.cpp
parent059fa151856293a8b6bdf9b16d1b72f415f59df3 (diff)
downloadqtmaildir-dd7845d847a3d6f961f0302551c33245d1f12bdc.tar.gz
qtmaildir-dd7845d847a3d6f961f0302551c33245d1f12bdc.zip
feat: add KeyMap with defaults and INI overrides
Maps key sequences to action name strings, with hardcoded vim-style defaults and QSettings-based [keys] overrides. Unknown actions and unparseable sequences are collected as warnings rather than treated as fatal, so a typo in the config cannot silently misbind or crash. Note: QKeySequence::fromString() on Qt 6.11 does not return an empty sequence for unparseable input (e.g. "NotAKey++") -- it returns a non-empty sequence whose toString() is empty. Detection uses that instead of isEmpty().
Diffstat (limited to 'tests/test_keymap.cpp')
-rw-r--r--tests/test_keymap.cpp131
1 files changed, 131 insertions, 0 deletions
diff --git a/tests/test_keymap.cpp b/tests/test_keymap.cpp
new file mode 100644
index 0000000..10863f2
--- /dev/null
+++ b/tests/test_keymap.cpp
@@ -0,0 +1,131 @@
+#include <QtTest>
+#include <QTemporaryDir>
+#include <QSettings>
+#include "keymap.h"
+
+class TestKeyMap : public QObject
+{
+ Q_OBJECT
+private slots:
+ void defaultsAreLoaded();
+ void iniOverridesDefault();
+ void iniAddsNewBinding();
+ void chordSequenceParses();
+ void unknownActionIsReported();
+ void invalidSequenceIsReported();
+};
+
+void TestKeyMap::defaultsAreLoaded()
+{
+ KeyMap map;
+ map.loadDefaults();
+ QCOMPARE(map.actionFor(QKeySequence(QStringLiteral("j"))),
+ QStringLiteral("next_thread"));
+ QCOMPARE(map.actionFor(QKeySequence(QStringLiteral("a"))),
+ QStringLiteral("archive"));
+}
+
+void TestKeyMap::iniOverridesDefault()
+{
+ QTemporaryDir dir;
+ const QString path = dir.filePath(QStringLiteral("t.conf"));
+ {
+ QSettings s(path, QSettings::IniFormat);
+ s.beginGroup(QStringLiteral("keys"));
+ s.setValue(QStringLiteral("j"), QStringLiteral("archive"));
+ s.endGroup();
+ }
+
+ KeyMap map;
+ map.loadDefaults();
+ QSettings s(path, QSettings::IniFormat);
+ map.loadOverrides(s);
+
+ QCOMPARE(map.actionFor(QKeySequence(QStringLiteral("j"))),
+ QStringLiteral("archive"));
+ // An untouched default survives.
+ QCOMPARE(map.actionFor(QKeySequence(QStringLiteral("k"))),
+ QStringLiteral("prev_thread"));
+}
+
+void TestKeyMap::iniAddsNewBinding()
+{
+ QTemporaryDir dir;
+ const QString path = dir.filePath(QStringLiteral("t.conf"));
+ {
+ QSettings s(path, QSettings::IniFormat);
+ s.beginGroup(QStringLiteral("keys"));
+ s.setValue(QStringLiteral("Ctrl+Shift+A"), QStringLiteral("archive"));
+ s.endGroup();
+ }
+
+ KeyMap map;
+ map.loadDefaults();
+ QSettings s(path, QSettings::IniFormat);
+ map.loadOverrides(s);
+
+ QCOMPARE(map.actionFor(QKeySequence(QStringLiteral("Ctrl+Shift+A"))),
+ QStringLiteral("archive"));
+}
+
+void TestKeyMap::chordSequenceParses()
+{
+ QTemporaryDir dir;
+ const QString path = dir.filePath(QStringLiteral("t.conf"));
+ {
+ QSettings s(path, QSettings::IniFormat);
+ s.beginGroup(QStringLiteral("keys"));
+ s.setValue(QStringLiteral("g,i"), QStringLiteral("focus_query"));
+ s.endGroup();
+ }
+
+ KeyMap map;
+ QSettings s(path, QSettings::IniFormat);
+ map.loadOverrides(s);
+
+ const QKeySequence chord = QKeySequence::fromString(QStringLiteral("g,i"));
+ QCOMPARE(chord.count(), 2);
+ QCOMPARE(map.actionFor(chord), QStringLiteral("focus_query"));
+}
+
+void TestKeyMap::unknownActionIsReported()
+{
+ QTemporaryDir dir;
+ const QString path = dir.filePath(QStringLiteral("t.conf"));
+ {
+ QSettings s(path, QSettings::IniFormat);
+ s.beginGroup(QStringLiteral("keys"));
+ s.setValue(QStringLiteral("z"), QStringLiteral("no_such_action"));
+ s.endGroup();
+ }
+
+ KeyMap map;
+ QSettings s(path, QSettings::IniFormat);
+ map.loadOverrides(s);
+
+ // Reported, not fatal, and not bound.
+ QCOMPARE(map.warnings().size(), 1);
+ QVERIFY(map.warnings().first().contains(QStringLiteral("no_such_action")));
+ QVERIFY(map.actionFor(QKeySequence(QStringLiteral("z"))).isEmpty());
+}
+
+void TestKeyMap::invalidSequenceIsReported()
+{
+ QTemporaryDir dir;
+ const QString path = dir.filePath(QStringLiteral("t.conf"));
+ {
+ QSettings s(path, QSettings::IniFormat);
+ s.beginGroup(QStringLiteral("keys"));
+ s.setValue(QStringLiteral("NotAKey++"), QStringLiteral("archive"));
+ s.endGroup();
+ }
+
+ KeyMap map;
+ QSettings s(path, QSettings::IniFormat);
+ map.loadOverrides(s);
+
+ QCOMPARE(map.warnings().size(), 1);
+}
+
+QTEST_MAIN(TestKeyMap)
+#include "test_keymap.moc"