3#include <ChimeraTK/Device.h>
5#include <boost/smart_ptr/make_shared_object.hpp>
6#include <boost/thread/exceptions.hpp>
8#define BOOST_TEST_MODULE testRecoveryGroups
17#include <ChimeraTK/BackendFactory.h>
18#include <ChimeraTK/cppext/finally.hpp>
19#include <ChimeraTK/Exception.h>
20#include <ChimeraTK/ExceptionDummyBackend.h>
21#include <ChimeraTK/LogicalNameMappingBackend.h>
22#include <ChimeraTK/NDRegisterAccessor.h>
23#include <ChimeraTK/ScalarRegisterAccessor.h>
24#include <ChimeraTK/VoidRegisterAccessor.h>
28#include <boost/smart_ptr/shared_ptr.hpp>
29#include <boost/test/included/unit_test.hpp>
45 using ExceptionDummy::ExceptionDummy;
52 void write(uint64_t bar, uint64_t address, int32_t
const* data,
size_t sizeInBytes)
override {
57 throw boost::thread_interrupted();
61 ExceptionDummy::write(bar, address, data, sizeInBytes);
65 static boost::shared_ptr<DeviceBackend>
creatorFunction(std::string, std::map<std::string, std::string> parameters) {
66 return boost::make_shared<WriteBlockingDummy>(parameters[
"map"]);
80 using LogicalNameMappingBackend::LogicalNameMappingBackend;
101 throw boost::thread_interrupted();
104 throw ctk::runtime_error(
"Not ready to block yet");
115 ChimeraTK::LogicalNameMappingBackend::open();
119 static boost::shared_ptr<DeviceBackend>
creatorFunction(std::string, std::map<std::string, std::string> parameters) {
120 auto ptr = boost::make_shared<OpenCountingLmapBackend>(parameters[
"map"]);
121 parameters.erase(parameters.find(
"map"));
122 ptr->_parameters = parameters;
123 return boost::static_pointer_cast<DeviceBackend>(ptr);
128 ctk::BackendFactory::getInstance().registerBackendType(
183 for(
auto const* dev : {
"Use1",
"Use2",
"Use3",
"Use12"}) {
184 CHECK_TIMEOUT(testFacility.readScalar<
int>(std::string(
"Devices/") + dev +
"/status") == 0, 10000);
188 auto dummy1 = boost::dynamic_pointer_cast<ctk::ExceptionDummy>(raw1.getBackend());
189 dummy1->throwExceptionOpen =
true;
190 dummy1->throwExceptionRead =
true;
196 for(
auto const* dev : {
"Use1",
"Use2",
"Use12"}) {
197 CHECK_TIMEOUT(testFacility.readScalar<
int>(std::string(
"Devices/") + dev +
"/status") == 1, 10000);
201 CHECK_TIMEOUT(testFacility.readScalar<
int>(
"Devices/Use3/status") == 0, 10000);
204 dummy1->throwExceptionOpen =
false;
205 dummy1->throwExceptionRead =
false;
207 for(
auto const* dev : {
"Use1",
"Use2",
"Use12",
"Use3"}) {
208 CHECK_TIMEOUT(testFacility.readScalar<
int>(std::string(
"Devices/") + dev +
"/status") == 0, 10000);
222 auto& dm1 = testApp.singleDev1.dev.getDeviceManager();
223 auto& dm2 = testApp.singleDev2.dev.getDeviceManager();
225 auto ids1 = dm1.getDevice().getInvolvedBackendIDs();
226 auto ids2 = dm2.getDevice().getInvolvedBackendIDs();
227 for(
auto id : ids1) {
228 BOOST_CHECK(!ids2.contains(
id));
249 for(
auto const* dev : {
"Use1",
"Use2",
"Use12"}) {
250 CHECK_TIMEOUT(testFacility.readScalar<
int>(std::string(
"Devices/") + dev +
"/status") == 0, 10000);
252 auto testLmap1 = boost::dynamic_pointer_cast<OpenCountingLmapBackend>(
253 testApp.singleDev1.dev.getDeviceManager().getDevice().getBackend());
254 testLmap1->blockOpen =
true;
256 testApp.singleDev1.dev.reportException(
"reported from TestDetectBarrier");
258 testLmap1->blockOpenArrivedBarrier.arrive_and_wait();
263 for(
auto const* dev : {
"Use1",
"Use2",
"Use12"}) {
265 BOOST_TEST(testFacility.readScalar<
int>(std::string(
"Devices/") + dev +
"/status") == 1);
269 (void)testLmap1->blockOpenContinueBarrier.arrive();
270 for(
auto const* dev : {
"Use1",
"Use2",
"Use12"}) {
271 CHECK_TIMEOUT(testFacility.readScalar<
int>(std::string(
"Devices/") + dev +
"/status") == 0, 10000);
284 for(
auto const* dev : {
"Use1",
"Use2",
"Use12"}) {
285 CHECK_TIMEOUT(testFacility.readScalar<
int>(std::string(
"Devices/") + dev +
"/status") == 0, 10000);
288 raw2.write<int32_t>(
"/MyModule/actuator", 16);
291 auto dummy1 = boost::dynamic_pointer_cast<ctk::ExceptionDummy>(raw1.getBackend());
292 dummy1->throwExceptionOpen =
true;
293 dummy1->throwExceptionRead =
true;
297 for(
auto const* dev : {
"Use1",
"Use2"}) {
298 CHECK_TIMEOUT(testFacility.readScalar<
int>(std::string(
"Devices/") + dev +
"/status") == 1, 10000);
306 BOOST_TEST(raw2.read<int32_t>(
"MyModule/actuator") == 16);
309 dummy1->throwExceptionOpen =
false;
310 dummy1->throwExceptionRead =
false;
312 for(
auto const* dev : {
"Use1",
"Use2"}) {
313 CHECK_TIMEOUT(testFacility.readScalar<
int>(std::string(
"Devices/") + dev +
"/status") == 0, 10000);
331 void init1(
const std::string& device) {
344 d.write(
"/MyModule/actuator", 1);
356 for(
auto const* dev : {
"Use1",
"Use2",
"Use12"}) {
357 CHECK_TIMEOUT(testFacility.readScalar<
int>(std::string(
"Devices/") + dev +
"/status") == 0, 10000);
361 testFacility.writeScalar<uint32_t>(
"/Use1/Integers/unsigned32", 17);
362 testFacility.writeScalar<uint32_t>(
"/Use2/Integers/unsigned32", 18);
365 CHECK_TIMEOUT(raw1.read<uint32_t>(
"/Integers/unsigned32") == 17, 10000);
366 CHECK_TIMEOUT(raw2.read<uint32_t>(
"/Integers/unsigned32") == 18, 10000);
367 raw1.write<uint32_t>(
"/Integers/unsigned32", 13);
368 raw2.write<uint32_t>(
"/Integers/unsigned32", 14);
371 testApp.blockInit =
true;
373 auto _ = cppext::finally([&]() { testApp.blockInit =
false; });
375 auto dummy1 = boost::dynamic_pointer_cast<ctk::ExceptionDummy>(raw1.getBackend());
376 dummy1->throwExceptionOpen =
true;
377 dummy1->throwExceptionRead =
true;
381 for(
auto const* dev : {
"Use1",
"Use2"}) {
382 CHECK_TIMEOUT(testFacility.readScalar<
int>(std::string(
"Devices/") + dev +
"/status") == 1, 10000);
386 dummy1->throwExceptionOpen =
false;
387 dummy1->throwExceptionRead =
false;
390 testApp.arrivedInInitHandler.arrive_and_wait();
391 assert(testApp.initCounter == 2);
398 BOOST_TEST(raw1.read<int32_t>(
"Integers/unsigned32") == 13);
399 BOOST_TEST(raw2.read<int32_t>(
"Integers/unsigned32") == 14);
402 testApp.blockInit =
false;
404 for(
auto const* dev : {
"Use1",
"Use2"}) {
405 CHECK_TIMEOUT(testFacility.readScalar<
int>(std::string(
"Devices/") + dev +
"/status") == 0, 10000);
437 throw ctk::runtime_error(
"Intentional failure in init()");
460 for(
auto const* dev : {
"Use1",
"Use2",
"Use12"}) {
461 CHECK_TIMEOUT(testFacility.readScalar<
int>(std::string(
"Devices/") + dev +
"/status") == 0, 10000);
466 testFacility.writeScalar<uint32_t>(
"/Use1/Integers/unsigned32", 17);
467 testFacility.writeScalar<uint32_t>(
"/Use2/Integers/unsigned32", 18);
468 testFacility.writeScalar<int16_t>(
"/Use12/Integers/signed16", 19);
470 CHECK_TIMEOUT(raw1.read<uint32_t>(
"/Integers/unsigned32") == 17, 10000);
471 CHECK_TIMEOUT(raw2.read<uint32_t>(
"/Integers/unsigned32") == 18, 10000);
472 CHECK_TIMEOUT(raw1.read<int16_t>(
"/Integers/signed16") == 19, 10000);
473 raw1.write<uint32_t>(
"/Integers/unsigned32", 13);
474 raw2.write<uint32_t>(
"/Integers/unsigned32", 14);
475 raw1.write<int16_t>(
"/Integers/signed16", 15);
478 testApp.failInit =
true;
479 testApp.initSuccessCounter = 0;
480 testApp.singleDev1.dev.reportException(
"reported from TestInitFailure");
482 testApp.aboutToFail.arrive_and_wait();
486 BOOST_CHECK(testApp.initSuccessCounter == 1);
494 auto testLmap1 = boost::dynamic_pointer_cast<OpenCountingLmapBackend>(
495 testApp.singleDev1.dev.getDeviceManager().getDevice().getBackend());
496 auto testLmap2 = boost::dynamic_pointer_cast<OpenCountingLmapBackend>(
497 testApp.singleDev2.dev.getDeviceManager().getDevice().getBackend());
498 auto testLmap12 = boost::dynamic_pointer_cast<OpenCountingLmapBackend>(
499 testApp.mappedDev12.dev.getDeviceManager().getDevice().getBackend());
500 size_t openCount1 = testLmap1->openCounter;
501 size_t openCount2 = testLmap2->openCounter;
502 size_t openCount12 = testLmap12->openCounter;
506 testApp.blockInitOnce =
true;
509 (void)testApp.proceedWithFail.arrive();
513 testApp.blockInitArrivedBarrier.arrive_and_wait();
514 BOOST_TEST(testLmap1->openCounter == openCount1 + 1);
515 BOOST_TEST(testLmap2->openCounter == openCount2 + 1);
516 BOOST_TEST(testLmap12->openCounter == openCount12 + 1);
524 BOOST_TEST(raw1.read<uint32_t>(
"/Integers/unsigned32") == 13);
525 BOOST_TEST(raw2.read<uint32_t>(
"/Integers/unsigned32") == 14);
526 BOOST_TEST(raw1.read<int16_t>(
"/Integers/signed16") == 15);
529 testApp.failInit =
false;
530 (void)testApp.blockInitContinueBarrier.arrive();
531 for(
auto const* dev : {
"Use1",
"Use2"}) {
532 CHECK_TIMEOUT(testFacility.readScalar<
int>(std::string(
"Devices/") + dev +
"/status") == 0, 10000);
564 for(
auto const* dev : {
"Use1",
"Use2",
"Use12ReadOnly"}) {
565 CHECK_TIMEOUT(testFacility.readScalar<
int>(std::string(
"Devices/") + dev +
"/status") == 0, 10000);
568 testFacility.writeScalar<uint32_t>(
"/Use1/Integers/unsigned32", 18);
569 CHECK_TIMEOUT(raw1.read<uint32_t>(
"Integers/unsigned32") == 18, 10000);
571 raw1.write(
"Integers/unsigned32", 0);
574 auto dummy1 = boost::dynamic_pointer_cast<WriteBlockingDummy>(raw1.getBackend());
575 auto dummy2 = boost::dynamic_pointer_cast<WriteBlockingDummy>(raw2.getBackend());
576 dummy2->blockWriteOnce =
true;
577 testApp.singleDev2.dev.reportException(
"reported from TestRecoveryWriteBarrier");
580 dummy2->blockWriteArrivedBarrier.arrive_and_wait();
583 auto lmapDummy1 = boost::dynamic_pointer_cast<OpenCountingLmapBackend>(
584 testApp.singleDev1.dev.getDeviceManager().getDevice().getBackend());
585 size_t openCount1 = lmapDummy1->openCounter;
591 CHECK_TIMEOUT(raw1.read<uint32_t>(
"Integers/unsigned32") == 18, 10000);
592 testApp.singleDev1.dev.reportException(
"This exception should be suppressed.");
593 (void)dummy2->blockWriteContinueBarrier.arrive();
594 for(
auto const* dev : {
"Use1",
"Use2",
"Use12ReadOnly"}) {
595 CHECK_TIMEOUT(testFacility.readScalar<
int>(std::string(
"Devices/") + dev +
"/status") == 0, 10000);
597 BOOST_TEST(lmapDummy1->openCounter == openCount1);
618 for(
auto const* dev : {
"Use1",
"Use2",
"Use12ReadOnly"}) {
619 CHECK_TIMEOUT(testFacility.readScalar<
int>(std::string(
"Devices/") + dev +
"/status") == 0, 10000);
623 testFacility.writeScalar<uint32_t>(
"/Use1/Integers/unsigned32", 18);
624 CHECK_TIMEOUT(raw1.read<uint32_t>(
"Integers/unsigned32") == 18, 10000);
626 raw1.write(
"Integers/unsigned32", 0);
629 auto dummy2 = boost::dynamic_pointer_cast<WriteBlockingDummy>(raw2.getBackend());
630 dummy2->throwExceptionWrite =
true;
631 dummy2->blockWriteOnce =
true;
632 testApp.singleDev2.dev.reportException(
"reported from TestRecoveryWriteFailure");
635 dummy2->blockWriteArrivedBarrier.arrive_and_wait();
636 CHECK_TIMEOUT(raw1.read<uint32_t>(
"Integers/unsigned32") == 18, 10000);
641 auto testLmap1 = boost::dynamic_pointer_cast<OpenCountingLmapBackend>(
642 testApp.singleDev1.dev.getDeviceManager().getDevice().getBackend());
643 auto testLmap2 = boost::dynamic_pointer_cast<OpenCountingLmapBackend>(
644 testApp.singleDev2.dev.getDeviceManager().getDevice().getBackend());
645 auto testLmap12 = boost::dynamic_pointer_cast<OpenCountingLmapBackend>(
646 testApp.mappedDev12.dev.getDeviceManager().getDevice().getBackend());
647 size_t openCount1 = testLmap1->openCounter;
648 size_t openCount2 = testLmap2->openCounter;
649 size_t openCount12 = testLmap12->openCounter;
653 auto pushedSigned32 = testFacility.getScalar<int32_t>(
"/Use1/Integers/pushedSigned32");
654 pushedSigned32.readLatest();
656 BOOST_CHECK(pushedSigned32.dataValidity() == ctk::DataValidity::faulty);
660 dummy2->blockWriteOnce =
true;
661 (void)dummy2->blockWriteContinueBarrier.arrive();
664 dummy2->blockWriteArrivedBarrier.arrive_and_wait();
668 BOOST_TEST(testLmap1->openCounter == openCount1 + 1);
669 BOOST_TEST(testLmap2->openCounter == openCount2 + 1);
670 BOOST_TEST(testLmap12->openCounter == openCount12 + 1);
676 BOOST_CHECK(!pushedSigned32.readNonBlocking());
679 dummy2->throwExceptionWrite =
false;
680 (void)dummy2->blockWriteContinueBarrier.arrive();
681 for(
auto const* dev : {
"Use1",
"Use2",
"Use12ReadOnly"}) {
682 CHECK_TIMEOUT(testFacility.readScalar<
int>(std::string(
"Devices/") + dev +
"/status") == 0, 10000);
702 auto pushed1 = testFacility.getScalar<
int>(std::string(
"/Use1/Integers/pushedSigned32"));
703 testFacility.runApplication();
712 auto testLmap1 = boost::dynamic_pointer_cast<OpenCountingLmapBackend>(
717 for(
auto const* dev : {
"Use1",
"Use2",
"Use12"}) {
718 CHECK_TIMEOUT(testFacility.readScalar<
int>(std::string(
"Devices/") + dev +
"/status") == 0, 10000);
734 testFacility.runApplication();
737 for(
auto const* dev : {
"Use1",
"Use2",
"Use12"}) {
738 CHECK_TIMEOUT(testFacility.readScalar<
int>(std::string(
"Devices/") + dev +
"/status") == 0, 10000);
743 auto testLmap2 = boost::dynamic_pointer_cast<OpenCountingLmapBackend>(
745 testLmap2->throwThreadInterrupted =
true;
749 testLmap2->aboutToThrowArrivedBarrier.arrive_and_wait();
757 (void)testLmap2->aboutToThrowContinueBarrier.arrive();
790 throw boost::thread_interrupted();
813 testFacility.runApplication();
816 for(
auto const* dev : {
"Use1",
"Use2",
"Use12ReadOnly"}) {
817 CHECK_TIMEOUT(testFacility.readScalar<
int>(std::string(
"Devices/") + dev +
"/status") == 0, 10000);
847 testFacility.runApplication();
850 for(
auto const* dev : {
"Use1",
"Use2",
"Use12ReadOnly"}) {
851 CHECK_TIMEOUT(testFacility.readScalar<
int>(std::string(
"Devices/") + dev +
"/status") == 0, 10000);
858 testFacility.writeScalar<uint32_t>(
"/Use1/Integers/unsigned32", 18);
859 CHECK_TIMEOUT(raw1.read<uint32_t>(
"Integers/unsigned32") == 18, 10000);
861 raw1.write(
"Integers/unsigned32", 0);
866 auto dummy2 = boost::dynamic_pointer_cast<WriteBlockingDummy>(raw2.getBackend());
867 dummy2->blockWriteOnce =
true;
868 dummy2->throwThreadInterrupted =
true;
872 dummy2->blockWriteArrivedBarrier.arrive_and_wait();
876 CHECK_TIMEOUT(raw1.read<uint32_t>(
"Integers/unsigned32") == 18, 10000);
880 (void)dummy2->blockWriteContinueBarrier.arrive();
void shutdown() override
This will remove the global pointer to the instance and allows creating another instance afterwards.
Device & getDevice()
Return the underlying ChimeraTK::Device object.
void addInitialisationHandler(std::function< void(ChimeraTK::Device &)> initialisationHandler)
void reportException(std::string errMsg)
Use this function to report an exception.
DeviceManager & getDeviceManager()
Return the corresponding DeviceManager.
ModuleGroup()=default
Default constructor to allow late initialisation of module groups.
Helper class to set the DMAP file path.
Helper class to facilitate tests of applications based on ApplicationCore.
ChimeraTK::VoidRegisterAccessor getVoid(const ChimeraTK::RegisterPath &name) const
Obtain a void process variable from the application, which is published to the control system.
void runApplication() const
Start the application in testable mode.
InvalidityTracer application module.
DeviceModuleWithPath mappedDev12
BasicTestApp(const std::string &name="BasicTestApp")
ctk::SetDMapFilePath path
DeviceModuleWithPath singleDev3
DeviceModuleWithPath singleDev1
DeviceModuleWithPath singleDev2
void init1(const std::string &device)
std::atomic< bool > blockInit
std::atomic< size_t > initCounter
std::barrier arrivedInInitHandler
~BlockInitTestApp() override
DeviceModuleWithPath(ModuleGroup *owner, std::string const &cdd)
ctk::VoidRegisterAccessor trigger
ChimeraTK::TestFacility testFacility
std::barrier aboutToThrow
DeviceModuleWithPath singleDev1
IncompleteRecoveryTestApp()
DeviceModuleWithPath singleDev2
~IncompleteRecoveryTestApp() override
std::atomic< size_t > initCounter
DeviceModuleWithPath mappedDev12
ctk::SetDMapFilePath path
std::atomic< bool > throwInInit
std::barrier blockInitArrivedBarrier
std::atomic< bool > blockInitOnce
std::atomic< bool > failInit
std::barrier proceedWithFail
~InitFailureApp() override
std::barrier blockInitContinueBarrier
std::atomic< size_t > initSuccessCounter
std::atomic< size_t > initCounter
std::barrier aboutToThrowArrivedBarrier
static boost::shared_ptr< DeviceBackend > creatorFunction(std::string, std::map< std::string, std::string > parameters)
std::atomic< size_t > openCounter
static std::atomic< size_t > globalOpenCounter
std::barrier blockOpenContinueBarrier
std::atomic< bool > throwThreadInterrupted
std::atomic< bool > blockOpen
std::barrier blockOpenArrivedBarrier
std::barrier aboutToThrowContinueBarrier
static boost::shared_ptr< DeviceBackend > creatorFunction(std::string, std::map< std::string, std::string > parameters)
void write(uint64_t bar, uint64_t address, int32_t const *data, size_t sizeInBytes) override
std::atomic< bool > blockWriteOnce
std::barrier blockWriteContinueBarrier
std::atomic< bool > throwThreadInterrupted
std::barrier blockWriteArrivedBarrier
~WriteRecoveryTestApp() override
ctk::SetDMapFilePath path
DeviceModuleWithPath mappedDev12
DeviceModuleWithPath singleDev2
DeviceModuleWithPath singleDev1
constexpr std::string_view cdd
#define CHECK_TIMEOUT(condition, maxMilliseconds)
BOOST_FIXTURE_TEST_CASE(TestRecoveryGroups, Fixture< BasicTestApp >)
A.5 DeviceManagers with at least one common involved backend ID (see DeviceBackend::getInvolvedBacken...
BOOST_AUTO_TEST_CASE(TestClearErrorBarrier)
B.3.2.4.1 DeviceManagers wait until all involved DeviceManagers clear their internal error before fla...