#define BOOST_TEST_MODULE testVirtualHierarchy #include <boost/test/included/unit_test.hpp> #include "Application.h" #include "ApplicationModule.h" #include "ModuleGroup.h" #include "TestFacility.h" using namespace boost::unit_test_framework; namespace ctk = ChimeraTK; struct TestModule : public ctk::ApplicationModule { using ctk::ApplicationModule::ApplicationModule; ctk::ScalarPushInput<int> input{this, "input", "", {"CS"}}; ctk::ScalarOutput<int> output{this, "output", "", {"CS"}}; void mainLoop() override {} }; struct TestModule2 : public ctk::ApplicationModule { using ctk::ApplicationModule::ApplicationModule; ctk::ScalarPushInput<int> input2{this, "input2", "", {"CS"}}; ctk::ScalarOutput<int> output2{this, "output2", "", {"CS"}}; void mainLoop() override {} }; struct TestModule3 : public ctk::ApplicationModule { using ctk::ApplicationModule::ApplicationModule; ctk::ScalarPushInput<int> input3{this, "input3", "", {"CS"}}; ctk::ScalarOutput<int> output3{this, "output3", "", {"CS"}}; void mainLoop() override {} }; struct TestModuleWithVariableGroups : public ctk::ApplicationModule { using ctk::ApplicationModule::ApplicationModule; struct GroupOneLevelUp : public ctk::VariableGroup { using ctk::VariableGroup::VariableGroup; ctk::ScalarOutput<int> outputInGroupOneLevelUp{this, "outputInGroupOneLevelUp", "", ""}; } groupOneLevelUp{this, "groupOneLevelUp", "", ctk::HierarchyModifier::oneLevelUp}; struct GroupOneUpAndHide : public ctk::VariableGroup { using ctk::VariableGroup::VariableGroup; ctk::ScalarOutput<int> outputInGroupOneUpAndHide{this, "outputInGroupOneUpAndHide", "", ""}; } groupOneUpAndHide{this, "groupOneUpAndHide", "", ctk::HierarchyModifier::oneUpAndHide}; void mainLoop() override {} }; struct InnerGroup : public ctk::ModuleGroup { using ctk::ModuleGroup::ModuleGroup; TestModule innerModule{this, "innerModule", "", ctk::HierarchyModifier::none}; TestModule2 innerModuleOneUpAndHide{this, "innerModuleOneUpAndHide", "", ctk::HierarchyModifier::oneUpAndHide}; TestModule3 innerModuleMoveToRoot{this, "innerModuleMoveToRoot", "", ctk::HierarchyModifier::moveToRoot}; TestModule3 innerModuleSameNameAsGroup{this, "innerModuleGroup", "", ctk::HierarchyModifier::oneLevelUp}; TestModuleWithVariableGroups innerModuleWithVariableGroups{this, "innerModuleWithVariableGroups", ""}; }; struct OuterGroup : public ctk::ModuleGroup { OuterGroup(EntityOwner* owner, const std::string& name, const std::string& description, ctk::HierarchyModifier modifier, ctk::HierarchyModifier innerGroupModifier = ctk::HierarchyModifier::none) : ModuleGroup{owner, name, description, modifier}, innerGroup{this, "innerModuleGroup", "", innerGroupModifier} { // Here, findTag should give proper exceptions if HierarchyModifiers are used illegally auto allAccessors = getOwner()->findTag(".*").getAccessorListRecursive(); // for(auto acc : allAccessors) { // std::cout << " -- Accessor: " << acc.getName() << " of module: " << acc.getOwningModule()->getName() // << std::endl; // } } virtual ~OuterGroup() {} TestModule outerModule{this, "outerModuleInGroup", "", ctk::HierarchyModifier::oneLevelUp}; InnerGroup innerGroup; }; struct TestApplication : public ctk::Application { TestApplication(ctk::HierarchyModifier outerModuleModifier, ctk::HierarchyModifier innerGroupModifier = ctk::HierarchyModifier::none, bool skipConnection = false) : Application("testApp"), outerModuleGroup1{this, "outerModuleGroup1", "", ctk::HierarchyModifier::none, innerGroupModifier}, outerModule{this, "outerModule", "", outerModuleModifier}, _skipConnection{skipConnection} {} ~TestApplication() { shutdown(); } OuterGroup outerModuleGroup1; TestModule outerModule; ctk::ControlSystemModule cs; void defineConnections() { // Tests for getVirtualQualifiedName require that findTag is not used globally, // so it can be disabled if(!_skipConnection) { findTag(".*").connectTo(cs); } //cs.dump(); } bool _skipConnection; }; // Check if HierarchyModifiers are properly handled in the call to findTag // in the constructor of TestApplication BOOST_AUTO_TEST_CASE(testIllegalModifiers) { std::cout << "testIllegalModifiers" << std::endl; { std::cout << " Creating TestApplication with outerModuleModifier = none " << std::endl; // Should work TestApplication app(ctk::HierarchyModifier::none); ctk::TestFacility test; std::cout << std::endl; } { std::cout << " Creating TestApplication with outerModuleModifier = oneLevelUp " << std::endl; TestApplication app(ctk::HierarchyModifier::oneLevelUp); // Should detect illegal usage of oneLevelUp on first level below Application and throw BOOST_CHECK_THROW(ctk::TestFacility test, ctk::logic_error); std::cout << std::endl; } // Should detect illegal usage of oneUpAndHide on first level below Application and throw // Currently leads to memory access violation, should also throw // Bug is described by issue #166. // { // std::cout << "Creating TestApplication with outerModuleModifier = oneUpAndHide " << std::endl; // TestApplication app(ctk::HierarchyModifier::oneUpAndHide); // ctk::TestFacility test; // std::cout << std::endl; // } { std::cout << " Creating TestApplication with outerModuleModifier = moveToRoot " << std::endl; // Should work TestApplication app(ctk::HierarchyModifier::moveToRoot); ctk::TestFacility test; std::cout << std::endl; } } BOOST_AUTO_TEST_CASE(testGetVirtualQualifiedName) { std::cout << "testGetVirtualQualifiedName" << std::endl; { TestApplication app(ctk::HierarchyModifier::none); ctk::TestFacility test; //app.cs.dump(); BOOST_CHECK_EQUAL(app.outerModule.getVirtualQualifiedName(), "/testApp/outerModule"); BOOST_CHECK_EQUAL(app.outerModuleGroup1.getVirtualQualifiedName(), "/testApp/outerModuleGroup1"); BOOST_CHECK_EQUAL(app.outerModuleGroup1.outerModule.getVirtualQualifiedName(), "/testApp/outerModuleInGroup"); BOOST_CHECK_EQUAL( app.outerModuleGroup1.innerGroup.getVirtualQualifiedName(), "/testApp/outerModuleGroup1/innerModuleGroup"); BOOST_CHECK_EQUAL(app.outerModuleGroup1.innerGroup.innerModule.getVirtualQualifiedName(), "/testApp/outerModuleGroup1/innerModuleGroup/innerModule"); BOOST_CHECK_EQUAL( app.outerModuleGroup1.innerGroup.innerModuleWithVariableGroups.groupOneLevelUp.getVirtualQualifiedName(), "/testApp/outerModuleGroup1/innerModuleGroup/groupOneLevelUp"); BOOST_CHECK_EQUAL( app.outerModuleGroup1.innerGroup.innerModuleWithVariableGroups.groupOneUpAndHide.getVirtualQualifiedName(), "/testApp/outerModuleGroup1/innerModuleGroup"); BOOST_CHECK_EQUAL(app.outerModuleGroup1.innerGroup.innerModuleOneUpAndHide.getVirtualQualifiedName(), "/testApp/outerModuleGroup1"); BOOST_CHECK_EQUAL(app.outerModuleGroup1.innerGroup.innerModuleMoveToRoot.getVirtualQualifiedName(), "/testApp/innerModuleMoveToRoot"); BOOST_CHECK_EQUAL(app.outerModuleGroup1.innerGroup.innerModuleSameNameAsGroup.getVirtualQualifiedName(), "/testApp/outerModuleGroup1/innerModuleGroup"); } { TestApplication app(ctk::HierarchyModifier::hideThis); ctk::TestFacility test; // app.cs.dump(); BOOST_CHECK_EQUAL(app.outerModule.getVirtualQualifiedName(), "/testApp"); } // Modifiers oneLevelUp and oneUpAndHide need to be catched by getVirtualQualifiedName, if used on // the top level og the application. Note: If defineConnections uses findTag on the entire app, // the error is catched there, this is avoided by the boolean constructor arguments below { TestApplication app(ctk::HierarchyModifier::oneLevelUp, ctk::HierarchyModifier::none, true); ctk::TestFacility test; BOOST_CHECK_THROW(app.outerModule.getVirtualQualifiedName(), ctk::logic_error); } { TestApplication app(ctk::HierarchyModifier::oneUpAndHide, ctk::HierarchyModifier::none, true); ctk::TestFacility test; BOOST_CHECK_THROW(app.outerModule.getVirtualQualifiedName(), ctk::logic_error); } { TestApplication app(ctk::HierarchyModifier::moveToRoot, ctk::HierarchyModifier::moveToRoot); ctk::TestFacility test; // app.cs.dump(); BOOST_CHECK_EQUAL(app.outerModule.getVirtualQualifiedName(), "/testApp/outerModule"); auto virtualisedApp = app.findTag(".*"); BOOST_CHECK_NO_THROW(virtualisedApp["outerModule"]); BOOST_CHECK_NO_THROW(virtualisedApp["innerModuleMoveToRoot"]); BOOST_CHECK_EQUAL(app.outerModuleGroup1.innerGroup.getVirtualQualifiedName(), "/testApp/innerModuleGroup"); BOOST_CHECK_EQUAL(app.outerModuleGroup1.innerGroup.innerModule.getVirtualQualifiedName(), "/testApp/innerModuleGroup/innerModule"); } } BOOST_AUTO_TEST_CASE(testGetNetworkNodesOnVirtualHierarchy) { std::cout << "testGetNetworkNodesOnVirtualHierarchy" << std::endl; TestApplication app(ctk::HierarchyModifier::none); ctk::TestFacility test; //app.cs.dump(); //app.outerModuleGroup1.virtualise().dump(); auto virtualisedApplication = app.findTag(".*"); virtualisedApplication.dump(); // Need to trip away "/appName/" in the submodule() calls size_t firstModuleOffsetInPath = ("/" + app.getName() + "/").size(); auto pathToInnerModuleOneUpAndHide = app.outerModuleGroup1.innerGroup.innerModuleOneUpAndHide.getVirtualQualifiedName(); // Get submodule by the virtual path ctk::Module& module = virtualisedApplication.submodule( {pathToInnerModuleOneUpAndHide.begin() + firstModuleOffsetInPath, pathToInnerModuleOneUpAndHide.end()}); auto node2 = module("input2"); BOOST_CHECK_EQUAL(node2.getName(), "input2"); // As a reference, navigate to the module using operator [] auto node2Ref = virtualisedApplication["outerModuleGroup1"]("input2"); BOOST_CHECK(node2 == node2Ref); // Repeat test for other modules: Module moved to root auto pathToInnerModuleMoveToRoot = app.outerModuleGroup1.innerGroup.innerModuleMoveToRoot.getVirtualQualifiedName(); ctk::Module& innerModuleMoveToRoot = virtualisedApplication.submodule( {pathToInnerModuleMoveToRoot.begin() + firstModuleOffsetInPath, pathToInnerModuleMoveToRoot.end()}); auto node3 = innerModuleMoveToRoot("input3"); auto node3Ref = virtualisedApplication["innerModuleMoveToRoot"]("input3"); BOOST_CHECK(node3 == node3Ref); // Repeat test for other modules: Module with same name as its group and modifier oneLevelUp auto pathToInnerModuleSameNameAsGroup = app.outerModuleGroup1.innerGroup.innerModuleSameNameAsGroup.getVirtualQualifiedName(); ctk::Module& innerModuleSameNameAsGroup = virtualisedApplication.submodule( {pathToInnerModuleSameNameAsGroup.begin() + firstModuleOffsetInPath, pathToInnerModuleSameNameAsGroup.end()}); node3 = innerModuleSameNameAsGroup("input3"); node3Ref = virtualisedApplication["outerModuleGroup1"]["innerModuleGroup"]("input3"); BOOST_CHECK(node3 == node3Ref); auto pathToGroupWithOneLevelUp = app.outerModuleGroup1.innerGroup.innerModuleWithVariableGroups.groupOneLevelUp.getVirtualQualifiedName(); ctk::Module& groupWithOneLevelUp = virtualisedApplication.submodule( {pathToGroupWithOneLevelUp.begin() + firstModuleOffsetInPath, pathToGroupWithOneLevelUp.end()}); auto node4 = groupWithOneLevelUp("outputInGroupOneLevelUp"); auto node4Ref = virtualisedApplication["outerModuleGroup1"]["innerModuleGroup"]["groupOneLevelUp"]("outputInGroupOneLevelUp"); BOOST_CHECK(node4 == node4Ref); auto pathToGroupWithOneUpAndHide = app.outerModuleGroup1.innerGroup.innerModuleWithVariableGroups.groupOneUpAndHide.getVirtualQualifiedName(); ctk::Module& groupWithOneUpAndHide = virtualisedApplication.submodule( {pathToGroupWithOneUpAndHide.begin() + firstModuleOffsetInPath, pathToGroupWithOneUpAndHide.end()}); auto node5 = groupWithOneUpAndHide("outputInGroupOneUpAndHide"); auto node5Ref = virtualisedApplication["outerModuleGroup1"]["innerModuleGroup"]("outputInGroupOneUpAndHide"); BOOST_CHECK(node5 == node5Ref); }