Something went wrong. Try again.
The agentic engineering control plane for the posthuman future
Something went wrong. Try again.
7.6 kB · 208 lines
C++
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209// XenoSettings persistence tests: the per-command key sequence override// map. Overrides round-trip through save/load via the settings JSON at// ${XDG_CONFIG_HOME}/xenomorphic/settings.json; init() points// XDG_CONFIG_HOME at a fresh temp dir per test so the user's real settings// file is never touched.
#include <QtTest/QtTest>
#include "XenoSettings.h"
#include <QDir>#include <QFile>#include <QJsonDocument>#include <QJsonObject>#include <QSignalSpy>#include <QTemporaryDir>
class TestXenoSettings : public QObject { Q_OBJECT
std::unique_ptr<XenoSettings> m_settings; // Recreated per test in init(): tests share one object, so the config // dir must reset too or a previous test's settings.json leaks in. std::unique_ptr<QTemporaryDir> m_configDir;
[[nodiscard]] QString settingsPath() const { return QDir(m_configDir->path()).filePath( QStringLiteral("xenomorphic/settings.json")); }
private slots: void init(); // Override round-trips through save and load. void overrideRoundTripsThroughSaveAndLoad(); // An absent entry means default: no key, and empty overrides load to an // empty map. void absentEntryMeansDefault(); // Clearing an entry (single and all) removes it and it survives reload. void clearingRestoresDefault(); // Malformed JSON entries (non-string, empty values) are dropped on load. void malformedEntriesDroppedOnLoad(); // The editor's own font size and line height round-trip through save/load. void editorFontSettingsRoundTrip(); // The line-height setter clamps to [1.0, 3.0], and the clamped value // survives a reload. void editorLineHeightClamps(); // A settings file without the editor keys leaves the defaults (0.0, 1.3). void absentEditorKeysUseDefaults();};
void TestXenoSettings::init(){ m_configDir = std::make_unique<QTemporaryDir>(); QVERIFY(m_configDir->isValid()); qputenv("XDG_CONFIG_HOME", m_configDir->path().toUtf8()); m_settings = std::make_unique<XenoSettings>();}
void TestXenoSettings::overrideRoundTripsThroughSaveAndLoad(){ m_settings->setCommandSequence(QStringLiteral("splitHorizontal"), QStringLiteral("Ctrl+Alt+H")); QCOMPARE(m_settings->commandSequences().value( QStringLiteral("splitHorizontal")), QStringLiteral("Ctrl+Alt+H"));
// The JSON file carries the key and value. QFile file(settingsPath()); QVERIFY(file.open(QIODevice::ReadOnly)); const QJsonObject object = QJsonDocument::fromJson(file.readAll()).object(); file.close(); const QJsonValue entry = object.value(QStringLiteral("commandSequences")); QVERIFY(entry.isObject()); QCOMPARE(entry.toObject().value(QStringLiteral("splitHorizontal")), QStringLiteral("Ctrl+Alt+H"));
// A fresh instance loads the override back. const XenoSettings reloaded; QCOMPARE(reloaded.commandSequences().value( QStringLiteral("splitHorizontal")), QStringLiteral("Ctrl+Alt+H"));}
void TestXenoSettings::absentEntryMeansDefault(){ // Nothing written: no overrides. QVERIFY(m_settings->commandSequences().isEmpty()); // Existing settings keys (fontFamily) load; commandSequences absent from // the file stays empty. const XenoSettings reloaded; QVERIFY(reloaded.commandSequences().isEmpty());}
void TestXenoSettings::clearingRestoresDefault(){ m_settings->setCommandSequence(QStringLiteral("quit"), QStringLiteral("Ctrl+Shift+Q")); m_settings->setCommandSequence(QStringLiteral("closePane"), QStringLiteral("Ctrl+Alt+K")); QCOMPARE(m_settings->commandSequences().size(), 2);
// Clear one. m_settings->clearCommandSequence(QStringLiteral("quit")); QVERIFY(!m_settings->commandSequences().contains(QStringLiteral("quit"))); QCOMPARE(m_settings->commandSequences().value(QStringLiteral("closePane")), QStringLiteral("Ctrl+Alt+K"));
const XenoSettings afterSingle; QVERIFY(!afterSingle.commandSequences().contains(QStringLiteral("quit")));
// Clear all. m_settings->clearAllCommandSequences(); QVERIFY(m_settings->commandSequences().isEmpty()); const XenoSettings afterAll; QVERIFY(afterAll.commandSequences().isEmpty());
// Setting "" clears too. m_settings->setCommandSequence(QStringLiteral("closePane"), QString()); QVERIFY(m_settings->commandSequences().isEmpty());}
void TestXenoSettings::malformedEntriesDroppedOnLoad(){ QDir().mkpath(QDir(m_configDir->path()).filePath(QStringLiteral("xenomorphic"))); QFile file(settingsPath()); QVERIFY(file.open(QIODevice::WriteOnly)); file.write(QByteArrayLiteral( R"({"commandSequences": {"good": "Ctrl+G", "empty": "", "bad": 12, "": "Ctrl+X"}})")); file.close();
const XenoSettings reloaded; QCOMPARE(reloaded.commandSequences().size(), 1); QCOMPARE(reloaded.commandSequences().value(QStringLiteral("good")), QStringLiteral("Ctrl+G"));}
void TestXenoSettings::editorFontSettingsRoundTrip(){ QSignalSpy sizeSpy(m_settings.get(), &XenoSettings::editorFontSizeChanged); QSignalSpy heightSpy(m_settings.get(), &XenoSettings::editorLineHeightChanged); QSignalSpy wrapSpy(m_settings.get(), &XenoSettings::editorWrapChanged);
m_settings->setEditorFontSize(13.5); m_settings->setEditorLineHeight(1.6); m_settings->setEditorWrap(true); QCOMPARE(sizeSpy.count(), 1); QCOMPARE(heightSpy.count(), 1); QCOMPARE(wrapSpy.count(), 1); QVERIFY(qFuzzyCompare(m_settings->editorFontSize(), 13.5)); QVERIFY(qFuzzyCompare(m_settings->editorLineHeight(), 1.6)); QVERIFY(m_settings->editorWrap());
// The JSON file carries both keys. QFile file(settingsPath()); QVERIFY(file.open(QIODevice::ReadOnly)); const QJsonObject object = QJsonDocument::fromJson(file.readAll()).object(); file.close(); QVERIFY(qFuzzyCompare( object.value(QStringLiteral("editorFontSize")).toDouble(), 13.5)); QVERIFY(qFuzzyCompare( object.value(QStringLiteral("editorLineHeight")).toDouble(), 1.6)); QVERIFY(object.value(QStringLiteral("editorWrap")).toBool());
// A fresh instance loads both back. const XenoSettings reloaded; QVERIFY(qFuzzyCompare(reloaded.editorFontSize(), 13.5)); QVERIFY(qFuzzyCompare(reloaded.editorLineHeight(), 1.6)); QVERIFY(reloaded.editorWrap());}
void TestXenoSettings::editorLineHeightClamps(){ m_settings->setEditorLineHeight(0.5); QVERIFY(qFuzzyCompare(m_settings->editorLineHeight(), 1.0)); m_settings->setEditorLineHeight(9.0); QVERIFY(qFuzzyCompare(m_settings->editorLineHeight(), 3.0));
// The file carries the clamped value, and so does a reload. const XenoSettings reloaded; QVERIFY(qFuzzyCompare(reloaded.editorLineHeight(), 3.0));}
void TestXenoSettings::absentEditorKeysUseDefaults(){ // A fresh object loaded no file, so the in-class defaults stand. QVERIFY(qFuzzyIsNull(m_settings->editorFontSize())); QVERIFY(qFuzzyCompare(m_settings->editorLineHeight(), 1.3)); QVERIFY(!m_settings->editorWrap());
// A file that omits both keys must not disturb them either. QDir().mkpath(QDir(m_configDir->path()).filePath(QStringLiteral("xenomorphic"))); QFile file(settingsPath()); QVERIFY(file.open(QIODevice::WriteOnly)); file.write(QByteArrayLiteral(R"({"fontSize": 12.0})")); file.close();
const XenoSettings reloaded; QVERIFY(qFuzzyIsNull(reloaded.editorFontSize())); QVERIFY(qFuzzyCompare(reloaded.editorLineHeight(), 1.3)); QVERIFY(!reloaded.editorWrap());}
QTEST_MAIN(TestXenoSettings)#include "tst_settings.moc"