#include "TestFixtures.h" #include "core/NetworkModel.h" #include "scene/Commands.h" #include #include using namespace diag; class TestUndo : public QObject { Q_OBJECT private slots: void init() { fixtures::fillRegistry(m_reg); delete m_model; m_model = new NetworkModel(&m_reg, this); m_undo.clear(); } void addNode_undoRedo() { auto* cmd = new AddNodeCommand(m_model, QStringLiteral("pump"), {100, 100}); m_undo.push(cmd); const QUuid id = cmd->nodeId(); QVERIFY(m_model->node(id)); m_undo.undo(); QVERIFY(!m_model->node(id)); m_undo.redo(); QVERIFY(m_model->node(id)); // same id restored } void addEdge_undoRedo() { const QUuid src = m_model->addNode(QStringLiteral("source"), {0, 0}); const QUuid pump = m_model->addNode(QStringLiteral("pump"), {200, 0}); auto* cmd = new AddEdgeCommand(m_model, src, QStringLiteral("out"), pump, QStringLiteral("in")); m_undo.push(cmd); QVERIFY(m_model->edge(cmd->edgeId())); m_undo.undo(); QCOMPARE(m_model->edgeIds().size(), 0); m_undo.redo(); QCOMPARE(m_model->edgeIds().size(), 1); } void moveNodes_undoRestoresPositions() { const QUuid a = m_model->addNode(QStringLiteral("pump"), {0, 0}); const QUuid b = m_model->addNode(QStringLiteral("pump"), {100, 0}); m_undo.push(new MoveNodesCommand( m_model, {{a, {0, 0}, {40, 40}}, {b, {100, 0}, {140, 40}}})); QCOMPARE(m_model->node(a)->pos, QPointF(40, 40)); m_undo.undo(); QCOMPARE(m_model->node(a)->pos, QPointF(0, 0)); QCOMPARE(m_model->node(b)->pos, QPointF(100, 0)); } void removeItems_undoRestoresEdgesAndProps() { const QUuid src = m_model->addNode(QStringLiteral("source"), {0, 0}); const QUuid pump = m_model->addNode(QStringLiteral("pump"), {200, 0}); const QUuid c = m_model->addNode(QStringLiteral("consumer"), {400, 0}); const QUuid e1 = m_model->addEdge(src, "out", pump, "in"); const QUuid e2 = m_model->addEdge(pump, "out", c, "in"); m_model->setEdgeProperty(e1, "diameter", 150.0); m_model->setNodeProperty(pump, "title", QStringLiteral("P-1")); // Deleting the pump takes both attached edges with it. m_undo.push(new RemoveItemsCommand(m_model, {pump}, {})); QCOMPARE(m_model->nodeIds().size(), 2); QCOMPARE(m_model->edgeIds().size(), 0); m_undo.undo(); QCOMPARE(m_model->nodeIds().size(), 3); QCOMPARE(m_model->edgeIds().size(), 2); QVERIFY(m_model->edge(e2)); QCOMPARE(m_model->edgeProperty(e1, "diameter").toDouble(), 150.0); QCOMPARE(m_model->nodeProperty(pump, "title").toString(), QStringLiteral("P-1")); } void setProperty_undoRedo() { const QUuid src = m_model->addNode(QStringLiteral("source"), {0, 0}); m_undo.push(new SetPropertyCommand(m_model, SetPropertyCommand::NodeTarget, src, QStringLiteral("supply"), 25.0)); QCOMPARE(m_model->nodeProperty(src, "supply").toDouble(), 25.0); m_undo.undo(); QCOMPARE(m_model->nodeProperty(src, "supply").toDouble(), 10.0); // spec default } void paste_remapsIdsAndOffsets() { const QUuid src = m_model->addNode(QStringLiteral("source"), {0, 0}); const QUuid pump = m_model->addNode(QStringLiteral("pump"), {200, 0}); m_model->addEdge(src, "out", pump, "in"); const QJsonObject frag = fragmentFromSelection(*m_model, {src, pump}, {}); auto* cmd = new PasteCommand(m_model, frag, {20, 20}); m_undo.push(cmd); QCOMPARE(m_model->nodeIds().size(), 4); QCOMPARE(m_model->edgeIds().size(), 2); // inner edge duplicated const QList pasted = cmd->pastedNodes(); QCOMPARE(pasted.size(), 2); QVERIFY(!pasted.contains(src)); QCOMPARE(m_model->node(pasted.first())->pos, m_model->node(src)->pos + QPointF(20, 20)); m_undo.undo(); QCOMPARE(m_model->nodeIds().size(), 2); QCOMPARE(m_model->edgeIds().size(), 1); m_undo.redo(); QCOMPARE(m_model->nodeIds().size(), 4); } void copyFragment_skipsDanglingEdges() { const QUuid src = m_model->addNode(QStringLiteral("source"), {0, 0}); const QUuid pump = m_model->addNode(QStringLiteral("pump"), {200, 0}); m_model->addEdge(src, "out", pump, "in"); // Only the pump is copied: the edge to the source must not paste. const QJsonObject frag = fragmentFromSelection(*m_model, {pump}, {}); auto* cmd = new PasteCommand(m_model, frag, {0, 40}); m_undo.push(cmd); QCOMPARE(m_model->nodeIds().size(), 3); QCOMPARE(m_model->edgeIds().size(), 1); } private: TypeRegistry m_reg; NetworkModel* m_model = nullptr; QUndoStack m_undo; }; QTEST_GUILESS_MAIN(TestUndo) #include "tst_undo.moc"