Files
sloposk/tests/tst_layoutloader.cpp
T
ericek111andClaude Opus 5.5 cfea9ece99 Reject more template mistakes
Empty keys, duplicate layer names and section ids, repeat on non-keysym
keys and stretch on letters/keys placeholders.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 10:13:56 +02:00

161 lines
5.7 KiB
C++

#include "TestLayouts.h"
#include "layout/KeyGeometry.h"
#include "layout/LayoutLoader.h"
#include <QTest>
#include <xkbcommon/xkbcommon-keysyms.h>
using namespace Qt::StringLiterals;
class TestLayoutLoader : public QObject
{
Q_OBJECT
private Q_SLOTS:
void parsesStringAndObjectKeys()
{
QString error;
const auto layout = LayoutLoader::parse(R"({
"id": "t", "layers": [{"name": "letters", "rows": [[
"q",
{"label": "e", "flick": "3", "alternates": ["é", "è"]},
{"type": "backspace", "width": 1.5}
]]}]
})", &error);
QVERIFY2(layout, qPrintable(error));
const Row &row = layout->layers.first().rows.first();
QCOMPARE(row.size(), 3);
QCOMPARE(row[0].label, u"q");
QCOMPARE(row[1].flick, u"3");
QCOMPARE(row[1].alternates, QStringList({u"é"_s, u"è"_s}));
QCOMPARE(row[2].type, KeyType::Backspace);
QCOMPARE(row[2].width, 1.5);
}
void rejectsUnknownKeyType()
{
QString error;
QVERIFY(!LayoutLoader::parse(R"({"id": "t", "layers": [{"name": "l", "rows": [[{"type": "warp"}]]}]})", &error));
QVERIFY(error.contains(u"warp"));
}
void rejectsInvalidTemplates_data()
{
QTest::addColumn<QByteArray>("json");
QTest::addColumn<QString>("why");
const auto row = [](const char *key) {
return QByteArray(R"({"id": "t", "layers": [{"name": "l", "rows": [[)") + key + "]]}]}";
};
QTest::newRow("empty key") << row(R"("")") << u"empty"_s;
QTest::newRow("repeat") << row(R"({"type": "backspace", "repeat": true})") << u"repeat"_s;
QTest::newRow("stretch") << row(R"({"type": "letters", "row": 0, "stretch": true})") << u"stretch"_s;
QTest::newRow("layer twice")
<< QByteArray(R"({"id": "t", "layers": [{"name": "l", "rows": [["a"]]}, {"name": "l", "rows": [["b"]]}]})")
<< u"duplicate layer"_s;
QTest::newRow("section twice")
<< QByteArray(R"({"id": "t", "layers": [{"name": "l", "rows": [["a"]]}], "sections": [)"
R"({"id": "s", "rows": [["b"]]}, {"id": "s", "rows": [["c"]]}]})")
<< u"duplicate section"_s;
}
void rejectsInvalidTemplates()
{
QFETCH(QByteArray, json);
QFETCH(QString, why);
QString error;
QVERIFY(!LayoutLoader::parse(json, &error));
QVERIFY2(error.contains(why), qPrintable(error));
}
void rejectsMissingId()
{
QString error;
QVERIFY(!LayoutLoader::parse(R"({"layers": []})", &error));
}
void validateCatchesUnknownLayerTarget()
{
QString error;
const auto layout = LayoutLoader::parse(
R"({"id": "t", "layers": [{"name": "l", "rows": [[{"type": "layer", "target": "nope"}]]}]})", &error);
QVERIFY(layout);
QVERIFY(!LayoutLoader::validate(*layout, &error));
}
void templateIsValid()
{
QString error;
QVERIFY2(LayoutLoader::validate(TestLayouts::builtinTemplate(), &error), qPrintable(error));
}
void rejectsLettersWithoutRow()
{
QString error;
QVERIFY(!LayoutLoader::parse(R"({"id": "t", "layers": [{"name": "l", "rows": [[{"type": "letters"}]]}]})",
&error));
}
// Stretch keys must even out rows whatever the keymap's letter counts.
void builtLayersHaveEvenRows_data()
{
QTest::addColumn<QString>("xkbLayout");
QTest::addColumn<KeyboardMode>("mode");
for (const char *layout : {"us", "sk", "se", "de", "fr", "ru", "gr"}) {
QTest::addRow("%s compact", layout) << QString::fromLatin1(layout) << KeyboardMode::Compact;
QTest::addRow("%s full", layout) << QString::fromLatin1(layout) << KeyboardMode::Full;
}
}
void builtLayersHaveEvenRows()
{
QFETCH(QString, xkbLayout);
QFETCH(KeyboardMode, mode);
const Layout layout = TestLayouts::build(qPrintable(xkbLayout), mode);
for (const Layer &layer : layout.layers) {
const QList<QRectF> rects = computeKeyRects(layer, {1000, 300});
qsizetype i = 0;
for (const Row &row : layer.rows) {
const QRectF first = rects[i];
const QRectF last = rects[i + row.size() - 1];
QVERIFY2(qAbs(first.left() - rects.first().left()) < 0.01, qPrintable(layer.name));
QVERIFY2(qAbs(last.right() - rects[layer.rows.first().size() - 1].right()) < 0.01,
qPrintable(layer.name));
i += row.size();
}
}
}
void templateHasKeySections()
{
const Layout layout = TestLayouts::builtinTemplate();
QStringList ids;
for (const Section &section : layout.sections)
ids.append(section.id);
QCOMPARE(ids, (QStringList{u"function"_s, u"navigation"_s, u"numpad"_s}));
}
void parsesKeysymKeys()
{
QString error;
const auto layout = LayoutLoader::parse(
R"({"id": "t", "layers": [{"name": "l", "rows": [[{"type": "keysym", "keysym": "Home", "repeat": true}]]}]})",
&error);
QVERIFY2(layout, qPrintable(error));
const KeyDef &key = layout->layers[0].rows[0][0];
QCOMPARE(key.keysym, quint32(XKB_KEY_Home));
QCOMPARE(key.label, u"Home"); // the keysym name unless labelled
QVERIFY(key.repeat);
}
void rejectsUnknownKeysym()
{
QString error;
QVERIFY(!LayoutLoader::parse(
R"({"id": "t", "layers": [{"name": "l", "rows": [[{"type": "keysym", "keysym": "Nope"}]]}]})", &error));
}
};
QTEST_GUILESS_MAIN(TestLayoutLoader)
#include "tst_layoutloader.moc"