diagrams-fable-qt/tests/tst_properties.cpp
Ilya Ashikhmin 26905a4ba0 feat(core): add network model with typed ports, relations and grouped properties
Data-driven node types (SVG + port config) loaded from JSON, relation-based
connection validation, and a property system supporting string, int, float,
enum, catalogue item(s), color and value-list types grouped by category.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 23:14:11 +02:00

135 lines
5.5 KiB
C++

#include "TestFixtures.h"
#include "core/NetworkModel.h"
#include <QJsonDocument>
#include <QJsonObject>
#include <QSignalSpy>
#include <QTest>
using namespace diag;
class TestProperties : public QObject {
Q_OBJECT
private slots:
void init()
{
fixtures::fillRegistry(m_reg);
delete m_model;
m_model = new NetworkModel(&m_reg, this);
}
void nodeProperty_fallsBackToDefault()
{
const QUuid src = m_model->addNode(QStringLiteral("source"), {0, 0});
QCOMPARE(m_model->nodeProperty(src, "supply").toDouble(), 10.0);
QCOMPARE(m_model->nodeProperty(src, "title").toString(), QString());
m_model->setNodeProperty(src, "supply", 42.5);
QCOMPARE(m_model->nodeProperty(src, "supply").toDouble(), 42.5);
}
void edgeProperty_commonAndRelationSpecs()
{
const QUuid src = m_model->addNode(QStringLiteral("source"), {0, 0});
const QUuid pump = m_model->addNode(QStringLiteral("pump"), {200, 0});
const QUuid e = m_model->addEdge(src, "out", pump, "in");
const auto specs = m_reg.edgeProperties(QStringLiteral("water"));
QStringList ids;
for (const auto& s : specs)
ids << s.id;
// Common presentation specs plus water physics specs.
for (const char* expected : {"title", "lineStyle", "lineWidth", "color",
"headDecoration", "tailDecoration", "length", "diameter",
"flowType", "series"})
QVERIFY2(ids.contains(QLatin1String(expected)), expected);
QCOMPARE(m_model->edgeProperty(e, "lineStyle").toString(), QStringLiteral("Solid"));
QCOMPARE(m_model->edgeProperty(e, "diameter").toDouble(), 100.0);
QCOMPARE(m_model->edgeProperty(e, "flowType").toString(), QStringLiteral("Laminar"));
}
void propertyGroups_present()
{
const auto specs = m_reg.edgeProperties(QStringLiteral("water"));
QStringList groups;
for (const auto& s : specs)
if (!groups.contains(s.group))
groups << s.group;
QVERIFY(groups.contains(QStringLiteral("General")));
QVERIFY(groups.contains(QStringLiteral("Presentation")));
QVERIFY(groups.contains(QStringLiteral("Physics")));
}
void allPropertyTypes_storeAndRead()
{
const QUuid src = m_model->addNode(QStringLiteral("source"), {0, 0});
const QUuid pump = m_model->addNode(QStringLiteral("pump"), {200, 0});
const QUuid e = m_model->addEdge(src, "out", pump, "in");
m_model->setNodeProperty(src, "title", QStringLiteral("Main intake")); // String
m_model->setNodeProperty(src, "tags", QStringList{"district-1", "hp"}); // ValueList
m_model->setNodeProperty(src, "labelColor", QStringLiteral("#ff8800")); // Color
m_model->setEdgeProperty(e, "length", 125.5); // Float
m_model->setEdgeProperty(e, "flowType", QStringLiteral("Turbulent")); // Enum
m_model->setEdgeProperty(e, "series", QStringLiteral("dn50")); // CatalogueItem
QCOMPARE(m_model->nodeProperty(src, "title").toString(), QStringLiteral("Main intake"));
QCOMPARE(m_model->nodeProperty(src, "tags").toStringList(),
(QStringList{"district-1", "hp"}));
QCOMPARE(m_model->nodeProperty(src, "labelColor").toString(), QStringLiteral("#ff8800"));
QCOMPARE(m_model->edgeProperty(e, "length").toDouble(), 125.5);
QCOMPARE(m_model->edgeProperty(e, "flowType").toString(), QStringLiteral("Turbulent"));
QCOMPARE(m_model->edgeProperty(e, "series").toString(), QStringLiteral("dn50"));
}
void catalogue_lookup()
{
const Catalogue* c = m_reg.catalogue(QStringLiteral("pipe_series"));
QVERIFY(c);
QCOMPARE(c->items.size(), 2);
const CatalogueItem* dn50 = c->item(QStringLiteral("dn50"));
QVERIFY(dn50);
QCOMPARE(dn50->attributes.value(QStringLiteral("diameter")).toInt(), 50);
QVERIFY(!c->item(QStringLiteral("missing")));
}
void propertySpec_parsesFromJson()
{
const auto doc = QJsonDocument::fromJson(R"({
"id": "materials", "name": "Materials", "group": "Physics",
"type": "catalogueItems", "catalogue": "pipe_series",
"default": ["dn50"], "readOnly": false
})");
const PropertySpec s = propertySpecFromJson(doc.object());
QCOMPARE(s.type, PropertyType::CatalogueItems);
QCOMPARE(s.catalogueId, QStringLiteral("pipe_series"));
QCOMPARE(s.defaultValue.toStringList(), QStringList{"dn50"});
const auto doc2 = QJsonDocument::fromJson(R"({
"id": "mode", "type": "enum", "values": ["A", "B"], "default": "B",
"min": 1, "max": 5
})");
const PropertySpec s2 = propertySpecFromJson(doc2.object());
QCOMPARE(s2.enumValues, (QStringList{"A", "B"}));
QCOMPARE(s2.defaultValue.toString(), QStringLiteral("B"));
QCOMPARE(s2.min, 1.0);
QCOMPARE(s2.max, 5.0);
}
void propertyChange_emitsSignal()
{
const QUuid src = m_model->addNode(QStringLiteral("source"), {0, 0});
QSignalSpy spy(m_model, &NetworkModel::nodeChanged);
m_model->setNodeProperty(src, "title", QStringLiteral("A"));
m_model->setNodeProperty(src, "title", QStringLiteral("A")); // no-op
QCOMPARE(spy.count(), 1);
}
private:
TypeRegistry m_reg;
NetworkModel* m_model = nullptr;
};
QTEST_GUILESS_MAIN(TestProperties)
#include "tst_properties.moc"