From 945c9e9240dc378790d29ab90d2dd62a8e571c52 Mon Sep 17 00:00:00 2001 From: Martin Hierholzer <martin.hierholzer@desy.de> Date: Wed, 27 Sep 2017 17:11:33 +0200 Subject: [PATCH] added tests for modules (including std::vector of modules) --- tests/executables_src/testModules.cc | 302 +++++++++++++++++++++++++++ 1 file changed, 302 insertions(+) create mode 100644 tests/executables_src/testModules.cc diff --git a/tests/executables_src/testModules.cc b/tests/executables_src/testModules.cc new file mode 100644 index 00000000..2e4bc131 --- /dev/null +++ b/tests/executables_src/testModules.cc @@ -0,0 +1,302 @@ +/* + * testModules.cc - Test ApplicationModule, ModuleGroup and VariableGroup + * + * Created on: Sep 27, 2017 + * Author: Martin Hierholzer + */ + +#include <future> +#include <chrono> + +#define BOOST_TEST_MODULE testModules + +#include <boost/test/included/unit_test.hpp> +#include <boost/test/test_case_template.hpp> +#include <boost/mpl/list.hpp> + +#include "ApplicationCore.h" + +using namespace boost::unit_test_framework; +namespace ctk = ChimeraTK; + +#define CHECK_TIMEOUT(condition, maxMilliseconds) \ + { \ + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); \ + while(!(condition)) { \ + bool timeout_reached = (std::chrono::steady_clock::now()-t0) > std::chrono::milliseconds(maxMilliseconds); \ + BOOST_CHECK( !timeout_reached ); \ + if(timeout_reached) break; \ + usleep(1000); \ + } \ + } + + +/*********************************************************************************************************************/ +/* Variable group used in the modules */ + +struct SomeGroup : ctk::VariableGroup { + using ctk::VariableGroup::VariableGroup; + ctk::ScalarPushInput<std::string> inGroup{this, "inGroup", "", "This is a string", {"C", "D"}}; + ctk::ArrayPushInput<int64_t> alsoInGroup{this, "alsoInGroup", "justANumber", 16, "A 64 bit number array", {"C"}}; +}; + +/*********************************************************************************************************************/ +/* A plain application module for testing */ + +struct TestModule : public ctk::ApplicationModule { + using ctk::ApplicationModule::ApplicationModule; + + ctk::ScalarPushInput<int> someInput{this, "nameOfSomeInput", "cm", "This is just some input for testing", {"A", "B"}}; + ctk::ScalarOutput<double> someOutput{this, "someOutput", "V", "Description", {"A", "C"}}; + + SomeGroup someGroup{this, "someGroup", "Description of my test group"}; + + struct AnotherGroup : ctk::VariableGroup { + using ctk::VariableGroup::VariableGroup; + ctk::ScalarPushInput<uint8_t> foo{this, "foo", "counts", "Some counter", {"D"}}; + } anotherGroup{this, "anotherName", "Description of my other group"}; + + void mainLoop() { + while(true) { + someInput.read(); + int val = someInput; + someOutput = val; + someOutput.write(); + } + } +}; + +/*********************************************************************************************************************/ +/* Simple application with just one module */ + +struct OneModuleApp : public ctk::Application { + OneModuleApp() : Application("myApp") {} + ~OneModuleApp() { shutdown(); } + + using Application::makeConnections; // we call makeConnections() manually in the tests to catch exceptions etc. + void defineConnections() {} // the setup is done in the tests + + TestModule testModule{this, "testModule", "Module to test"}; +}; + +/*********************************************************************************************************************/ +/* Application with a vector of modules */ + +struct VectorOfModulesApp : public ctk::Application { + VectorOfModulesApp(size_t nInstances) + : Application("myApp"), + _nInstances(nInstances) + {} + ~VectorOfModulesApp() { shutdown(); } + + using Application::makeConnections; // we call makeConnections() manually in the tests to catch exceptions etc. + + void defineConnections() { + for(size_t i = 0; i < _nInstances; ++i) { + std::string name = "testModule_" + std::to_string(i) + "_instance"; + vectorOfTestModule.emplace_back(this, name, "Description"); + } + } + + size_t _nInstances; + std::vector<TestModule> vectorOfTestModule; +}; + +/*********************************************************************************************************************/ +/* test module and variable ownerships */ + +BOOST_AUTO_TEST_CASE( test_ownership ) { + std::cout << "*********************************************************************************************************************" << std::endl; + std::cout << "==> test_ownership" << std::endl; + + OneModuleApp app; + + BOOST_CHECK( app.testModule.getOwner() == &app ); + BOOST_CHECK( app.testModule.someGroup.getOwner() == &(app.testModule) ); + BOOST_CHECK( app.testModule.anotherGroup.getOwner() == &(app.testModule) ); + + BOOST_CHECK( app.testModule.someInput.getOwner() == &(app.testModule) ); + BOOST_CHECK( app.testModule.someOutput.getOwner() == &(app.testModule) ); + + BOOST_CHECK( app.testModule.someGroup.inGroup.getOwner() == &(app.testModule.someGroup) ); + BOOST_CHECK( app.testModule.someGroup.alsoInGroup.getOwner() == &(app.testModule.someGroup) ); + + BOOST_CHECK( app.testModule.anotherGroup.foo.getOwner() == &(app.testModule.anotherGroup) ); + +} + +/*********************************************************************************************************************/ +/* test getSubmoduleList() and getSubmoduleListRecursive() */ + +BOOST_AUTO_TEST_CASE( test_getSubmoduleList ) { + std::cout << "*********************************************************************************************************************" << std::endl; + std::cout << "==> test_getSubmoduleList" << std::endl; + + OneModuleApp app; + + { + std::list<ctk::Module*> list = app.getSubmoduleList(); + BOOST_CHECK( list.size() == 1 ); + BOOST_CHECK( list.front() == &(app.testModule) ); + } + + { + std::list<ctk::Module*> list = app.testModule.getSubmoduleList(); + BOOST_CHECK( list.size() == 2 ); + size_t foundSomeGroup = 0; + size_t foundAnotherGroup = 0; + for(auto mod : list) { + if(mod == &(app.testModule.someGroup)) foundSomeGroup++; + if(mod == &(app.testModule.anotherGroup)) foundAnotherGroup++; + } + BOOST_CHECK( foundSomeGroup == 1 ); + BOOST_CHECK( foundAnotherGroup == 1 ); + } + + { + std::list<ctk::Module*> list = app.getSubmoduleListRecursive(); + BOOST_CHECK( list.size() == 3 ); + size_t foundTestModule = 0; + size_t foundSomeGroup = 0; + size_t foundAnotherGroup = 0; + for(auto mod : list) { + if(mod == &(app.testModule)) foundTestModule++; + if(mod == &(app.testModule.someGroup)) foundSomeGroup++; + if(mod == &(app.testModule.anotherGroup)) foundAnotherGroup++; + } + BOOST_CHECK( foundTestModule == 1 ); + BOOST_CHECK( foundSomeGroup == 1 ); + BOOST_CHECK( foundAnotherGroup == 1 ); + } + + { + std::list<ctk::Module*> list = app.testModule.getSubmoduleListRecursive(); // identical to getSubmoduleList(), since no deeper hierarchies + BOOST_CHECK( list.size() == 2 ); + size_t foundSomeGroup = 0; + size_t foundAnotherGroup = 0; + for(auto mod : list) { + if(mod == &(app.testModule.someGroup)) foundSomeGroup++; + if(mod == &(app.testModule.anotherGroup)) foundAnotherGroup++; + } + BOOST_CHECK( foundSomeGroup == 1 ); + BOOST_CHECK( foundAnotherGroup == 1 ); + } + +} + +/*********************************************************************************************************************/ +/* test function call operator of the ApplicationModule */ + +BOOST_AUTO_TEST_CASE( testApplicatioModuleFnCallOp ) { + std::cout << "*********************************************************************************************************************" << std::endl; + std::cout << "==> testApplicatioModuleFnCallOp" << std::endl; + + OneModuleApp app; + + BOOST_CHECK( app.testModule("nameOfSomeInput") == static_cast<ctk::VariableNetworkNode>(app.testModule.someInput) ); + BOOST_CHECK( app.testModule("nameOfSomeInput") != static_cast<ctk::VariableNetworkNode>(app.testModule.someOutput) ); + BOOST_CHECK( app.testModule("someOutput") == static_cast<ctk::VariableNetworkNode>(app.testModule.someOutput) ); + + BOOST_CHECK( app.testModule("nameOfSomeInput").getType() == ctk::NodeType::Application ); + BOOST_CHECK( app.testModule("nameOfSomeInput").getMode() == ctk::UpdateMode::push ); + BOOST_CHECK( app.testModule("nameOfSomeInput").getDirection() == ctk::VariableDirection::consuming ); + BOOST_CHECK( app.testModule("nameOfSomeInput").getValueType() == typeid(int) ); + BOOST_CHECK( app.testModule("nameOfSomeInput").getName() == "nameOfSomeInput" ); + BOOST_CHECK( app.testModule("nameOfSomeInput").getQualifiedName() == "/myApp/testModule/nameOfSomeInput" ); + BOOST_CHECK( app.testModule("nameOfSomeInput").getUnit() == "cm" ); + BOOST_CHECK( app.testModule("nameOfSomeInput").getDescription() == "This is just some input for testing" ); + BOOST_CHECK( app.testModule("nameOfSomeInput").getTags() == std::unordered_set<std::string>({"A", "B"}) ); + +} + +/*********************************************************************************************************************/ +/* test function call operator of the ApplicationModule */ + +BOOST_AUTO_TEST_CASE( testApplicatioModuleSubscriptOp ) { + std::cout << "*********************************************************************************************************************" << std::endl; + std::cout << "==> testApplicatioModuleSubscriptOp" << std::endl; + + OneModuleApp app; + + BOOST_CHECK( &(app.testModule["someGroup"]) == &(app.testModule.someGroup) ); + BOOST_CHECK( &(app.testModule["anotherName"]) == &(app.testModule.anotherGroup) ); + +} + +/*********************************************************************************************************************/ +/* test correct behaviour when using a std::vector of ApplicatioModules */ + +BOOST_AUTO_TEST_CASE( testVectorOfApplicationModule ) { + std::cout << "*********************************************************************************************************************" << std::endl; + std::cout << "==> testVectorOfApplicationModule" << std::endl; + + // create app with a vector containing 10 modules + size_t nInstances = 10; + VectorOfModulesApp app(nInstances); + + // the app creates the 10 module instances in defineConnections, check if this is done proplery (a quite redundant test...) + BOOST_CHECK(app.vectorOfTestModule.size() == 0); + app.defineConnections(); + BOOST_CHECK(app.vectorOfTestModule.size() == nInstances); + + // some direct checks on the created instances + for(size_t i=0; i<nInstances; ++i) { + std::string name = "testModule_" + std::to_string(i) + "_instance"; + BOOST_CHECK( app.vectorOfTestModule[i].getName() == name ); + auto node = static_cast<ctk::VariableNetworkNode>(app.vectorOfTestModule[i].someInput); + BOOST_CHECK( node.getQualifiedName() == "/myApp/"+name+"/nameOfSomeInput" ); + } + + // check if instances appear properly in getSubmoduleList() + { + std::list<ctk::Module*> list = app.getSubmoduleList(); + BOOST_CHECK( list.size() == nInstances ); + std::map<size_t, size_t> instancesFound; + for(size_t i = 0; i < nInstances; ++i) instancesFound[i] = 0; + for(auto mod : list) { + for(size_t i = 0; i < nInstances; ++i) { + if(mod == &(app.vectorOfTestModule[i])) instancesFound[i]++; + } + } + for(size_t i = 0; i < nInstances; ++i) { + BOOST_CHECK( instancesFound[i] == 1 ); + } + } + + // check if instances appear properly in getSubmoduleListRecursive() as well + { + std::list<ctk::Module*> list = app.getSubmoduleListRecursive(); + BOOST_CHECK( list.size() == 3*nInstances ); + std::map<size_t, size_t> instancesFound, instancesSomeGroupFound, instancesAnotherGroupFound; + for(size_t i = 0; i < nInstances; ++i) { + instancesFound[i] = 0; + instancesSomeGroupFound[i] = 0; + instancesAnotherGroupFound[i] = 0; + } + for(auto mod : list) { + for(size_t i = 0; i < nInstances; ++i) { + if(mod == &(app.vectorOfTestModule[i])) instancesFound[i]++; + if(mod == &(app.vectorOfTestModule[i].someGroup)) instancesSomeGroupFound[i]++; + if(mod == &(app.vectorOfTestModule[i].anotherGroup)) instancesAnotherGroupFound[i]++; + } + } + for(size_t i = 0; i < nInstances; ++i) { + BOOST_CHECK( instancesFound[i] == 1 ); + BOOST_CHECK( instancesSomeGroupFound[i] == 1 ); + BOOST_CHECK( instancesAnotherGroupFound[i] == 1 ); + } + } + + // check ownerships + for(size_t i = 0; i < nInstances; ++i) { + BOOST_CHECK( app.vectorOfTestModule[i].getOwner() == &app ); + BOOST_CHECK( app.vectorOfTestModule[i].someInput.getOwner() == &(app.vectorOfTestModule[i]) ); + BOOST_CHECK( app.vectorOfTestModule[i].someOutput.getOwner() == &(app.vectorOfTestModule[i]) ); + BOOST_CHECK( app.vectorOfTestModule[i].someGroup.getOwner() == &(app.vectorOfTestModule[i]) ); + BOOST_CHECK( app.vectorOfTestModule[i].someGroup.inGroup.getOwner() == &(app.vectorOfTestModule[i].someGroup) ); + BOOST_CHECK( app.vectorOfTestModule[i].someGroup.alsoInGroup.getOwner() == &(app.vectorOfTestModule[i].someGroup) ); + BOOST_CHECK( app.vectorOfTestModule[i].anotherGroup.getOwner() == &(app.vectorOfTestModule[i]) ); + BOOST_CHECK( app.vectorOfTestModule[i].anotherGroup.foo.getOwner() == &(app.vectorOfTestModule[i].anotherGroup) ); + } + +} -- GitLab