Skip to content

Commit e2b9428

Browse files
authored
[Testing] Mitigate plugin import leakage between tests (#5161)
* [Testing] ScopedPlugin * add version with lists * unload plugins at tear down * use ScopedPlugin in a test as an example * support multiple calls to loadPlugins
1 parent ca66554 commit e2b9428

File tree

9 files changed

+222
-16
lines changed

9 files changed

+222
-16
lines changed

Sofa/Component/Engine/Select/tests/BoxROI_test.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,12 @@ struct BoxROITest : public sofa::testing::BaseTest
7070
Node::SPtr m_node;
7171
typename TheBoxROI::SPtr m_boxroi;
7272

73-
void SetUp() override
73+
void onSetUp() override
7474
{
75-
sofa::simpleapi::importPlugin("Sofa.Component.StateContainer");
76-
sofa::simpleapi::importPlugin("Sofa.Component.Topology.Container.Dynamic");
77-
sofa::simpleapi::importPlugin("Sofa.Component.Engine.Select");
75+
this->loadPlugins({
76+
"Sofa.Component.StateContainer",
77+
"Sofa.Component.Topology.Container.Dynamic",
78+
"Sofa.Component.Engine.Select"});
7879

7980
m_simu = sofa::simulation::getSimulation();
8081
ASSERT_NE(m_simu, nullptr);
@@ -86,7 +87,7 @@ struct BoxROITest : public sofa::testing::BaseTest
8687
m_node->addObject(m_boxroi);
8788
}
8889

89-
void TearDown() override
90+
void onTearDown() override
9091
{
9192
if (m_root != nullptr){
9293
sofa::simulation::node::unload(m_root);

Sofa/Component/LinearSolver/Direct/tests/SparseLDLSolver_test.cpp

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,10 @@ TEST(SparseLDLSolver, EmptyMState)
5757

5858
const sofa::simulation::Node::SPtr root = sofa::simulation::getSimulation()->createNewGraph("root");
5959

60-
sofa::simpleapi::importPlugin("Sofa.Component.LinearSolver.Direct");
61-
sofa::simpleapi::importPlugin("Sofa.Component.ODESolver.Backward");
62-
sofa::simpleapi::importPlugin("Sofa.Component.StateContainer");
60+
const auto plugins = sofa::testing::makeScopedPlugin({
61+
"Sofa.Component.LinearSolver.Direct",
62+
"Sofa.Component.ODESolver.Backward",
63+
"Sofa.Component.StateContainer"});
6364

6465
sofa::simpleapi::createObject(root, "DefaultAnimationLoop");
6566
sofa::simpleapi::createObject(root, "EulerImplicitSolver");
@@ -83,14 +84,15 @@ TEST(SparseLDLSolver, TopologyChangeEmptyMState)
8384
// required to be able to use EXPECT_MSG_NOEMIT and EXPECT_MSG_EMIT
8485
sofa::helper::logging::MessageDispatcher::addHandler(sofa::testing::MainGtestMessageHandler::getInstance() ) ;
8586

86-
const sofa::simulation::Node::SPtr root = sofa::simulation::getSimulation()->createNewGraph("root");
87+
const sofa::simulation::Node::SPtr root = sofa::simulation::getSimulation()->createNewGraph("root");
8788

88-
sofa::simpleapi::importPlugin("Sofa.Component.LinearSolver.Direct");
89-
sofa::simpleapi::importPlugin("Sofa.Component.Mass");
90-
sofa::simpleapi::importPlugin("Sofa.Component.ODESolver.Backward");
91-
sofa::simpleapi::importPlugin("Sofa.Component.StateContainer");
92-
sofa::simpleapi::importPlugin("Sofa.Component.Topology.Container.Dynamic");
93-
sofa::simpleapi::importPlugin("Sofa.Component.Topology.Utility");
89+
const auto plugins = sofa::testing::makeScopedPlugin({
90+
"Sofa.Component.LinearSolver.Direct",
91+
"Sofa.Component.Mass",
92+
"Sofa.Component.ODESolver.Backward",
93+
"Sofa.Component.StateContainer",
94+
"Sofa.Component.Topology.Container.Dynamic",
95+
"Sofa.Component.Topology.Utility"});
9496

9597
sofa::simpleapi::createObject(root, "DefaultAnimationLoop");
9698
sofa::simpleapi::createObject(root, "EulerImplicitSolver");

Sofa/framework/Testing/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ set(HEADER_FILES
6565
${SOFATESTINGSRC_ROOT}/BaseTest.h
6666
${SOFATESTINGSRC_ROOT}/LinearCongruentialRandomGenerator.h
6767
${SOFATESTINGSRC_ROOT}/NumericTest.h
68+
${SOFATESTINGSRC_ROOT}/ScopedPlugin.h
6869
${SOFATESTINGSRC_ROOT}/TestMessageHandler.h
6970
${SOFATESTINGSRC_ROOT}/BaseSimulationTest.h
7071
)
@@ -74,6 +75,7 @@ set(SOURCE_FILES
7475
${SOFATESTINGSRC_ROOT}/BaseTest.cpp
7576
${SOFATESTINGSRC_ROOT}/LinearCongruentialRandomGenerator.cpp
7677
${SOFATESTINGSRC_ROOT}/NumericTest.cpp
78+
${SOFATESTINGSRC_ROOT}/ScopedPlugin.cpp
7779
${SOFATESTINGSRC_ROOT}/TestMessageHandler.cpp
7880
${SOFATESTINGSRC_ROOT}/BaseSimulationTest.cpp
7981
)

Sofa/framework/Testing/src/sofa/testing/BaseTest.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,13 +93,20 @@ BaseTest::BaseTest() :
9393

9494
BaseTest::~BaseTest() {}
9595

96+
void BaseTest::loadPlugins(
97+
const std::initializer_list<std::string>& pluginNames)
98+
{
99+
m_loadedPlugins.emplace_back(pluginNames.begin(), pluginNames.end());
100+
}
101+
96102
void BaseTest::SetUp()
97103
{
98104
onSetUp();
99105
}
100106

101107
void BaseTest::TearDown()
102108
{
109+
m_loadedPlugins.clear();
103110
onTearDown();
104111
}
105112

Sofa/framework/Testing/src/sofa/testing/BaseTest.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,12 @@
2121
******************************************************************************/
2222
#pragma once
2323

24+
#include <deque>
2425
#include <sofa/testing/config.h>
2526

2627
#include <gtest/gtest.h>
2728
#include <sofa/testing/TestMessageHandler.h>
29+
#include <sofa/testing/ScopedPlugin.h>
2830

2931
namespace sofa::testing
3032
{
@@ -51,9 +53,13 @@ class SOFA_TESTING_API BaseTest : public ::testing::Test
5153
/// Seed value
5254
static int seed;
5355

56+
void loadPlugins(const std::initializer_list<std::string>& pluginNames);
57+
5458
private:
5559
void SetUp() override ;
5660
void TearDown() override ;
61+
62+
std::deque<sofa::testing::ScopedPlugin> m_loadedPlugins;
5763
};
5864

5965
} // namespace sofa::testing
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/******************************************************************************
2+
* SOFA, Simulation Open-Framework Architecture *
3+
* (c) 2006 INRIA, USTL, UJF, CNRS, MGH *
4+
* *
5+
* This program is free software; you can redistribute it and/or modify it *
6+
* under the terms of the GNU Lesser General Public License as published by *
7+
* the Free Software Foundation; either version 2.1 of the License, or (at *
8+
* your option) any later version. *
9+
* *
10+
* This program is distributed in the hope that it will be useful, but WITHOUT *
11+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
12+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License *
13+
* for more details. *
14+
* *
15+
* You should have received a copy of the GNU Lesser General Public License *
16+
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
17+
*******************************************************************************
18+
* Authors: The SOFA Team and external contributors (see Authors.txt) *
19+
* *
20+
* Contact information: contact@sofa-framework.org *
21+
******************************************************************************/
22+
#include <sofa/core/ObjectFactory.h>
23+
#include <sofa/testing/ScopedPlugin.h>
24+
25+
namespace sofa::testing
26+
{
27+
28+
ScopedPlugin::ScopedPlugin(const std::string& pluginName,
29+
helper::system::PluginManager* pluginManager)
30+
: m_pluginManager(pluginManager)
31+
{
32+
addPlugin(pluginName);
33+
}
34+
35+
ScopedPlugin::~ScopedPlugin()
36+
{
37+
if (m_pluginManager)
38+
{
39+
for (const auto& pluginName : m_loadedPlugins)
40+
{
41+
const auto [path, isLoaded] = m_pluginManager->isPluginLoaded(pluginName);
42+
if (isLoaded)
43+
{
44+
m_pluginManager->unloadPlugin(path);
45+
}
46+
}
47+
}
48+
}
49+
50+
void ScopedPlugin::addPlugin(const std::string& pluginName)
51+
{
52+
const auto status = m_pluginManager->loadPlugin(pluginName);
53+
if(status == helper::system::PluginManager::PluginLoadStatus::SUCCESS)
54+
{
55+
m_loadedPlugins.insert(pluginName);
56+
sofa::core::ObjectFactory::getInstance()->registerObjectsFromPlugin(pluginName);
57+
}
58+
}
59+
60+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/******************************************************************************
2+
* SOFA, Simulation Open-Framework Architecture *
3+
* (c) 2006 INRIA, USTL, UJF, CNRS, MGH *
4+
* *
5+
* This program is free software; you can redistribute it and/or modify it *
6+
* under the terms of the GNU Lesser General Public License as published by *
7+
* the Free Software Foundation; either version 2.1 of the License, or (at *
8+
* your option) any later version. *
9+
* *
10+
* This program is distributed in the hope that it will be useful, but WITHOUT *
11+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
12+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License *
13+
* for more details. *
14+
* *
15+
* You should have received a copy of the GNU Lesser General Public License *
16+
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
17+
*******************************************************************************
18+
* Authors: The SOFA Team and external contributors (see Authors.txt) *
19+
* *
20+
* Contact information: contact@sofa-framework.org *
21+
******************************************************************************/
22+
#pragma once
23+
#include <sofa/testing/config.h>
24+
#include <sofa/helper/system/PluginManager.h>
25+
26+
namespace sofa::testing
27+
{
28+
29+
struct SOFA_TESTING_API ScopedPlugin
30+
{
31+
ScopedPlugin() = delete;
32+
ScopedPlugin(const ScopedPlugin&) = delete;
33+
void operator=(const ScopedPlugin&) = delete;
34+
35+
explicit ScopedPlugin(
36+
const std::string& pluginName,
37+
helper::system::PluginManager* pluginManager = &helper::system::PluginManager::getInstance());
38+
39+
template<class InputIt>
40+
ScopedPlugin(
41+
InputIt first, InputIt last,
42+
helper::system::PluginManager* pluginManager = &helper::system::PluginManager::getInstance())
43+
: m_pluginManager(pluginManager)
44+
{
45+
while (first != last)
46+
{
47+
addPlugin(*first++);
48+
}
49+
}
50+
51+
~ScopedPlugin();
52+
53+
private:
54+
helper::system::PluginManager* m_pluginManager { nullptr };
55+
56+
std::set<std::string> m_loadedPlugins;
57+
58+
void addPlugin(const std::string& pluginName);
59+
};
60+
61+
62+
inline std::unique_ptr<ScopedPlugin> makeScopedPlugin(const std::initializer_list<std::string>& pluginNames)
63+
{
64+
return std::make_unique<ScopedPlugin>(pluginNames.begin(), pluginNames.end());
65+
}
66+
67+
68+
}

Sofa/framework/Testing/test/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ cmake_minimum_required(VERSION 3.22)
33
project(Sofa.Testing_test)
44

55
set(SOURCE_FILES
6-
TestMessageHandler_test.cpp
6+
ScopedPlugin_test.cpp
7+
TestMessageHandler_test.cpp
78
)
89

910
add_executable(${PROJECT_NAME} ${SOURCE_FILES})
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/******************************************************************************
2+
* SOFA, Simulation Open-Framework Architecture *
3+
* (c) 2006 INRIA, USTL, UJF, CNRS, MGH *
4+
* *
5+
* This program is free software; you can redistribute it and/or modify it *
6+
* under the terms of the GNU Lesser General Public License as published by *
7+
* the Free Software Foundation; either version 2.1 of the License, or (at *
8+
* your option) any later version. *
9+
* *
10+
* This program is distributed in the hope that it will be useful, but WITHOUT *
11+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
12+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License *
13+
* for more details. *
14+
* *
15+
* You should have received a copy of the GNU Lesser General Public License *
16+
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
17+
*******************************************************************************
18+
* Authors: The SOFA Team and external contributors (see Authors.txt) *
19+
* *
20+
* Contact information: contact@sofa-framework.org *
21+
******************************************************************************/
22+
#include <sofa/testing/ScopedPlugin.h>
23+
#include <gtest/gtest.h>
24+
25+
TEST(ScopedPlugin, test)
26+
{
27+
static std::string pluginName = "Sofa.Component.AnimationLoop";
28+
auto& pluginManager = sofa::helper::system::PluginManager::getInstance();
29+
30+
//make sure that pluginName is not already loaded
31+
{
32+
const auto [path, isLoaded] = pluginManager.isPluginLoaded(pluginName);
33+
if (isLoaded)
34+
{
35+
pluginManager.unloadPlugin(path);
36+
}
37+
}
38+
39+
{
40+
const auto [path, isLoaded] = pluginManager.isPluginLoaded(pluginName);
41+
EXPECT_FALSE(isLoaded);
42+
}
43+
44+
{
45+
const sofa::testing::ScopedPlugin plugin(pluginName);
46+
47+
{
48+
const auto [path, isLoaded] = pluginManager.isPluginLoaded(pluginName);
49+
EXPECT_TRUE(isLoaded);
50+
}
51+
52+
//end of scope: plugin should be unloaded
53+
}
54+
55+
{
56+
const auto [path, isLoaded] = pluginManager.isPluginLoaded(pluginName);
57+
EXPECT_FALSE(isLoaded);
58+
}
59+
}

0 commit comments

Comments
 (0)