From e877c410ad2c08fc53827c462e6b01a23aedac8e Mon Sep 17 00:00:00 2001 From: Alex Bilger Date: Fri, 10 Jan 2025 15:39:39 +0100 Subject: [PATCH] [Simulation] Trigger error when Node already contains component --- .../Core/src/sofa/simulation/Node.cpp | 19 ++++++- .../Simulation/Graph/test/Node_test.cpp | 55 +++++++++++++++++++ .../Simulation/Graph/test/Simulation_test.cpp | 3 +- 3 files changed, 74 insertions(+), 3 deletions(-) diff --git a/Sofa/framework/Simulation/Core/src/sofa/simulation/Node.cpp b/Sofa/framework/Simulation/Core/src/sofa/simulation/Node.cpp index f4f2ce91e80..9d9c1739791 100644 --- a/Sofa/framework/Simulation/Core/src/sofa/simulation/Node.cpp +++ b/Sofa/framework/Simulation/Core/src/sofa/simulation/Node.cpp @@ -1024,8 +1024,25 @@ void Node::setSleeping(bool val) } } +template +void checkAlreadyContains(Node& self, LinkType& link, Component* obj) +{ + if constexpr (!LinkType::IsMultiLink) + { + if (link != obj && link != nullptr) + { + static const auto componentClassName = Component::GetClass()->className; + msg_error(&self) << "Trying to add a " << componentClassName << " ('" << obj->getName() << "' " << obj << ")" + << " into the Node '" << self.getPathName() + << "', whereas it already contains one ('" << link->getName() << "' " << link.get() << ")." + << " Only one " << componentClassName << " is permitted in a Node. The previous " + << componentClassName << " is replaced and the behavior is undefined."; + } + } +} + #define NODE_DEFINE_SEQUENCE_ACCESSOR( CLASSNAME, FUNCTIONNAME, SEQUENCENAME ) \ - void Node::add##FUNCTIONNAME( CLASSNAME* obj ) { SEQUENCENAME.add(obj); } \ + void Node::add##FUNCTIONNAME( CLASSNAME* obj ) { checkAlreadyContains(*this, SEQUENCENAME, obj); SEQUENCENAME.add(obj); } \ void Node::remove##FUNCTIONNAME( CLASSNAME* obj ) { SEQUENCENAME.remove(obj); } NODE_DEFINE_SEQUENCE_ACCESSOR( sofa::core::behavior::BaseAnimationLoop, AnimationLoop, animationManager ) diff --git a/Sofa/framework/Simulation/Graph/test/Node_test.cpp b/Sofa/framework/Simulation/Graph/test/Node_test.cpp index bb07c4221c1..294adb1fe94 100644 --- a/Sofa/framework/Simulation/Graph/test/Node_test.cpp +++ b/Sofa/framework/Simulation/Graph/test/Node_test.cpp @@ -19,6 +19,8 @@ * * * Contact information: contact@sofa-framework.org * ******************************************************************************/ +#include +#include #include using sofa::testing::BaseSimulationTest ; @@ -33,6 +35,9 @@ namespace sofa TEST( Node_test, getPathName) { + // required to be able to use EXPECT_MSG_NOEMIT and EXPECT_MSG_EMIT + sofa::helper::logging::MessageDispatcher::addHandler(sofa::testing::MainGtestMessageHandler::getInstance() ) ; + /* create trivial DAG : * * A @@ -90,6 +95,9 @@ TEST(Node_test, addObjectAtFront) TEST(Node_test, addObjectPreventingSharedContext) { + // required to be able to use EXPECT_MSG_NOEMIT and EXPECT_MSG_EMIT + sofa::helper::logging::MessageDispatcher::addHandler(sofa::testing::MainGtestMessageHandler::getInstance() ) ; + const sofa::core::sptr root = sofa::simpleapi::createNode("root"); const BaseObject::SPtr A = core::objectmodel::New("A"); @@ -177,6 +185,53 @@ TEST(Node_test, getObjectsStdUnorderedSet) EXPECT_NE(objects.find(B.get()), objects.end()); } +class CounterVisitor : public simulation::MechanicalVisitor +{ +public: + using MechanicalVisitor::MechanicalVisitor; + + Result fwdMechanicalState(simulation::Node* node, sofa::core::behavior::BaseMechanicalState* state) override + { + SOFA_UNUSED(node); + SOFA_UNUSED(state); + m_counter++; + return Result::RESULT_CONTINUE; + } + + Result fwdMappedMechanicalState(simulation::Node* node, sofa::core::behavior::BaseMechanicalState* state) override + { + SOFA_UNUSED(node); + SOFA_UNUSED(state); + ++m_counter; + return Result::RESULT_CONTINUE; + } + + std::size_t m_counter = 0; +}; + +TEST(Node_test, twoMechanicalStatesInTheSameNode) +{ + // required to be able to use EXPECT_MSG_NOEMIT and EXPECT_MSG_EMIT + sofa::helper::logging::MessageDispatcher::addHandler(sofa::testing::MainGtestMessageHandler::getInstance() ) ; + + const sofa::core::sptr root = sofa::simpleapi::createNode("root"); + + const auto plugins = testing::makeScopedPlugin({Sofa.Component.StateContainer}); + sofa::simpleapi::createObject(root, "MechanicalObject", {{"template", "Vec3"}, {"name", "A"}}); + + EXPECT_MSG_EMIT(Error); + sofa::simpleapi::createObject(root, "MechanicalObject", {{"template", "Vec3"}, {"name", "B"}}); + + //the last added state is the one in Node + EXPECT_EQ(root->mechanicalState->getName(), "B"); + + CounterVisitor visitor(core::MechanicalParams::defaultInstance()); + root->executeVisitor(&visitor); + + //only one of the two added states is visited + EXPECT_EQ(visitor.m_counter, 1); +} + }// namespace sofa diff --git a/Sofa/framework/Simulation/Graph/test/Simulation_test.cpp b/Sofa/framework/Simulation/Graph/test/Simulation_test.cpp index 53507e22024..8fad0b75081 100644 --- a/Sofa/framework/Simulation/Graph/test/Simulation_test.cpp +++ b/Sofa/framework/Simulation/Graph/test/Simulation_test.cpp @@ -223,10 +223,9 @@ struct Scene_test: public NumericTest root = simulation::getSimulation()->createNewGraph("root"); root->addObject(core::objectmodel::New >()); - typename UniformMass3::SPtr uniformMass = core::objectmodel::New(); + typename UniformMass3::SPtr uniformMass = core::objectmodel::New>(); uniformMass->d_totalMass.setValue(1.0); root->addObject(uniformMass); - root->addObject(core::objectmodel::New >()); const simulation::Node::SPtr child = simulation::getSimulation()->createNewNode("child"); root->addChild(child);