diff --git a/Plugin/src/SofaPython3/PythonTestExtractor.cpp b/Plugin/src/SofaPython3/PythonTestExtractor.cpp index d0594500..afda872e 100644 --- a/Plugin/src/SofaPython3/PythonTestExtractor.cpp +++ b/Plugin/src/SofaPython3/PythonTestExtractor.cpp @@ -38,14 +38,30 @@ namespace sofapython3 { // extract the test suite from a module py::object PythonTestExtractor::getTestSuite(py::module& unittest, py::module& module, const std::vector& arguments) { - py::object testSuite = py::list(); py::module inspect = py::module::import("inspect"); + py::module scenetest = py::module::import("Sofa.scenetestcase"); - py::list classes = inspect.attr("getmembers")(module); - for(const auto n : classes) + py::list members = inspect.attr("getmembers")(module); + py::object testSuite = unittest.attr("TestSuite")(); + for(const auto n : members) { std::string name = py::cast(py::cast(n)[0]); py::object obj = py::cast(n)[1]; + + // Test if the current python object is a function. If this is the case, check if this one + // is named "createdScene". In that case add a new test to the suite. + if (py::cast(inspect.attr("isfunction")(obj)) == true) + { + if (py::cast(obj.attr("__name__")) == "createScene") + { + py::object testcase = scenetest.attr("SceneTestCase")(obj); + testSuite.attr("addTest")(testcase); + } + continue; + } + + // Test if the current python object is a function. If this is the case, check if this one + // is named "createdScene". In that case add a new test to the suite. if (py::cast(inspect.attr("isclass")(obj)) == true) { py::tuple bases = obj.attr("__bases__"); @@ -56,7 +72,9 @@ py::object PythonTestExtractor::getTestSuite(py::module& unittest, py::module& m if (arguments.empty()) return unittest.attr("TestLoader")().attr("loadTestsFromTestCase")(obj); + testSuite = unittest.attr("TestSuite")(); + py::list list; for (auto& arg : arguments) { list.append(py::str(arg)); @@ -87,13 +105,13 @@ std::vector PythonTestExtractor::extract () const std::string fullpath = (test.path + "/" + test.filename); SetDirectory localDir(fullpath.c_str()); std::string basename = SetDirectory::GetFileNameWithoutExtension( - SetDirectory::GetFileName(test.filename.c_str()).c_str() - ); + SetDirectory::GetFileName(test.filename.c_str()).c_str() + ); try { py::module module = PythonEnvironment::importFromFile( - basename, SetDirectory::GetFileName(test.filename.c_str()), &globals - ); + basename, SetDirectory::GetFileName(test.filename.c_str()), &globals + ); std::list testNames; py::list testSuite = getTestSuite(unittest, module, test.arguments); @@ -115,8 +133,8 @@ std::vector PythonTestExtractor::extract () const msg_info("PythonTestExtractor") << "File '" << test.filename << "' loaded with " << testNames.size() << " unit tests."; } catch(const std::exception& e) { - msg_error("PythonTestExtractor") << "File skipped: " << (test.path+"/"+test.filename) << msgendl - << e.what(); + msg_warning("PythonTestExtractor") << "File skipped: " << (test.path+"/"+test.filename) << msgendl + << e.what(); } } @@ -133,17 +151,26 @@ void PythonTestExtractor::addTestFile (const std::string & filename, p_tests.push_back({filename, path, testgroup, arguments}); } -void PythonTestExtractor::addTestDirectory (const std::string & dir, const std::string & testgroup, const std::string & prefix) +void PythonTestExtractor::addTestDirectory (const std::string & dir, const std::string & testgroup, const std::string & prefix, bool recursive) { std::vector files; sofa::helper::system::FileSystem::listDirectory(dir, files); - for(const std::string& file : files) { - if( sofa::helper::starts_with(prefix, file) - && (sofa::helper::ends_with(".py", file) || sofa::helper::ends_with(".py3", file))) + for(const std::string& file : files) + { + std::string childpath = (dir + "/" + file); + if( recursive && sofa::helper::system::FileSystem::isDirectory(childpath) ) + { + addTestDirectory(childpath, testgroup+"_"+file, prefix, true); + } + else { - addTestFile(file, dir, testgroup); + if( sofa::helper::starts_with(prefix, file) + && (sofa::helper::ends_with(".py", file) || sofa::helper::ends_with(".py3", file))) + { + addTestFile(file, dir, testgroup); + } } } } -} // namespace sofapython3 \ No newline at end of file +} // namespace sofapython3 diff --git a/Plugin/src/SofaPython3/PythonTestExtractor.h b/Plugin/src/SofaPython3/PythonTestExtractor.h index b399461b..de88c053 100644 --- a/Plugin/src/SofaPython3/PythonTestExtractor.h +++ b/Plugin/src/SofaPython3/PythonTestExtractor.h @@ -50,7 +50,7 @@ class SOFAPYTHON3_API PythonTestExtractor const std::vector &arguments = std::vector(0)); /// add all the python test files in `dir` starting with `prefix` - void addTestDirectory (const std::string &dir, const std::string &testgroup = "", const std::string &prefix = ""); + void addTestDirectory (const std::string &dir, const std::string &testgroup = "", const std::string &prefix = "", bool recursive=false); private: /// concatenate path and filename @@ -72,4 +72,4 @@ class SOFAPYTHON3_API PythonTestExtractor std::vector p_tests; }; -} // namespace sofapython3 \ No newline at end of file +} // namespace sofapython3 diff --git a/bindings/Sofa/package/scenetestcase.py b/bindings/Sofa/package/scenetestcase.py new file mode 100644 index 00000000..38fed3d8 --- /dev/null +++ b/bindings/Sofa/package/scenetestcase.py @@ -0,0 +1,19 @@ +import Sofa.Core +import Sofa.Simulation +import SofaRuntime +import unittest + +SofaRuntime.importPlugin("SofaComponentAll") + +class SceneTestCase(unittest.TestCase): + def __init__(self, cb): + unittest.TestCase.__init__(self, methodName="test_createScene") + self.cb = cb + + def test_createScene(self): + root = Sofa.Core.Node("root") + self.cb(root) + Sofa.Simulation.init(root) + + +