ChimeraTK-ApplicationCore 04.06.00
Loading...
Searching...
No Matches
testModel.cc
Go to the documentation of this file.
1// SPDX-FileCopyrightText: Deutsches Elektronen-Synchrotron DESY, MSK, ChimeraTK Project <chimeratk-support@desy.de>
2// SPDX-License-Identifier: LGPL-3.0-or-later
3
4#include "ApplicationModule.h"
5#include "DeviceModule.h"
6#include "Model.h"
7#include "ModuleGroup.h"
8#include "ScalarAccessor.h"
9#include "VariableGroup.h"
10
11#define BOOST_TEST_MODULE testApplicationPVModel
12#include <boost/test/included/unit_test.hpp>
13
15
16 using namespace boost::unit_test_framework;
17 namespace ctk = ChimeraTK;
18
19 /********************************************************************************************************************/
20 /* Simple TestApplication */
21
23 MyModule(ctk::ModuleGroup* owner, const std::string& name, const std::string& description,
24 const std::unordered_set<std::string>& tags = {})
25 : ApplicationModule(owner, name, description, tags) {
26 actuator.addTag("B");
27 }
28
29 using ctk::ApplicationModule::ApplicationModule;
30
31 ctk::ScalarOutput<int> actuator{this, "actuator", "unit", "Some output scalar"};
32
34 using ctk::VariableGroup::VariableGroup;
35 ctk::ScalarPollInput<int> readBack{this, "../readBack", "unit", "Some input scalar"};
36 } pointlessVariableGroup{this, "pointlessVariableGroup", ""};
37
38 void mainLoop() override {}
39 };
40
41 /********************************************************************************************************************/
42
44 using ctk::ApplicationModule::ApplicationModule;
45
46 ctk::ScalarPushInput<int> also{this, "also", "unit", "Some push input"};
47
49 using ctk::VariableGroup::VariableGroup;
50 ctk::ScalarPollInput<int> tests{this, "tests", "unit", "Some poll input", {"B"}};
51 } need{this, "need", ""};
52
53 void mainLoop() override {}
54 };
55
56 /********************************************************************************************************************/
57
59 using ctk::ModuleGroup::ModuleGroup;
60
61 TestModule testModule{this, ".", "The test module"};
62 };
63
64 /********************************************************************************************************************/
65
67 TestApplication() : Application("testSuite") {}
69
70 ~TestApplication() override { shutdown(); }
71
72 TestModuleGroup deeperHierarchies{this, "Deeper/hierarchies", "The test module group", {"A"}};
73 MyModule myModule{this, "MyModule", "ApplicationModule directly owned by app"};
74 MyModule myModule2{this, "Deeper/MyModule", "Additional "};
75 ctk::DeviceModule dev{this, "Dummy0", "/somepath/dummyTrigger"}; // test2.map
76 };
77
78 /********************************************************************************************************************/
79 /* Generic tests */
80 /********************************************************************************************************************/
81
82 BOOST_AUTO_TEST_CASE(testGraphViz) {
83 // this is no real test, just a smoke test, since there might be too much variance in the dot graph output
85 app.getModel().writeGraphViz("test.dot");
86 app.getModel().writeGraphViz("test-parenthood.dot", ChimeraTK::Model::keepParenthood);
87 }
88
89 /********************************************************************************************************************/
90
91 BOOST_AUTO_TEST_CASE(testGetFullyQualifiedPath) {
93
94 BOOST_TEST(app.getModel().getFullyQualifiedPath() == "/");
95 BOOST_TEST(app.deeperHierarchies.getModel().getFullyQualifiedPath() == "/Deeper/hierarchies");
96 BOOST_TEST(app.deeperHierarchies.testModule.getModel().getFullyQualifiedPath() == "/Deeper/hierarchies");
97 BOOST_TEST(app.deeperHierarchies.testModule.need.getModel().getFullyQualifiedPath() == "/Deeper/hierarchies/need");
99 "/Deeper/hierarchies/need/tests");
100 BOOST_TEST(app.myModule.pointlessVariableGroup.readBack.getModel().getFullyQualifiedPath() == "/MyModule/readBack");
101 }
102
103 /********************************************************************************************************************/
104
105 BOOST_AUTO_TEST_CASE(testIsValid) {
106 TestApplication app;
107
109
110 BOOST_TEST(invalid.isValid() == false);
111 BOOST_TEST(app.getModel().isValid() == true);
112 }
113
114 /********************************************************************************************************************/
115
116 BOOST_AUTO_TEST_CASE(testVisitByPath) {
117 TestApplication app;
118 bool found;
119
121 found = app.getModel().visitByPath("/Deeper/hierarchies", [&](auto proxy) {
122 if constexpr(isDirectory(proxy)) {
123 dir = proxy;
124 }
125 else {
126 BOOST_FAIL("Wrong proxy type found.");
127 }
128 });
129 BOOST_TEST(found == true);
130 BOOST_TEST(dir.isValid());
131 BOOST_TEST(dir.getName() == "hierarchies");
132
133 found = app.getModel().visitByPath(
134 "/Deeper/hierarchies/notExisting", [&](auto) { BOOST_FAIL("Visitor must not be called."); });
135 BOOST_TEST(found == false);
136
138 found = app.getModel().visitByPath("/Deeper/hierarchies/also", [&](auto proxy) {
139 if constexpr(isVariable(proxy)) {
140 var = proxy;
141 }
142 else {
143 BOOST_FAIL("Wrong proxy type found.");
144 }
145 });
146 BOOST_TEST(found == true);
147 BOOST_TEST(var.isValid());
148 BOOST_TEST(var.getName() == "also");
149 }
150
151 /********************************************************************************************************************/
152 /* Test functionality specific to the individual Proxy implementations */
153 /********************************************************************************************************************/
154
155 BOOST_AUTO_TEST_CASE(testModuleGroupProxy) {
156 TestApplication app;
157
159 BOOST_CHECK(&proxy.getModuleGroup() == &app.deeperHierarchies);
160 BOOST_CHECK(proxy.getName() == "Deeper/hierarchies");
161 }
162
163 /********************************************************************************************************************/
164
165 BOOST_AUTO_TEST_CASE(testApplicationModuleProxy) {
166 TestApplication app;
167
169 BOOST_CHECK(&proxy.getApplicationModule() == &app.deeperHierarchies.testModule);
170 BOOST_CHECK(proxy.getName() == ".");
171 }
172
173 /********************************************************************************************************************/
174
175 BOOST_AUTO_TEST_CASE(testVariableGroupProxy) {
176 TestApplication app;
177
179 BOOST_CHECK(&proxy.getVariableGroup() == &app.deeperHierarchies.testModule.need);
180 BOOST_CHECK(proxy.getName() == "need");
181 }
182
183 /********************************************************************************************************************/
184
185 BOOST_AUTO_TEST_CASE(testDeviceModuleProxy) {
186 TestApplication app;
187
189 BOOST_CHECK(proxy.getAliasOrCdd() == "Dummy0");
190 BOOST_REQUIRE(proxy.getTrigger().isValid());
191 BOOST_CHECK(proxy.getTrigger().getName() == "dummyTrigger");
192 }
193
194 /********************************************************************************************************************/
195
196 BOOST_AUTO_TEST_CASE(testProcessVariableProxy) {
197 TestApplication app;
198
200 BOOST_TEST(pv.getName() == "actuator");
201 auto nodes = pv.getNodes();
202 BOOST_TEST(nodes.size() == 2);
203 BOOST_CHECK(
204 nodes[0]->getType() == ChimeraTK::NodeType::Device || nodes[1]->getType() == ChimeraTK::NodeType::Device);
205 BOOST_CHECK(nodes[0]->getType() == ChimeraTK::NodeType::Application ||
206 nodes[1]->getType() == ChimeraTK::NodeType::Application);
207
208 auto checker = [](auto proxy) {
209 if constexpr(isVariable(proxy)) {
210 BOOST_TEST(proxy.getName() == "readBack");
211 }
212 else {
213 BOOST_FAIL("Wrong vertex type found");
214 }
215 };
216 bool found = pv.visitByPath("../readBack", checker);
217 BOOST_TEST(found == true);
218 }
219
220 /********************************************************************************************************************/
221
222 BOOST_AUTO_TEST_CASE(testDirectoryProxy) {
223 TestApplication app;
224
225 // get the directory. this relies on some other features...
226 auto dir = app.myModule.getModel().visit(ChimeraTK::Model::returnDirectory, ChimeraTK::Model::getNeighbourDirectory,
228 assert(dir.isValid());
229
230 BOOST_TEST(dir.getName() == "MyModule");
231
232 auto checker = [](auto proxy) {
233 if constexpr(isVariable(proxy)) {
234 BOOST_TEST(proxy.getName() == "readBack");
235 }
236 else {
237 BOOST_FAIL("Wrong vertex type found");
238 }
239 };
240 bool found = dir.visitByPath("./readBack", checker);
241 BOOST_TEST(found == true);
242 }
243
244 /********************************************************************************************************************/
245 /* Test predicates */
246 /********************************************************************************************************************/
247
248 BOOST_AUTO_TEST_CASE(testPredicatesWithProxy) {
250 BOOST_TEST(isRoot(rp) == true);
251 BOOST_TEST(isModuleGroup(rp) == false);
252 BOOST_TEST(isApplicationModule(rp) == false);
253 BOOST_TEST(isVariableGroup(rp) == false);
254 BOOST_TEST(isDeviceModule(rp) == false);
255 BOOST_TEST(isVariable(rp) == false);
256 BOOST_TEST(isDirectory(rp) == false);
257 BOOST_TEST(hasName(rp) == false);
258
260 BOOST_TEST(isRoot(mgp) == false);
261 BOOST_TEST(isModuleGroup(mgp) == true);
262 BOOST_TEST(isApplicationModule(mgp) == false);
263 BOOST_TEST(isVariableGroup(mgp) == false);
264 BOOST_TEST(isDeviceModule(mgp) == false);
265 BOOST_TEST(isVariable(mgp) == false);
266 BOOST_TEST(isDirectory(mgp) == false);
267 BOOST_TEST(hasName(mgp) == true);
268
270 BOOST_TEST(isRoot(amp) == false);
271 BOOST_TEST(isModuleGroup(amp) == false);
272 BOOST_TEST(isApplicationModule(amp) == true);
273 BOOST_TEST(isVariableGroup(amp) == false);
274 BOOST_TEST(isDeviceModule(amp) == false);
275 BOOST_TEST(isVariable(amp) == false);
276 BOOST_TEST(isDirectory(amp) == false);
277 BOOST_TEST(hasName(amp) == true);
278
280 BOOST_TEST(isRoot(vgp) == false);
281 BOOST_TEST(isModuleGroup(vgp) == false);
282 BOOST_TEST(isApplicationModule(vgp) == false);
283 BOOST_TEST(isVariableGroup(vgp) == true);
284 BOOST_TEST(isDeviceModule(vgp) == false);
285 BOOST_TEST(isVariable(vgp) == false);
286 BOOST_TEST(isDirectory(vgp) == false);
287 BOOST_TEST(hasName(vgp) == true);
288
290 BOOST_TEST(isRoot(dmp) == false);
291 BOOST_TEST(isModuleGroup(dmp) == false);
292 BOOST_TEST(isApplicationModule(dmp) == false);
293 BOOST_TEST(isVariableGroup(dmp) == false);
294 BOOST_TEST(isDeviceModule(dmp) == true);
295 BOOST_TEST(isVariable(dmp) == false);
296 BOOST_TEST(isDirectory(dmp) == false);
297 BOOST_TEST(hasName(dmp) == false);
298
300 BOOST_TEST(isRoot(pvp) == false);
301 BOOST_TEST(isModuleGroup(pvp) == false);
302 BOOST_TEST(isApplicationModule(pvp) == false);
303 BOOST_TEST(isVariableGroup(pvp) == false);
304 BOOST_TEST(isDeviceModule(pvp) == false);
305 BOOST_TEST(isVariable(pvp) == true);
306 BOOST_TEST(isDirectory(pvp) == false);
307 BOOST_TEST(hasName(pvp) == true);
308
310 BOOST_TEST(isRoot(dp) == false);
311 BOOST_TEST(isModuleGroup(dp) == false);
312 BOOST_TEST(isApplicationModule(dp) == false);
313 BOOST_TEST(isVariableGroup(dp) == false);
314 BOOST_TEST(isDeviceModule(dp) == false);
315 BOOST_TEST(isVariable(dp) == false);
316 BOOST_TEST(isDirectory(dp) == true);
317 BOOST_TEST(hasName(dp) == true);
318 }
319
320 /********************************************************************************************************************/
321
322 BOOST_AUTO_TEST_CASE(testPredicatesWithProperties) {
323 TestApplication app;
324
326 BOOST_TEST(isRoot(rp) == true);
327 BOOST_TEST(isModuleGroup(rp) == false);
328 BOOST_TEST(isApplicationModule(rp) == false);
329 BOOST_TEST(isVariableGroup(rp) == false);
330 BOOST_TEST(isDeviceModule(rp) == false);
331 BOOST_TEST(isVariable(rp) == false);
332 BOOST_TEST(isDirectory(rp) == false);
333 BOOST_TEST(hasName(rp) == false);
334
336 BOOST_TEST(isRoot(mgp) == false);
337 BOOST_TEST(isModuleGroup(mgp) == true);
338 BOOST_TEST(isApplicationModule(mgp) == false);
339 BOOST_TEST(isVariableGroup(mgp) == false);
340 BOOST_TEST(isDeviceModule(mgp) == false);
341 BOOST_TEST(isVariable(mgp) == false);
342 BOOST_TEST(isDirectory(mgp) == false);
343 BOOST_TEST(hasName(mgp) == true);
344
346 BOOST_TEST(isRoot(amp) == false);
347 BOOST_TEST(isModuleGroup(amp) == false);
348 BOOST_TEST(isApplicationModule(amp) == true);
349 BOOST_TEST(isVariableGroup(amp) == false);
350 BOOST_TEST(isDeviceModule(amp) == false);
351 BOOST_TEST(isVariable(amp) == false);
352 BOOST_TEST(isDirectory(amp) == false);
353 BOOST_TEST(hasName(amp) == true);
354
356 BOOST_TEST(isRoot(vgp) == false);
357 BOOST_TEST(isModuleGroup(vgp) == false);
358 BOOST_TEST(isApplicationModule(vgp) == false);
359 BOOST_TEST(isVariableGroup(vgp) == true);
360 BOOST_TEST(isDeviceModule(vgp) == false);
361 BOOST_TEST(isVariable(vgp) == false);
362 BOOST_TEST(isDirectory(vgp) == false);
363 BOOST_TEST(hasName(vgp) == true);
364
366 BOOST_TEST(isRoot(dmp) == false);
367 BOOST_TEST(isModuleGroup(dmp) == false);
368 BOOST_TEST(isApplicationModule(dmp) == false);
369 BOOST_TEST(isVariableGroup(dmp) == false);
370 BOOST_TEST(isDeviceModule(dmp) == true);
371 BOOST_TEST(isVariable(dmp) == false);
372 BOOST_TEST(isDirectory(dmp) == false);
373 BOOST_TEST(hasName(dmp) == false);
374
376 BOOST_TEST(isRoot(pvp) == false);
377 BOOST_TEST(isModuleGroup(pvp) == false);
378 BOOST_TEST(isApplicationModule(pvp) == false);
379 BOOST_TEST(isVariableGroup(pvp) == false);
380 BOOST_TEST(isDeviceModule(pvp) == false);
381 BOOST_TEST(isVariable(pvp) == true);
382 BOOST_TEST(isDirectory(pvp) == false);
383 BOOST_TEST(hasName(pvp) == true);
384
386 BOOST_TEST(isRoot(dp) == false);
387 BOOST_TEST(isModuleGroup(dp) == false);
388 BOOST_TEST(isApplicationModule(dp) == false);
389 BOOST_TEST(isVariableGroup(dp) == false);
390 BOOST_TEST(isDeviceModule(dp) == false);
391 BOOST_TEST(isVariable(dp) == false);
392 BOOST_TEST(isDirectory(dp) == true);
393 BOOST_TEST(hasName(dp) == true);
394 }
395
396 /********************************************************************************************************************/
397 /* Test search types */
398 /********************************************************************************************************************/
399
400 BOOST_AUTO_TEST_CASE(testAdjacentIn) {
401 TestApplication app;
402
403 // Check on root
404 {
405 size_t foundElements = 0;
406
407 auto checker = [&](auto proxy) {
408 ++foundElements;
409 if constexpr(ChimeraTK::Model::isDeviceModule(proxy)) {
410 // Root is the neighbouring directory of the device module
411 BOOST_TEST(proxy.getAliasOrCdd() == "Dummy0");
412 }
413 else if constexpr(ChimeraTK::Model::isRoot(proxy)) {
414 // Root is its own neighbouring directory
415 }
416 else {
417 BOOST_FAIL("Wrong vertex type found");
418 }
419 };
420
421 app.getModel().visit(checker, ChimeraTK::Model::adjacentInSearch);
422
423 BOOST_TEST(foundElements == 2);
424 }
425
426 // Check on MyModule application module
427 {
428 size_t foundElements = 0;
429
430 auto checker = [&](auto proxy) {
431 ++foundElements;
432 if constexpr(ChimeraTK::Model::isVariable(proxy)) {
433 // this PV is an input
434 BOOST_TEST(proxy.getName() == "readBack");
435 }
436 else if constexpr(ChimeraTK::Model::isRoot(proxy)) {
437 // module is owned by root
438 }
439 else {
440 BOOST_FAIL("Wrong vertex type found");
441 }
442 };
443
444 app.myModule.getModel().visit(checker, ChimeraTK::Model::adjacentInSearch);
445
446 BOOST_TEST(foundElements == 2);
447 }
448 }
449
450 /********************************************************************************************************************/
451
452 BOOST_AUTO_TEST_CASE(testAdjacentOut) {
453 TestApplication app;
454
455 // Check on root
456 {
457 size_t foundElements = 0;
458
459 auto checker = [&](auto proxy) {
460 ++foundElements;
461 if constexpr(ChimeraTK::Model::isDeviceModule(proxy)) {
462 // Root owns the DeviceModule
463 BOOST_TEST(proxy.getAliasOrCdd() == "Dummy0");
464 }
465 else if constexpr(ChimeraTK::Model::isDirectory(proxy)) {
466 // Root owns several directories
467 const auto& name = proxy.getName();
468 BOOST_CHECK(name == "Deeper" || name == "MyModule" || name == "somepath" || name == "Devices");
469 }
470 else if constexpr(ChimeraTK::Model::isModuleGroup(proxy)) {
471 // Root owns the ModuleGroup
472 const auto& name = proxy.getName();
473 BOOST_CHECK(name == "Deeper/hierarchies");
474 }
475 else if constexpr(ChimeraTK::Model::isApplicationModule(proxy)) {
476 const auto& name = proxy.getName();
477 BOOST_CHECK(name == "MyModule" || name == "Deeper/MyModule" || name == "/Devices/Dummy0");
478 }
479 else if constexpr(ChimeraTK::Model::isRoot(proxy)) {
480 // Root is its own neighbouring directory
481 }
482 else {
483 BOOST_FAIL("Wrong vertex type found");
484 }
485 };
486
487 app.getModel().visit(checker, ChimeraTK::Model::adjacentOutSearch);
488
489 BOOST_TEST(foundElements == 10);
490 }
491
492 // Check on MyModule application module
493 {
494 size_t foundElements = 0;
495
496 auto checker = [&](auto proxy) {
497 ++foundElements;
498 if constexpr(ChimeraTK::Model::isVariable(proxy)) {
499 // this variable is an output
500 BOOST_CHECK(proxy.getName() == "actuator");
501 }
502 else if constexpr(ChimeraTK::Model::isDirectory(proxy)) {
503 // The neightbouring directory
504 BOOST_CHECK(proxy.getName() == "MyModule");
505 }
506 else if constexpr(ChimeraTK::Model::isVariableGroup(proxy)) {
507 // VariableGroup owned by the module
508 BOOST_CHECK(proxy.getName() == "pointlessVariableGroup");
509 }
510 else {
511 BOOST_FAIL("Wrong vertex type found");
512 }
513 };
514
515 app.myModule.getModel().visit(checker, ChimeraTK::Model::adjacentOutSearch);
516
517 BOOST_TEST(foundElements == 4); // actuator is found twice because of pvAccess and ownership relationships
518 }
519 }
520
521 /********************************************************************************************************************/
522
523 // helper class for testAdjacent
524 struct Item {
525 template<typename PROXY>
526 explicit Item(PROXY proxy) : type(typeid(proxy)) {
527 if constexpr(ChimeraTK::Model::hasName(proxy)) {
528 nameOrAlias = proxy.getName();
529 }
530 else if constexpr(ChimeraTK::Model::isDeviceModule(proxy)) {
531 nameOrAlias = proxy.getAliasOrCdd();
532 }
533 else {
534 nameOrAlias = "(unnamed)";
535 }
536 }
537 const std::type_info& type;
538 std::string nameOrAlias;
539
540 bool operator<(const Item& rhs) const {
541 return &type < &rhs.type || (&type == &rhs.type && nameOrAlias < rhs.nameOrAlias);
542 }
543 };
544
545 /********************************************************************************************************************/
546
547 BOOST_AUTO_TEST_CASE(testAdjacent) {
548 // adjacent is the sum of adjacentIn and adjacentOut
549 TestApplication app;
550
551 // First collect information about search results of adjacentOut and adjacentIn.
552 std::set<Item> items;
553 size_t itemsToFind = 0; // count also duplicates
554 auto collector = [&](auto proxy) {
555 Item itemToInsert(proxy);
556 items.insert(itemToInsert);
557 ++itemsToFind;
558 };
559
560 app.getModel().visit(collector, ChimeraTK::Model::adjacentOutSearch);
561 app.getModel().visit(collector, ChimeraTK::Model::adjacentInSearch);
562
563 // Now compare the result of the adjacent search (without implying a certain ordering)
564 size_t itemsFound = 0;
565 auto finder = [&](auto proxy) {
566 Item itemToFind(proxy);
567 // adjacent search result item must be among items previously found in either adjacentOut or adjacentIn
568 BOOST_CHECK(items.find(itemToFind) != items.end());
569 ++itemsFound;
570 };
571 app.getModel().visit(finder, ChimeraTK::Model::adjacentSearch);
572 // check that all items have been found
573 BOOST_TEST(itemsFound == itemsToFind);
574 }
575
576 /********************************************************************************************************************/
577
578 BOOST_AUTO_TEST_CASE(testDepthFirstSearch) {
579 TestApplication app;
580
581 std::vector<std::string> pvNames;
582
583 auto pvNamesFiller = [&](auto proxy) { pvNames.push_back(proxy.getFullyQualifiedPath()); };
584
585 app.getModel().visit(pvNamesFiller, ChimeraTK::Model::depthFirstSearch, ChimeraTK::Model::keepDirectories,
586 ChimeraTK::Model::keepParenthood);
587
588 BOOST_TEST(pvNames.size() == 10);
589
590 // All directories have been found
591 BOOST_CHECK(std::find(pvNames.begin(), pvNames.end(), "/Deeper") != pvNames.end());
592 BOOST_CHECK(std::find(pvNames.begin(), pvNames.end(), "/Deeper/hierarchies") != pvNames.end());
593 BOOST_CHECK(std::find(pvNames.begin(), pvNames.end(), "/Deeper/hierarchies/need") != pvNames.end());
594 BOOST_CHECK(std::find(pvNames.begin(), pvNames.end(), "/Deeper/MyModule") != pvNames.end());
595 BOOST_CHECK(std::find(pvNames.begin(), pvNames.end(), "/Deeper/MyModule/pointlessVariableGroup") != pvNames.end());
596 BOOST_CHECK(std::find(pvNames.begin(), pvNames.end(), "/somepath") != pvNames.end());
597 BOOST_CHECK(std::find(pvNames.begin(), pvNames.end(), "/MyModule") != pvNames.end());
598 BOOST_CHECK(std::find(pvNames.begin(), pvNames.end(), "/MyModule/pointlessVariableGroup") != pvNames.end());
599 BOOST_CHECK(std::find(pvNames.begin(), pvNames.end(), "/Devices") != pvNames.end());
600 BOOST_CHECK(std::find(pvNames.begin(), pvNames.end(), "/Devices/Dummy0") != pvNames.end());
601
602 // Check ordering: depth first, not breadth first
603 // Note: The ordering on a single hierarchy is not strictly defined, hence we need to make the test insensitive
604 // to allowed reordering. Hence we have two allowed cases:
605 // 1) /Deeper/hierarchies is found before /Deeper/MyModule
606 // 2) /Deeper/MyModule is found before /Deeper/hierarchies
607 // In case 1), /Deeper/hierarchies/need needs to be found before /Deeper/MyModule
608 // In case 2), /Deeper/MyModule/pointlessVariableGroup needs to be found before /Deeper/hierarchies
609 auto deeperHierarchies = std::find(pvNames.begin(), pvNames.end(), "/Deeper/hierarchies");
610 auto deeperHierarchiesNeed = std::find(pvNames.begin(), pvNames.end(), "/Deeper/hierarchies/need");
611 auto deeperMyModule = std::find(pvNames.begin(), pvNames.end(), "/Deeper/MyModule");
612 auto deeperMyModulePVG = std::find(pvNames.begin(), pvNames.end(), "/Deeper/MyModule/pointlessVariableGroup");
613
614 BOOST_CHECK((deeperHierarchies < deeperMyModule && deeperHierarchiesNeed < deeperMyModule) ||
615 (deeperMyModule < deeperHierarchies && deeperMyModulePVG < deeperHierarchies));
616 }
617
618 /********************************************************************************************************************/
619
620 BOOST_AUTO_TEST_CASE(testBreadthFirstSearch) {
621 TestApplication app;
622
623 std::vector<std::string> pvNames;
624
625 auto pvNamesFiller = [&](auto proxy) { pvNames.push_back(proxy.getFullyQualifiedPath()); };
626
627 app.getModel().visit(pvNamesFiller, ChimeraTK::Model::breadthFirstSearch, ChimeraTK::Model::keepDirectories,
628 ChimeraTK::Model::keepParenthood);
629
630 BOOST_TEST(pvNames.size() == 10);
631
632 // All directories have been found
633 BOOST_CHECK(std::find(pvNames.begin(), pvNames.end(), "/Deeper") != pvNames.end());
634 BOOST_CHECK(std::find(pvNames.begin(), pvNames.end(), "/Deeper/hierarchies") != pvNames.end());
635 BOOST_CHECK(std::find(pvNames.begin(), pvNames.end(), "/Deeper/hierarchies/need") != pvNames.end());
636 BOOST_CHECK(std::find(pvNames.begin(), pvNames.end(), "/Deeper/MyModule") != pvNames.end());
637 BOOST_CHECK(std::find(pvNames.begin(), pvNames.end(), "/Deeper/MyModule/pointlessVariableGroup") != pvNames.end());
638 BOOST_CHECK(std::find(pvNames.begin(), pvNames.end(), "/somepath") != pvNames.end());
639 BOOST_CHECK(std::find(pvNames.begin(), pvNames.end(), "/MyModule") != pvNames.end());
640 BOOST_CHECK(std::find(pvNames.begin(), pvNames.end(), "/MyModule/pointlessVariableGroup") != pvNames.end());
641 BOOST_CHECK(std::find(pvNames.begin(), pvNames.end(), "/Devices") != pvNames.end());
642 BOOST_CHECK(std::find(pvNames.begin(), pvNames.end(), "/Devices/Dummy0") != pvNames.end());
643
644 // Check ordering: breadth first, not depth first
645 auto deeperHierarchies = std::find(pvNames.begin(), pvNames.end(), "/Deeper/hierarchies");
646 auto deeperHierarchiesNeed = std::find(pvNames.begin(), pvNames.end(), "/Deeper/hierarchies/need");
647 auto deeperMyModule = std::find(pvNames.begin(), pvNames.end(), "/Deeper/MyModule");
648 auto deeperMyModulePVG = std::find(pvNames.begin(), pvNames.end(), "/Deeper/MyModule/pointlessVariableGroup");
649
650 BOOST_CHECK(deeperHierarchies < deeperHierarchiesNeed);
651 BOOST_CHECK(deeperMyModule < deeperHierarchiesNeed);
652 BOOST_CHECK(deeperHierarchies < deeperMyModulePVG);
653 BOOST_CHECK(deeperMyModule < deeperMyModulePVG);
654 }
655
656 /********************************************************************************************************************/
657 /* Test edge/relationship filters */
658 /********************************************************************************************************************/
659
660 BOOST_AUTO_TEST_CASE(testKeepPvAccess) {
661 TestApplication app;
662
663 // Run check on ApplicationModule MyModule
664 {
665 size_t foundElements = 0;
666
667 auto checker = [&](auto proxy) {
668 ++foundElements;
669 if constexpr(ChimeraTK::Model::isVariable(proxy)) {
670 BOOST_CHECK(proxy.getName() == "readBack" || proxy.getName() == "actuator");
671 }
672 else {
673 BOOST_FAIL("Wrong vertex type found");
674 }
675 };
676
677 app.myModule.getModel().visit(checker, ChimeraTK::Model::keepPvAccess, ChimeraTK::Model::adjacentSearch);
678
679 BOOST_TEST(foundElements == 2);
680 }
681
682 // Run check on the PV readBack
683 {
684 size_t foundElements = 0;
685
686 auto checker = [&](auto proxy) {
687 ++foundElements;
688 if constexpr(ChimeraTK::Model::isDeviceModule(proxy)) {
689 BOOST_CHECK(proxy.getAliasOrCdd() == "Dummy0");
690 }
691 else if constexpr(ChimeraTK::Model::isApplicationModule(proxy)) {
692 BOOST_CHECK(proxy.getName() == "MyModule");
693 }
694 else {
695 BOOST_FAIL("Wrong vertex type found");
696 }
697 };
698
700 checker, ChimeraTK::Model::keepPvAccess, ChimeraTK::Model::adjacentSearch);
701
702 BOOST_TEST(foundElements == 2);
703 }
704 }
705
706 /********************************************************************************************************************/
707
708 BOOST_AUTO_TEST_CASE(testKeepOwnership) {
709 TestApplication app;
710
711 // Run check on ApplicationModule MyModule
712 {
713 size_t foundElements = 0;
714
715 auto checker = [&](auto proxy) {
716 ++foundElements;
717 if constexpr(ChimeraTK::Model::isVariable(proxy)) {
718 BOOST_CHECK(proxy.getName() == "actuator");
719 }
720 else if constexpr(ChimeraTK::Model::isVariableGroup(proxy)) {
721 BOOST_CHECK(proxy.getName() == "pointlessVariableGroup");
722 }
723 else if constexpr(ChimeraTK::Model::isRoot(proxy)) {
724 }
725 else {
726 BOOST_FAIL("Wrong vertex type found");
727 }
728 };
729
730 app.myModule.getModel().visit(checker, ChimeraTK::Model::keepOwnership, ChimeraTK::Model::adjacentSearch);
731
732 BOOST_TEST(foundElements == 3);
733 }
734
735 // Run check on the PV readBack
736 {
737 size_t foundElements = 0;
738
739 auto checker = [&](auto proxy) {
740 ++foundElements;
741 if constexpr(ChimeraTK::Model::isVariableGroup(proxy)) {
742 BOOST_CHECK(proxy.getName() == "pointlessVariableGroup");
743 }
744 else if constexpr(ChimeraTK::Model::isDeviceModule(proxy)) {
745 BOOST_CHECK(proxy.getAliasOrCdd() == "Dummy0");
746 }
747 else {
748 BOOST_FAIL("Wrong vertex type found");
749 }
750 };
751
753 checker, ChimeraTK::Model::keepOwnership, ChimeraTK::Model::adjacentSearch);
754
755 BOOST_TEST(foundElements == 2);
756 }
757 }
758
759 /********************************************************************************************************************/
760
761 BOOST_AUTO_TEST_CASE(testKeepParenthood) {
762 TestApplication app;
763
764 // Run check on directory MyModule
765 {
766 // get the directory. this relies on some other features...
767 auto dir =
768 app.myModule.getModel().visit(ChimeraTK::Model::returnDirectory, ChimeraTK::Model::getNeighbourDirectory,
770 assert(dir.isValid());
771
772 size_t foundElements = 0;
773
774 auto checker = [&](auto proxy) {
775 ++foundElements;
776 if constexpr(ChimeraTK::Model::isVariable(proxy)) {
777 BOOST_CHECK(proxy.getName() == "actuator" || proxy.getName() == "readBack");
778 }
779 else if constexpr(ChimeraTK::Model::isDirectory(proxy)) {
780 BOOST_CHECK(proxy.getName() == "pointlessVariableGroup");
781 }
782 else if constexpr(ChimeraTK::Model::isRoot(proxy)) {
783 }
784 else {
785 BOOST_FAIL("Wrong vertex type found");
786 }
787 };
788
789 dir.visit(checker, ChimeraTK::Model::keepParenthood, ChimeraTK::Model::adjacentSearch);
790
791 BOOST_TEST(foundElements == 4);
792 }
793
794 // Run check on the PV readBack
795 {
796 size_t foundElements = 0;
797
798 auto checker = [&](auto proxy) {
799 ++foundElements;
800 if constexpr(ChimeraTK::Model::isDirectory(proxy)) {
801 BOOST_CHECK(proxy.getName() == "MyModule");
802 }
803 else {
804 BOOST_FAIL("Wrong vertex type found");
805 }
806 };
807
809 checker, ChimeraTK::Model::keepParenthood, ChimeraTK::Model::adjacentSearch);
810
811 BOOST_TEST(foundElements == 1);
812 }
813 }
814
815 /********************************************************************************************************************/
816
817 BOOST_AUTO_TEST_CASE(testKeepNeighbourhood) {
818 TestApplication app;
819
820 // Run check on ApplicationModule MyModule
821 {
822 size_t foundElements = 0;
823
824 auto checker = [&](auto proxy) {
825 ++foundElements;
826 if constexpr(ChimeraTK::Model::isDirectory(proxy)) {
827 BOOST_CHECK(proxy.getName() == "MyModule");
828 }
829 else {
830 BOOST_FAIL("Wrong vertex type found");
831 }
832 };
833
834 app.myModule.getModel().visit(checker, ChimeraTK::Model::keepNeighbourhood, ChimeraTK::Model::adjacentOutSearch);
835
836 BOOST_TEST(foundElements == 1);
837 }
838
839 // Run check on the directory /Deeper/hierarchies
840 {
841 // get the directory. this relies on some other features...
843 [[maybe_unused]] auto found = app.getModel().visitByPath("/Deeper/hierarchies", [&](auto proxy) {
844 if constexpr(ChimeraTK::Model::isDirectory(proxy)) {
845 dir = proxy;
846 }
847 });
848 assert(found);
849
850 size_t foundElements = 0;
851
852 auto checker = [&](auto proxy) {
853 ++foundElements;
854 if constexpr(ChimeraTK::Model::isModuleGroup(proxy)) {
855 BOOST_CHECK(proxy.getName() == "Deeper/hierarchies");
856 }
857 else if constexpr(ChimeraTK::Model::isApplicationModule(proxy)) {
858 BOOST_CHECK(proxy.getName() == ".");
859 }
860 else {
861 BOOST_FAIL("Wrong vertex type found");
862 }
863 };
864
865 dir.visit(checker, ChimeraTK::Model::keepNeighbourhood, ChimeraTK::Model::adjacentInSearch);
866
867 BOOST_TEST(foundElements == 2);
868 }
869 }
870
871 /********************************************************************************************************************/
872 /* Test vertex/object type filters */
873 /********************************************************************************************************************/
874
875 BOOST_AUTO_TEST_CASE(testKeepModuleGroups) {
876 TestApplication app;
877
878 // Run check on root
879 {
880 size_t foundElements = 0;
881
882 auto checker = [&](auto proxy) {
883 ++foundElements;
884 if constexpr(ChimeraTK::Model::isModuleGroup(proxy)) {
885 BOOST_CHECK(proxy.getName() == "Deeper/hierarchies");
886 }
887 else {
888 BOOST_FAIL("Wrong vertex type found");
889 }
890 };
891
892 app.getModel().visit(checker, ChimeraTK::Model::keepModuleGroups, ChimeraTK::Model::adjacentSearch);
893
894 BOOST_TEST(foundElements == 1);
895 }
896
897 // Run check on the application module "."
898 {
899 size_t foundElements = 0;
900
901 auto checker = [&](auto proxy) {
902 ++foundElements;
903 if constexpr(ChimeraTK::Model::isModuleGroup(proxy)) {
904 BOOST_CHECK(proxy.getName() == "Deeper/hierarchies");
905 }
906 else {
907 BOOST_FAIL("Wrong vertex type found");
908 }
909 };
910
912 checker, ChimeraTK::Model::keepModuleGroups, ChimeraTK::Model::adjacentSearch);
913
914 BOOST_TEST(foundElements == 1);
915 }
916 }
917
918 /********************************************************************************************************************/
919
920 BOOST_AUTO_TEST_CASE(testKeepApplicationModules) {
921 TestApplication app;
922
923 // Run check on module group Deeper/hierarchies
924 {
925 size_t foundElements = 0;
926
927 auto checker = [&](auto proxy) {
928 ++foundElements;
929 if constexpr(ChimeraTK::Model::isApplicationModule(proxy)) {
930 BOOST_CHECK(proxy.getName() == ".");
931 }
932 else {
933 BOOST_FAIL("Wrong vertex type found");
934 }
935 };
936
938 checker, ChimeraTK::Model::keepApplicationModules, ChimeraTK::Model::adjacentSearch);
939
940 BOOST_TEST(foundElements == 1);
941 }
942
943 // Run check on PV "also"
944 {
945 size_t foundElements = 0;
946
947 auto checker = [&](auto proxy) {
948 ++foundElements;
949 if constexpr(ChimeraTK::Model::isApplicationModule(proxy)) {
950 BOOST_CHECK(proxy.getName() == ".");
951 }
952 else {
953 BOOST_FAIL("Wrong vertex type found");
954 }
955 };
956
958 checker, ChimeraTK::Model::keepApplicationModules, ChimeraTK::Model::adjacentSearch);
959
960 BOOST_TEST(foundElements ==
961 2); // The element is found twice because there is an ownership relation and a PV access relation
962 }
963 }
964
965 /********************************************************************************************************************/
966
967 BOOST_AUTO_TEST_CASE(testKeepVariableGroups) {
968 TestApplication app;
969
970 // Run check on application module MyModule
971 {
972 size_t foundElements = 0;
973
974 auto checker = [&](auto proxy) {
975 ++foundElements;
976 if constexpr(ChimeraTK::Model::isVariableGroup(proxy)) {
977 BOOST_CHECK(proxy.getName() == "pointlessVariableGroup");
978 }
979 else {
980 BOOST_FAIL("Wrong vertex type found");
981 }
982 };
983
984 app.myModule.getModel().visit(checker, ChimeraTK::Model::keepVariableGroups, ChimeraTK::Model::adjacentSearch);
985
986 BOOST_TEST(foundElements == 1);
987 }
988
989 // Run check on PV "tests"
990 {
991 size_t foundElements = 0;
992
993 auto checker = [&](auto proxy) {
994 ++foundElements;
995 if constexpr(ChimeraTK::Model::isVariableGroup(proxy)) {
996 BOOST_CHECK(proxy.getName() == "need");
997 }
998 else {
999 BOOST_FAIL("Wrong vertex type found");
1000 }
1001 };
1002
1004 checker, ChimeraTK::Model::keepVariableGroups, ChimeraTK::Model::adjacentSearch);
1005
1006 BOOST_TEST(foundElements == 1);
1007 }
1008 }
1009
1010 /********************************************************************************************************************/
1011
1012 BOOST_AUTO_TEST_CASE(testKeepDeviceModules) {
1013 TestApplication app;
1014
1015 // Run check on root
1016 {
1017 size_t foundElements = 0;
1018
1019 auto checker = [&](auto proxy) {
1020 ++foundElements;
1021 if constexpr(ChimeraTK::Model::isDeviceModule(proxy)) {
1022 BOOST_CHECK(proxy.getAliasOrCdd() == "Dummy0");
1023 }
1024 else {
1025 BOOST_FAIL("Wrong vertex type found");
1026 }
1027 };
1028
1029 app.getModel().visit(checker, ChimeraTK::Model::keepDeviceModules, ChimeraTK::Model::adjacentSearch);
1030
1031 BOOST_TEST(foundElements == 2); // found twice because ownership and neighbourhood relation
1032 }
1033
1034 // Run check on PV "tests"
1035 {
1036 size_t foundElements = 0;
1037
1038 auto checker = [&](auto proxy) {
1039 ++foundElements;
1040 if constexpr(ChimeraTK::Model::isDeviceModule(proxy)) {
1041 BOOST_CHECK(proxy.getAliasOrCdd() == "Dummy0");
1042 }
1043 else {
1044 BOOST_FAIL("Wrong vertex type found");
1045 }
1046 };
1047
1049 checker, ChimeraTK::Model::keepDeviceModules, ChimeraTK::Model::adjacentSearch);
1050
1051 BOOST_TEST(foundElements == 2); // found twice because ownership and pv acces relation
1052 }
1053 }
1054
1055 /********************************************************************************************************************/
1056
1057 BOOST_AUTO_TEST_CASE(testKeepProcessVariables) {
1058 TestApplication app;
1059
1060 // Run check on application module MyModule
1061 {
1062 size_t foundElements = 0;
1063
1064 auto checker = [&](auto proxy) {
1065 ++foundElements;
1066 if constexpr(ChimeraTK::Model::isVariable(proxy)) {
1067 BOOST_CHECK(proxy.getName() == "readBack" || proxy.getName() == "actuator");
1068 }
1069 else {
1070 BOOST_FAIL("Wrong vertex type found");
1071 }
1072 };
1073
1074 app.myModule.getModel().visit(checker, ChimeraTK::Model::keepProcessVariables, ChimeraTK::Model::adjacentSearch);
1075
1076 BOOST_TEST(foundElements == 3); // actuator is found twice due to pvAccess and ownership relation
1077 }
1078
1079 // Run check on the directory /Deeper/hierarchies
1080 {
1081 // get the directory. this relies on some other features...
1083 [[maybe_unused]] auto found = app.getModel().visitByPath("/Deeper/hierarchies", [&](auto proxy) {
1084 if constexpr(ChimeraTK::Model::isDirectory(proxy)) {
1085 dir = proxy;
1086 }
1087 });
1088 assert(found);
1089
1090 size_t foundElements = 0;
1091
1092 auto checker = [&](auto proxy) {
1093 ++foundElements;
1094 if constexpr(ChimeraTK::Model::isVariable(proxy)) {
1095 BOOST_CHECK(proxy.getName() == "also");
1096 }
1097 else {
1098 BOOST_FAIL("Wrong vertex type found");
1099 }
1100 };
1101
1102 dir.visit(checker, ChimeraTK::Model::keepProcessVariables, ChimeraTK::Model::adjacentSearch);
1103
1104 BOOST_TEST(foundElements == 1);
1105 }
1106 }
1107
1108 /********************************************************************************************************************/
1109
1110 BOOST_AUTO_TEST_CASE(testKeepDirectories) {
1111 TestApplication app;
1112
1113 // Run check on application module MyModule
1114 {
1115 size_t foundElements = 0;
1116
1117 auto checker = [&](auto proxy) {
1118 ++foundElements;
1119 if constexpr(ChimeraTK::Model::isDirectory(proxy)) {
1120 BOOST_CHECK(proxy.getName() == "MyModule");
1121 }
1122 else {
1123 BOOST_FAIL("Wrong vertex type found");
1124 }
1125 };
1126
1127 app.myModule.getModel().visit(checker, ChimeraTK::Model::keepDirectories, ChimeraTK::Model::adjacentSearch);
1128
1129 BOOST_TEST(foundElements == 1);
1130 }
1131
1132 // Run check on the directory /Deeper/hierarchies
1133 {
1134 // get the directory. this relies on some other features...
1136 [[maybe_unused]] auto found = app.getModel().visitByPath("/Deeper/hierarchies", [&](auto proxy) {
1137 if constexpr(ChimeraTK::Model::isDirectory(proxy)) {
1138 dir = proxy;
1139 }
1140 });
1141 assert(found);
1142
1143 size_t foundElements = 0;
1144
1145 auto checker = [&](auto proxy) {
1146 ++foundElements;
1147 if constexpr(ChimeraTK::Model::isDirectory(proxy)) {
1148 BOOST_CHECK(proxy.getName() == "Deeper" || proxy.getName() == "need");
1149 }
1150 else {
1151 BOOST_FAIL("Wrong vertex type found");
1152 }
1153 };
1154
1155 dir.visit(checker, ChimeraTK::Model::keepDirectories, ChimeraTK::Model::adjacentSearch);
1156
1157 BOOST_TEST(foundElements == 2);
1158 }
1159 }
1160
1161 /********************************************************************************************************************/
1162
1163 BOOST_AUTO_TEST_CASE(testKeepName) {
1164 TestApplication app;
1165
1166 // Run check on application root
1167 {
1168 size_t foundElements = 0;
1169
1170 auto checker = [&](auto proxy) {
1171 ++foundElements;
1172 if constexpr(ChimeraTK::Model::isDirectory(proxy)) {
1173 BOOST_CHECK(proxy.getName() == "MyModule");
1174 }
1175 else if constexpr(ChimeraTK::Model::isApplicationModule(proxy)) {
1176 BOOST_CHECK(proxy.getName() == "MyModule");
1177 }
1178 else {
1179 BOOST_FAIL("Wrong vertex type found");
1180 }
1181 };
1182
1183 app.getModel().visit(checker, ChimeraTK::Model::keepName("MyModule"), ChimeraTK::Model::adjacentSearch);
1184
1185 BOOST_TEST(foundElements == 2);
1186 }
1187 }
1188
1189 /********************************************************************************************************************/
1190
1192 TestApplication app;
1193
1194 // Search for tag A
1195 {
1196 size_t foundElements = 0;
1197
1198 auto checker = [&](auto proxy) {
1199 ++foundElements;
1200 BOOST_CHECK(proxy.getFullyQualifiedPath() == "/Deeper/hierarchies/also" ||
1201 proxy.getFullyQualifiedPath() == "/Deeper/hierarchies/need/tests");
1202 };
1203
1204 app.getModel().visit(checker, ChimeraTK::Model::keepTag("A"), ChimeraTK::Model::depthFirstSearch,
1205 ChimeraTK::Model::keepProcessVariables);
1206
1207 BOOST_TEST(foundElements == 2);
1208 }
1209
1210 // Search for tag B
1211 {
1212 size_t foundElements = 0;
1213
1214 auto checker = [&](auto proxy) {
1215 ++foundElements;
1216 BOOST_CHECK(proxy.getFullyQualifiedPath() == "/MyModule/actuator" ||
1217 proxy.getFullyQualifiedPath() == "/Deeper/MyModule/actuator" ||
1218 proxy.getFullyQualifiedPath() == "/Deeper/hierarchies/need/tests");
1219 };
1220
1221 app.getModel().visit(checker, ChimeraTK::Model::keepTag("B"), ChimeraTK::Model::depthFirstSearch,
1222 ChimeraTK::Model::keepProcessVariables);
1223
1224 BOOST_TEST(foundElements == 3);
1225 }
1226 }
1227
1228 /********************************************************************************************************************/
1229 /* Test search options */
1230 /********************************************************************************************************************/
1231
1232 BOOST_AUTO_TEST_CASE(testReturnFirstHit) {
1233 TestApplication app;
1234 std::string alias;
1235
1236 // Check returning a string
1237 auto returnAlias = [&](auto proxy) -> std::string { return proxy.getAliasOrCdd(); };
1238 alias = app.getModel().visit(returnAlias, ChimeraTK::Model::depthFirstSearch, ChimeraTK::Model::keepDeviceModules,
1239 ChimeraTK::Model::returnFirstHit(std::string{}));
1240 BOOST_TEST(alias == "Dummy0");
1241
1242 // Check returning nothing (void)
1243 alias = "";
1244 auto setAlias = [&](auto proxy) -> void { alias = proxy.getAliasOrCdd(); };
1245 app.getModel().visit(setAlias, ChimeraTK::Model::depthFirstSearch, ChimeraTK::Model::keepDeviceModules,
1247 BOOST_TEST(alias == "Dummy0");
1248 }
1249
1250 /********************************************************************************************************************/
1251
1252 BOOST_AUTO_TEST_CASE(testContinueSearchDisjunctTrees) {
1253 TestApplication app;
1254
1255 size_t hits{0};
1256 auto countHits = [&](auto) { ++hits; };
1257 app.getModel().visit(countHits, ChimeraTK::Model::depthFirstSearch, ChimeraTK::Model::keepPvAccess,
1258 ChimeraTK::Model::keepProcessVariables);
1259
1260 // first make sure nothing is found when doing a DFS without continueSearchDisjunctTrees from root with the
1261 // keepPvAccess
1262 BOOST_TEST(hits == 0);
1263
1264 // same test again with continueSearchDisjunctTrees should now find something as the search is continued in the
1265 // disjuct parts
1266 app.getModel().visit(countHits, ChimeraTK::Model::depthFirstSearch, ChimeraTK::Model::keepPvAccess,
1267 ChimeraTK::Model::keepProcessVariables, ChimeraTK::Model::continueSearchDisjunctTrees);
1268 BOOST_TEST(hits == 10);
1269 }
1270
1271 /********************************************************************************************************************/
1272 /* Test OrSet and AndSet of filters */
1273 /********************************************************************************************************************/
1274
1275 BOOST_AUTO_TEST_CASE(testOrSetVertexFilter) {
1276 TestApplication app;
1277
1278 size_t foundElements = 0;
1279
1280 auto checker = [&](auto proxy) {
1281 ++foundElements;
1282 if constexpr(ChimeraTK::Model::isModuleGroup(proxy)) {
1283 BOOST_CHECK(proxy.getName() == "Deeper/hierarchies");
1284 }
1285 else if constexpr(ChimeraTK::Model::isDeviceModule(proxy)) {
1286 BOOST_CHECK(proxy.getAliasOrCdd() == "Dummy0");
1287 }
1288 else {
1289 BOOST_FAIL("Wrong vertex type found");
1290 }
1291 };
1292
1293 app.getModel().visit(checker, ChimeraTK::Model::keepModuleGroups || ChimeraTK::Model::keepDeviceModules,
1294 ChimeraTK::Model::adjacentSearch);
1295
1296 BOOST_TEST(foundElements == 3); // the DeviceModule is found twice (ownership + neighbourhood)
1297 }
1298
1299 /********************************************************************************************************************/
1300
1301 BOOST_AUTO_TEST_CASE(testAndSetVertexFilter) {
1302 TestApplication app;
1303
1304 size_t foundElements = 0;
1305
1306 auto checker = [&](auto proxy) {
1307 ++foundElements;
1308 if constexpr(ChimeraTK::Model::isModuleGroup(proxy)) {
1309 BOOST_CHECK(proxy.getName() == "Deeper/hierarchies");
1310 }
1311 else {
1312 BOOST_FAIL("Wrong vertex type found");
1313 }
1314 };
1315
1316 app.getModel().visit(checker,
1317 ChimeraTK::Model::keepModuleGroups && ChimeraTK::Model::keepName("Deeper/hierarchies"),
1318 ChimeraTK::Model::adjacentSearch);
1319
1320 BOOST_TEST(foundElements == 1);
1321 }
1322
1323 /********************************************************************************************************************/
1324
1325 BOOST_AUTO_TEST_CASE(testAndSetInOrSetVertexFilter) {
1326 TestApplication app;
1327
1328 size_t foundElements = 0;
1329
1330 auto checker = [&](auto proxy) {
1331 ++foundElements;
1332 if constexpr(ChimeraTK::Model::isModuleGroup(proxy)) {
1333 BOOST_CHECK(proxy.getName() == "Deeper/hierarchies");
1334 }
1335 else if constexpr(ChimeraTK::Model::isDeviceModule(proxy)) {
1336 BOOST_CHECK(proxy.getAliasOrCdd() == "Dummy0");
1337 }
1338 else {
1339 BOOST_FAIL("Wrong vertex type found");
1340 }
1341 };
1342
1343 app.getModel().visit(checker,
1344 (ChimeraTK::Model::keepModuleGroups && ChimeraTK::Model::keepName("Deeper/hierarchies")) ||
1345 ChimeraTK::Model::keepDeviceModules,
1346 ChimeraTK::Model::adjacentOutSearch);
1347
1348 BOOST_TEST(foundElements == 2);
1349
1350 foundElements = 0;
1351
1352 app.getModel().visit(checker,
1353 ChimeraTK::Model::keepDeviceModules ||
1354 (ChimeraTK::Model::keepModuleGroups && ChimeraTK::Model::keepName("Deeper/hierarchies")),
1355 ChimeraTK::Model::adjacentOutSearch);
1356
1357 BOOST_TEST(foundElements == 2);
1358 }
1359
1360 /********************************************************************************************************************/
1361
1362 BOOST_AUTO_TEST_CASE(testOrSetInAndSetVertexFilter) {
1363 TestApplication app;
1364
1365 size_t foundElements = 0;
1366
1367 auto checker = [&](auto proxy) {
1368 ++foundElements;
1369 if constexpr(ChimeraTK::Model::isApplicationModule(proxy)) {
1370 BOOST_CHECK(proxy.getName() == "MyModule");
1371 }
1372 else if constexpr(ChimeraTK::Model::isDirectory(proxy)) {
1373 BOOST_CHECK(proxy.getName() == "MyModule");
1374 }
1375 else {
1376 BOOST_FAIL("Wrong vertex type found");
1377 }
1378 };
1379
1380 app.getModel().visit(checker,
1381 (ChimeraTK::Model::keepApplicationModules || ChimeraTK::Model::keepDirectories) &&
1382 ChimeraTK::Model::keepName("MyModule"),
1383 ChimeraTK::Model::adjacentOutSearch);
1384
1385 BOOST_TEST(foundElements == 2);
1386
1387 foundElements = 0;
1388
1389 app.getModel().visit(checker,
1390 ChimeraTK::Model::keepName("MyModule") &&
1391 (ChimeraTK::Model::keepApplicationModules || ChimeraTK::Model::keepDirectories),
1392 ChimeraTK::Model::adjacentOutSearch);
1393
1394 BOOST_TEST(foundElements == 2);
1395 }
1396
1397 /********************************************************************************************************************/
1398
1399 BOOST_AUTO_TEST_CASE(testOrSetEdgeFilter) {
1400 TestApplication app;
1401
1402 size_t foundElements = 0;
1403
1404 auto checker = [&](auto proxy) {
1405 ++foundElements;
1406 if constexpr(ChimeraTK::Model::isDeviceModule(proxy)) {
1407 BOOST_CHECK(proxy.getAliasOrCdd() == "Dummy0");
1408 }
1409 else if constexpr(ChimeraTK::Model::isDirectory(proxy)) {
1410 BOOST_CHECK(proxy.getName() == "Deeper" || proxy.getName() == "MyModule" || proxy.getName() == "somepath" ||
1411 proxy.getName() == "Devices");
1412 }
1413 else if constexpr(ChimeraTK::Model::isRoot(proxy)) {
1414 }
1415 else {
1416 BOOST_FAIL("Wrong vertex type found");
1417 }
1418 };
1419
1420 app.getModel().visit(checker, ChimeraTK::Model::keepNeighbourhood || ChimeraTK::Model::keepParenthood,
1421 ChimeraTK::Model::adjacentSearch);
1422
1423 BOOST_TEST(foundElements == 7); // ROOT is found twice: incoming and outgoing neighbourhood to itself
1424 }
1425
1426 // Note: AndSet for edge filters does not really make any sense, since each edge can have only one single type!
1427
1428 /********************************************************************************************************************/
1429 /* Test combined search configurations */
1430 /********************************************************************************************************************/
1431
1432 BOOST_AUTO_TEST_CASE(testOwnedModuleGroups) {
1433 TestApplication app;
1434
1435 {
1436 size_t foundElements = 0;
1437
1438 auto checker = [&](auto proxy) {
1439 ++foundElements;
1440 if constexpr(ChimeraTK::Model::isModuleGroup(proxy)) {
1441 BOOST_CHECK(proxy.getName() == "Deeper/hierarchies");
1442 }
1443 else {
1444 BOOST_FAIL("Wrong vertex type found");
1445 }
1446 };
1447
1448 app.getModel().visit(checker, ChimeraTK::Model::ownedModuleGroups);
1449
1450 BOOST_TEST(foundElements == 1);
1451 }
1452 }
1453
1454 /********************************************************************************************************************/
1455
1456 BOOST_AUTO_TEST_CASE(testOwnedApplicationModules) {
1457 TestApplication app;
1458
1459 {
1460 size_t foundElements = 0;
1461
1462 auto checker = [&](auto proxy) {
1463 ++foundElements;
1464 if constexpr(ChimeraTK::Model::isApplicationModule(proxy)) {
1465 BOOST_CHECK(proxy.getName() == ".");
1466 }
1467 else {
1468 BOOST_FAIL("Wrong vertex type found");
1469 }
1470 };
1471
1472 app.deeperHierarchies.getModel().visit(checker, ChimeraTK::Model::ownedApplicationModules);
1473
1474 BOOST_TEST(foundElements == 1);
1475 }
1476 }
1477
1478 /********************************************************************************************************************/
1479
1480 BOOST_AUTO_TEST_CASE(testOwnedVariableGroups) {
1481 TestApplication app;
1482
1483 {
1484 size_t foundElements = 0;
1485
1486 auto checker = [&](auto proxy) {
1487 ++foundElements;
1488 if constexpr(ChimeraTK::Model::isVariableGroup(proxy)) {
1489 BOOST_CHECK(proxy.getName() == "need");
1490 }
1491 else {
1492 BOOST_FAIL("Wrong vertex type found");
1493 }
1494 };
1495
1496 app.deeperHierarchies.testModule.getModel().visit(checker, ChimeraTK::Model::ownedVariableGroups);
1497
1498 BOOST_TEST(foundElements == 1);
1499 }
1500 }
1501
1502 /********************************************************************************************************************/
1503
1504 BOOST_AUTO_TEST_CASE(testOwnedVariables) {
1505 TestApplication app;
1506
1507 {
1508 size_t foundElements = 0;
1509
1510 auto checker = [&](auto proxy) {
1511 ++foundElements;
1512 if constexpr(ChimeraTK::Model::isVariable(proxy)) {
1513 BOOST_CHECK(proxy.getName() == "also");
1514 }
1515 else {
1516 BOOST_FAIL("Wrong vertex type found");
1517 }
1518 };
1519
1520 app.deeperHierarchies.testModule.getModel().visit(checker, ChimeraTK::Model::ownedVariables);
1521
1522 BOOST_TEST(foundElements == 1);
1523 }
1524 }
1525
1526 /********************************************************************************************************************/
1527
1528 BOOST_AUTO_TEST_CASE(testChildDirectories) {
1529 TestApplication app;
1530
1531 {
1532 size_t foundElements = 0;
1533
1534 auto checker = [&](auto proxy) {
1535 ++foundElements;
1536 if constexpr(ChimeraTK::Model::isDirectory(proxy)) {
1537 BOOST_CHECK(proxy.getName() == "Deeper" || proxy.getName() == "MyModule" || proxy.getName() == "somepath" ||
1538 proxy.getName() == "Devices");
1539 }
1540 else {
1541 BOOST_FAIL("Wrong vertex type found");
1542 }
1543 };
1544
1545 app.getModel().visit(checker, ChimeraTK::Model::childDirectories);
1546
1547 BOOST_TEST(foundElements == 4);
1548 }
1549 }
1550
1551 /********************************************************************************************************************/
1552
1553 BOOST_AUTO_TEST_CASE(testChildVariables) {
1554 TestApplication app;
1555
1556 {
1557 // get the directory. this relies on some other features...
1559 [[maybe_unused]] auto found = app.getModel().visitByPath("/MyModule", [&](auto proxy) {
1560 if constexpr(ChimeraTK::Model::isDirectory(proxy)) {
1561 dir = proxy;
1562 }
1563 });
1564 assert(found);
1565
1566 size_t foundElements = 0;
1567
1568 auto checker = [&](auto proxy) {
1569 ++foundElements;
1570 if constexpr(ChimeraTK::Model::isVariable(proxy)) {
1571 BOOST_CHECK(proxy.getName() == "readBack" || proxy.getName() == "actuator");
1572 }
1573 else {
1574 BOOST_FAIL("Wrong vertex type found");
1575 }
1576 };
1577
1578 dir.visit(checker, ChimeraTK::Model::childVariables);
1579
1580 BOOST_TEST(foundElements == 2);
1581 }
1582 }
1583
1584 /********************************************************************************************************************/
1585
1586 BOOST_AUTO_TEST_CASE(testChildren) {
1587 TestApplication app;
1588
1589 {
1590 // get the directory. this relies on some other features...
1592 [[maybe_unused]] auto found = app.getModel().visitByPath("/MyModule", [&](auto proxy) {
1593 if constexpr(ChimeraTK::Model::isDirectory(proxy)) {
1594 dir = proxy;
1595 }
1596 });
1597 assert(found);
1598
1599 size_t foundElements = 0;
1600
1601 auto checker = [&](auto proxy) {
1602 ++foundElements;
1603 if constexpr(ChimeraTK::Model::isVariable(proxy)) {
1604 BOOST_CHECK(proxy.getName() == "readBack" || proxy.getName() == "actuator");
1605 }
1606 else if constexpr(ChimeraTK::Model::isDirectory(proxy)) {
1607 BOOST_CHECK(proxy.getName() == "pointlessVariableGroup");
1608 }
1609 else {
1610 BOOST_FAIL("Wrong vertex type found");
1611 }
1612 };
1613
1614 dir.visit(checker, ChimeraTK::Model::children);
1615
1616 BOOST_TEST(foundElements == 3);
1617 }
1618 }
1619
1620 /********************************************************************************************************************/
1621
1622 BOOST_AUTO_TEST_CASE(testGetOwner) {
1623 TestApplication app;
1624
1625 {
1626 size_t foundElements = 0;
1627
1628 auto checker = [&](auto proxy) {
1629 ++foundElements;
1630 if constexpr(ChimeraTK::Model::isModuleGroup(proxy)) {
1631 BOOST_CHECK(proxy.getName() == "Deeper/hierarchies");
1632 }
1633 else {
1634 BOOST_FAIL("Wrong vertex type found");
1635 }
1636 };
1637
1638 app.deeperHierarchies.testModule.getModel().visit(checker, ChimeraTK::Model::getOwner);
1639
1640 BOOST_TEST(foundElements == 1);
1641 }
1642 }
1643
1644 /********************************************************************************************************************/
1645
1646 BOOST_AUTO_TEST_CASE(testGetParent) {
1647 TestApplication app;
1648
1649 {
1650 size_t foundElements = 0;
1651
1652 auto checker = [&](auto proxy) {
1653 ++foundElements;
1654 if constexpr(ChimeraTK::Model::isDirectory(proxy)) {
1655 BOOST_CHECK(proxy.getName() == "need");
1656 }
1657 else {
1658 BOOST_FAIL("Wrong vertex type found");
1659 }
1660 };
1661
1662 app.deeperHierarchies.testModule.need.tests.getModel().visit(checker, ChimeraTK::Model::getParent);
1663
1664 BOOST_TEST(foundElements == 1);
1665 }
1666 }
1667
1668 /********************************************************************************************************************/
1669
1670 BOOST_AUTO_TEST_CASE(testGetNeighbourDirectory) {
1671 TestApplication app;
1672
1673 {
1674 size_t foundElements = 0;
1675
1676 auto checker = [&](auto proxy) {
1677 ++foundElements;
1678 if constexpr(ChimeraTK::Model::isDirectory(proxy)) {
1679 BOOST_CHECK(proxy.getName() == "hierarchies");
1680 }
1681 else {
1682 BOOST_FAIL("Wrong vertex type found");
1683 }
1684 };
1685
1686 app.deeperHierarchies.testModule.getModel().visit(checker, ChimeraTK::Model::getNeighbourDirectory);
1687
1688 BOOST_TEST(foundElements == 1);
1689 }
1690 }
1691
1692 /********************************************************************************************************************/
1693
1694 BOOST_AUTO_TEST_CASE(testNeighbourModules) {
1695 TestApplication app;
1696
1697 {
1698 // get the directory. this relies on some other features...
1700 [[maybe_unused]] auto found = app.getModel().visitByPath("/Deeper/hierarchies", [&](auto proxy) {
1701 if constexpr(ChimeraTK::Model::isDirectory(proxy)) {
1702 dir = proxy;
1703 }
1704 });
1705 assert(found);
1706
1707 size_t foundElements = 0;
1708
1709 auto checker = [&](auto proxy) {
1710 ++foundElements;
1711 if constexpr(ChimeraTK::Model::isApplicationModule(proxy)) {
1712 BOOST_CHECK(proxy.getName() == ".");
1713 }
1714 else if constexpr(ChimeraTK::Model::isModuleGroup(proxy)) {
1715 BOOST_CHECK(proxy.getName() == "Deeper/hierarchies");
1716 }
1717 else {
1718 BOOST_FAIL("Wrong vertex type found");
1719 }
1720 };
1721
1722 dir.visit(checker, ChimeraTK::Model::neighbourModules);
1723
1724 BOOST_TEST(foundElements == 2);
1725 }
1726 }
1727
1728 /********************************************************************************************************************/
1729 /* Test pre-defined visitor functors */
1730 /********************************************************************************************************************/
1731
1732 BOOST_AUTO_TEST_CASE(testReturnModuleGroup) {
1733 TestApplication app;
1734
1735 {
1737 app.deeperHierarchies.testModule.getModel().visit(ChimeraTK::Model::returnModuleGroup,
1739
1740 BOOST_TEST(rv.isValid());
1741 BOOST_TEST(rv.getName() == "Deeper/hierarchies");
1742 }
1743 }
1744
1745 /********************************************************************************************************************/
1746
1747 BOOST_AUTO_TEST_CASE(testReturnApplicationModule) {
1748 TestApplication app;
1749
1750 {
1752 app.deeperHierarchies.testModule.need.getModel().visit(ChimeraTK::Model::returnApplicationModule,
1754
1755 BOOST_TEST(rv.isValid());
1756 BOOST_TEST(rv.getName() == ".");
1757 }
1758 }
1759
1760 /********************************************************************************************************************/
1761
1762 BOOST_AUTO_TEST_CASE(testReturnVariableGroup) {
1763 TestApplication app;
1764
1765 {
1767 app.myModule2.pointlessVariableGroup.readBack.getModel().visit(ChimeraTK::Model::returnVariableGroup,
1769
1770 BOOST_TEST(rv.isValid());
1771 BOOST_TEST(rv.getName() == "pointlessVariableGroup");
1772 }
1773 }
1774
1775 /********************************************************************************************************************/
1776
1777 BOOST_AUTO_TEST_CASE(testReturnProcessVariable) {
1778 TestApplication app;
1779
1780 {
1782 ChimeraTK::Model::returnProcessVariable, ChimeraTK::Model::ownedVariables,
1784
1785 BOOST_TEST(rv.isValid());
1786 BOOST_TEST(rv.getName() == "tests");
1787 }
1788 }
1789
1790 /********************************************************************************************************************/
1791
1792 BOOST_AUTO_TEST_CASE(testReturnDirectory) {
1793 TestApplication app;
1794
1795 {
1797 ChimeraTK::Model::returnDirectory, ChimeraTK::Model::getNeighbourDirectory,
1799
1800 BOOST_TEST(rv.isValid());
1801 BOOST_TEST(rv.getName() == "need");
1802 }
1803 }
1804
1805 /********************************************************************************************************************/
1806
1807 BOOST_AUTO_TEST_CASE(testIllegalNames) {
1808 TestApplication app;
1809
1810 constexpr std::string_view illegalCharsToTest = "-~!@#$%^&*()-=+{}|[]\\;':\",.<>?` ";
1811
1812 for(auto c : illegalCharsToTest) {
1813 std::string nameToTest = "MyModule" + std::string(1, c) + "withIllegalChar";
1814 BOOST_CHECK_THROW(
1815 (app.myModule = MyModule(&app, nameToTest, "ApplicationModule directly owned by app")), ctk::logic_error);
1816 }
1817 }
1818
1819 /********************************************************************************************************************/
1820
1822 ctk::ScalarPushInput<int> var{this, "trigger", "", ""};
1823
1824 // This module has a push input and creates a temporary input in its constructor with the same name
1825 // The second one is never used and thrown away immediately. This is the smallest possible reproduction
1826 // for redmine issue 11105
1827 RogueModule(ctk::ModuleGroup* owner, const std::string& name, const std::string& description,
1828 const std::unordered_set<std::string>& tags = {})
1829 : ctk::ApplicationModule(owner, name, description, tags) {
1830 auto v = ctk::ScalarPushInput<int>{this, "trigger", "", ""};
1831 }
1832
1833 void mainLoop() override {}
1834 };
1835
1837 TestApplication2() : Application("testSuite") {}
1839
1840 ~TestApplication2() override { shutdown(); }
1841
1842 RogueModule myModule{this, "MyModule", "ApplicationModule directly owned by app"};
1843 };
1844
1845 /********************************************************************************************************************/
1846
1847 BOOST_AUTO_TEST_CASE(testMassCreationOfUnusedAcecssors) {
1848 TestApplication2 app;
1849 ChimeraTK::Model::ProcessVariableProxy rv = app.myModule.getModel().visit(ChimeraTK::Model::returnProcessVariable,
1851
1852 BOOST_TEST(rv.isValid());
1853 BOOST_TEST(rv.getName() == "trigger");
1854 }
1855
1856 /********************************************************************************************************************/
1857
1858} // namespace Tests::testApplicationPVModel
Model::RootProxy getModel()
Return the root of the application model.
Definition Application.h:75
void shutdown() override
This will remove the global pointer to the instance and allows creating another instance afterwards.
ApplicationModule()=default
Default constructor: Allows late initialisation of modules (e.g.
ChimeraTK::Model::ApplicationModuleProxy getModel()
Return the application model proxy representing this module.
Model::DeviceModuleProxy getModel()
void addTag(const std::string &tag)
Add a tag.
Model::ProcessVariableProxy getModel() const
ApplicationModule & getApplicationModule() const
Return the actual ApplicationModule.
Definition Model.cc:246
const std::string & getName() const
Get the name of the ApplicationModule.
Definition Model.cc:216
ProcessVariableProxy getTrigger() const
Get the ProcessVariableProxy for the trigger.
Definition Model.cc:346
const std::string & getAliasOrCdd() const
Get the alias or CDD of the device.
Definition Model.cc:340
const std::string & getName() const
Get the name of the Directory.
Definition Model.cc:539
const std::string & getName() const
Get the name of the ModuleGroup.
Definition Model.cc:158
ModuleGroup & getModuleGroup() const
Return the actual ModuleGroup.
Definition Model.cc:194
const std::vector< std::shared_ptr< VariableNetworkNode > > & getNodes() const
Return all VariableNetworkNodes for this variable.
Definition Model.cc:381
const std::string & getName() const
Get the name of the ProcessVariable.
Definition Model.cc:375
bool visitByPath(std::string_view path, VISITOR visitor) const
Resolve the given path and call the visitor for the found object.
Definition Model.h:1917
bool isValid() const
Check if the model is valid.
Definition Model.cc:31
auto visit(VISITOR visitor, Args... args) const
Traverse the model using the specified filter and call the visitor functor for each ModuleGroup,...
Definition Model.h:1451
std::string getFullyQualifiedPath() const
Return the fully qualified path.
Definition Model.cc:25
Proxy representing the root of the application model.
Definition Model.h:210
void writeGraphViz(const std::string &filename, Args... args) const
Implementations of RootProxy.
Definition Model.h:1789
bool visitByPath(std::string_view path, VISITOR visitor) const
Resolve the given path and call the visitor for the found object.
Definition Model.h:1895
const std::string & getName() const
Get the name of the VariableGroup.
Definition Model.cc:270
VariableGroup & getVariableGroup() const
Return the actual VariableGroup.
Definition Model.cc:316
friend class Application
Definition ModuleGroup.h:47
ChimeraTK::Model::ModuleGroupProxy getModel()
Return the application model proxy representing this module.
Definition ModuleGroup.h:40
Convenience class for input scalar accessors with UpdateMode::push.
Helper class to set the DMAP file path.
ChimeraTK::Model::VariableGroupProxy getModel()
Return the application model proxy representing this module.
constexpr ReturnFirstHitWithValue< void > returnFirstHit()
Stop the search after the first hit and return.
Definition Model.h:790
constexpr bool isDirectory(const PROPERTY_OR_PROXY &)
Definition Model.h:669
constexpr bool isVariableGroup(const PROPERTY_OR_PROXY &)
Definition Model.h:645
constexpr bool isApplicationModule(const PROPERTY_OR_PROXY &)
Definition Model.h:637
constexpr bool isDeviceModule(const PROPERTY_OR_PROXY &)
Definition Model.h:653
constexpr bool isModuleGroup(const PROPERTY_OR_PROXY &)
Definition Model.h:629
constexpr bool hasName(const PROPERTY_OR_PROXY &)
Definition Model.h:677
constexpr bool isRoot(const PROPERTY_OR_PROXY &)
Predicates to identify the type of a proxy or a properties struct.
Definition Model.h:621
constexpr bool isVariable(const PROPERTY_OR_PROXY &)
Definition Model.h:661
InvalidityTracer application module.
BOOST_AUTO_TEST_CASE(testGraphViz)
Definition testModel.cc:82
This is used to allow default construction of the std::variant.
Definition Model.h:523
Convenience class for output scalar accessors (always UpdateMode::push)
Convenience class for input scalar accessors with UpdateMode::poll.
bool operator<(const Item &rhs) const
Definition testModel.cc:540
const std::type_info & type
Definition testModel.cc:537
MyModule(ctk::ModuleGroup *owner, const std::string &name, const std::string &description, const std::unordered_set< std::string > &tags={})
Definition testModel.cc:23
ctk::ScalarOutput< int > actuator
Definition testModel.cc:31
Tests::testApplicationPVModel::MyModule::PointlessVariableGroup pointlessVariableGroup
void mainLoop() override
To be implemented by the user: function called in a separate thread executing the main loop of the mo...
Definition testModel.cc:38
void mainLoop() override
To be implemented by the user: function called in a separate thread executing the main loop of the mo...
RogueModule(ctk::ModuleGroup *owner, const std::string &name, const std::string &description, const std::unordered_set< std::string > &tags={})
void mainLoop() override
To be implemented by the user: function called in a separate thread executing the main loop of the mo...
Definition testModel.cc:53
Tests::testApplicationPVModel::TestModule::Need need
ctk::ScalarPushInput< int > also
Definition testModel.cc:46