#include <nodes/FlowScene>#include <functional>#include <memory>#include <utility>#include <vector>#include <nodes/Node>#include <nodes/NodeDataModel>#include <catch2/catch.hpp>#include "ApplicationSetup.hpp"#include "Stringify.hpp"#include "StubNodeDataModel.hpp"using QtNodes::Connection;using QtNodes::DataModelRegistry;using QtNodes::FlowScene;using QtNodes::Node;using QtNodes::NodeData;using QtNodes::NodeDataModel;using QtNodes::NodeDataType;using QtNodes::PortIndex;using QtNodes::PortType;TEST_CASE("FlowScene triggers connections created or deleted", "[gui]"){struct MockDataModel : StubNodeDataModel{unsigned int nPorts(PortType) const override { return 1; }voidinputConnectionCreated(Connection const&) override{inputCreatedCalledCount++;}voidinputConnectionDeleted(Connection const&) override{inputDeletedCalledCount++;}voidoutputConnectionCreated(Connection const&) override{outputCreatedCalledCount++;}voidoutputConnectionDeleted(Connection const&) override{outputDeletedCalledCount++;}int inputCreatedCalledCount = 0;int inputDeletedCalledCount = 0;int outputCreatedCalledCount = 0;int outputDeletedCalledCount = 0;voidresetCallCounts(){inputCreatedCalledCount = 0;inputDeletedCalledCount = 0;outputCreatedCalledCount = 0;outputDeletedCalledCount = 0;}};auto setup = applicationSetup();FlowScene scene;Node& fromNode = scene.createNode(std::make_unique<MockDataModel>());Node& toNode = scene.createNode(std::make_unique<MockDataModel>());Node& unrelatedNode = scene.createNode(std::make_unique<MockDataModel>());auto& fromNgo = fromNode.nodeGraphicsObject();auto& toNgo = toNode.nodeGraphicsObject();auto& unrelatedNgo = unrelatedNode.nodeGraphicsObject();fromNgo.setPos(0, 0);toNgo.setPos(200, 20);unrelatedNgo.setPos(-100, -100);auto& from = dynamic_cast<MockDataModel&>(*fromNode.nodeDataModel());auto& to = dynamic_cast<MockDataModel&>(*toNode.nodeDataModel());auto& unrelated = dynamic_cast<MockDataModel&>(*unrelatedNode.nodeDataModel());SECTION("creating half a connection (not finishing the connection)"){auto connection = scene.createConnection(PortType::Out, fromNode, 0);CHECK(from.inputCreatedCalledCount == 0);CHECK(from.outputCreatedCalledCount == 0);CHECK(to.inputCreatedCalledCount == 0);CHECK(to.outputCreatedCalledCount == 0);CHECK(unrelated.inputCreatedCalledCount == 0);CHECK(unrelated.outputCreatedCalledCount == 0);scene.deleteConnection(*connection);}struct Creation{std::string name;std::function<std::shared_ptr<Connection>()> createConnection;};Creation sceneCreation{"scene.createConnection",[&] { return scene.createConnection(toNode, 0, fromNode, 0); }};Creation partialCreation{"scene.createConnection-by partial", [&] {auto connection = scene.createConnection(PortType::Out, fromNode, 0);connection->setNodeToPort(toNode, PortType::In, 0);return connection;}};struct Deletion{std::string name;std::function<void(Connection & connection)> deleteConnection;};Deletion sceneDeletion{"scene.deleteConnection",[&](Connection & c) { scene.deleteConnection(c); }};Deletion partialDragDeletion{"scene-deleteConnectionByDraggingOff",[&](Connection & c){PortIndex portIndex = c.getPortIndex(PortType::In);Node * node = c.getNode(PortType::In);node->nodeState().getEntries(PortType::In)[portIndex].clear();c.clearNode(PortType::In);}};SECTION("creating a connection"){std::vector<Creation> cases({sceneCreation, partialCreation});for (Creation const& create : cases){SECTION(create.name){auto connection = create.createConnection();CHECK(from.inputCreatedCalledCount == 0);CHECK(from.outputCreatedCalledCount == 1);CHECK(to.inputCreatedCalledCount == 1);CHECK(to.outputCreatedCalledCount == 0);CHECK(unrelated.inputCreatedCalledCount == 0);CHECK(unrelated.outputCreatedCalledCount == 0);scene.deleteConnection(*connection);}}}SECTION("deleting a connection"){std::vector<Deletion> cases({sceneDeletion, partialDragDeletion});for (auto const& deletion : cases){SECTION("deletion: " + deletion.name){Connection & connection = *sceneCreation.createConnection();from.resetCallCounts();to.resetCallCounts();deletion.deleteConnection(connection);// Here the Connection reference becomes danglingCHECK(from.inputDeletedCalledCount == 0);CHECK(from.outputDeletedCalledCount == 1);CHECK(to.inputDeletedCalledCount == 1);CHECK(to.outputDeletedCalledCount == 0);CHECK(unrelated.inputDeletedCalledCount == 0);CHECK(unrelated.outputDeletedCalledCount == 0);}}}}TEST_CASE("FlowScene's DataModelRegistry outlives nodes and connections", "[asan][gui]"){class MockDataModel : public StubNodeDataModel{public:MockDataModel(int* const& incrementOnDestruction): incrementOnDestruction(incrementOnDestruction){}~MockDataModel(){(*incrementOnDestruction)++;}// The reference ensures that we point into the memory that would be free'd// if the DataModelRegistry doesn't outlive this nodeint* const& incrementOnDestruction;};struct MockDataModelCreator{MockDataModelCreator(int* shouldBeAliveWhenAssignedTo): shouldBeAliveWhenAssignedTo(shouldBeAliveWhenAssignedTo){}autooperator()() const{return std::make_unique<MockDataModel>(shouldBeAliveWhenAssignedTo);}int* shouldBeAliveWhenAssignedTo;};int modelsDestroyed = 0;// Introduce a new scope, so that modelsDestroyed will be alive even after the// FlowScene is destroyed.{auto setup = applicationSetup();auto registry = std::make_shared<DataModelRegistry>();registry->registerModel(MockDataModelCreator(&modelsDestroyed));modelsDestroyed = 0;FlowScene scene(std::move(registry));auto& node = scene.createNode(scene.registry().create("name"));// On destruction, if this `node` outlives its MockDataModelCreator,// (if it outlives the DataModelRegistry), then we trigger undefined// behavior through use-after-free. ASAN will catch that.}CHECK(modelsDestroyed == 1);}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。