ChimeraTK-ApplicationCore 04.06.00
Loading...
Searching...
No Matches
testExceptionHandling.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#define BOOST_TEST_MODULE testExceptionHandling
4
5#include "check_timeout.h"
6#include "DeviceModule.h"
7#include "fixtures.h"
8#include "ScalarAccessor.h"
9
10#include <ChimeraTK/BackendFactory.h>
11#include <ChimeraTK/Device.h>
12#include <ChimeraTK/DummyRegisterAccessor.h>
13#include <ChimeraTK/ExceptionDummyBackend.h>
14#include <ChimeraTK/NDRegisterAccessor.h>
15
16#include <boost/mpl/list.hpp>
17
18#include <chrono>
19#include <cstring>
20#include <future>
21
22// this #include must come last
23#define BOOST_NO_EXCEPTIONS
24#include <boost/test/included/unit_test.hpp>
25#undef BOOST_NO_EXCEPTIONS
26
28
29 using namespace boost::unit_test_framework;
30 namespace ctk = ChimeraTK;
34
35 /*
36 * This test suite checks behavior on a device related runtime error.
37 */
38 BOOST_AUTO_TEST_SUITE(runtimeErrorHandling)
39
40 /********************************************************************************************************************/
47 std::cout << "B_2_1 - fault indicators" << std::endl;
48
49 // These are instantiated in the fixture:
50 // status -> /Devices/(ExceptionDummy:1?map=test.map)/status
51 // message -> /Devices/(ExceptionDummy:1?map=test.map)/status_message
52
53 BOOST_CHECK_EQUAL(status, 0);
54 BOOST_CHECK_EQUAL(static_cast<std::string>(message), "");
55
56 deviceBackend->throwExceptionOpen = true;
57 deviceBackend->throwExceptionRead = true;
58 application.group1.pollModule.pollInput.read(); // causes device exception
59
60 CHECK_TIMEOUT(status.readNonBlocking() == true, 10000);
61 CHECK_TIMEOUT(message.readNonBlocking() == true, 10000);
62 BOOST_CHECK_EQUAL(status, 1);
63 BOOST_CHECK(!static_cast<std::string>(message).empty());
64
65 deviceBackend->throwExceptionRead = false;
66 deviceBackend->throwExceptionOpen = false;
67
68 CHECK_TIMEOUT(status.readNonBlocking() == true, 10000);
69 CHECK_TIMEOUT(message.readNonBlocking() == true, 10000);
70 BOOST_CHECK_EQUAL(status, 0);
71 BOOST_CHECK_EQUAL(static_cast<std::string>(message), "");
72 }
73
74 /********************************************************************************************************************/
85 std::cout << "B_2_2_2_poll - exception with previous DataValidity::faulty" << std::endl;
86
87 // initialize to known value in deviceBackend register
88 write(exceptionDummyRegister, 134);
89 pollVariable.read();
90 BOOST_REQUIRE_EQUAL(pollVariable, 134);
91 auto versionNumberBeforeRuntimeError = pollVariable.getVersionNumber();
92
93 // Modify the validity flag of the application buffer. Note: This is not a 100% sane test, since in theory it could
94 // make a difference whether the flag is actually coming from the device, but implementing such test is tedious. It
95 // does not seem worth the effort, as it is unlikely that even a future, refactored implementation would be
96 // sensitive to this difference (flag would need to be stored artifically in an additional place). It is only
97 // important to change the validity on all decorator levels.
98 pollVariable.setDataValidity(ctk::DataValidity::faulty);
99 for(auto& e : pollVariable.getHardwareAccessingElements()) {
100 e->setDataValidity(ctk::DataValidity::faulty);
101 }
102
103 // modify value in register after breaking the device
104 deviceBackend->throwExceptionOpen = true;
105 deviceBackend->throwExceptionRead = true;
106 write(exceptionDummyRegister, 10);
107
108 // This read should be skipped but obtain a new version number
109 pollVariable.read();
110 auto versionNumberOnRuntimeError = pollVariable.getVersionNumber();
111 BOOST_CHECK_EQUAL(pollVariable, 134);
112 BOOST_CHECK(pollVariable.dataValidity() == ctk::DataValidity::faulty);
113 BOOST_CHECK(versionNumberOnRuntimeError > versionNumberBeforeRuntimeError);
114 }
115
116 /********************************************************************************************************************/
127 std::cout << "B_2_2_2_push - exception with previous DataValidity::faulty" << std::endl;
128
129 // verify normal operation
130 // initialize to known value in deviceBackend register
131 write(exceptionDummyRegister, 101);
132 ctk::VersionNumber versionNumberBeforeRuntimeError = {};
133 deviceBackend->triggerInterrupt(1);
134 pushVariable.read();
135
136 // Modify the validity flag of the application buffer (see note above in poll-type test)
137 pushVariable.setDataValidity(ctk::DataValidity::faulty);
138 for(auto& e : pushVariable.getHardwareAccessingElements()) {
139 e->setDataValidity(ctk::DataValidity::faulty);
140 }
141
142 // modify value in register after breaking the device
143 deviceBackend->throwExceptionOpen = true;
144 deviceBackend->throwExceptionRead = true;
145 write(exceptionDummyRegister, 11);
146 deviceBackend->triggerInterrupt(1);
147
148 // This read should be skipped but obtain a new version number
149 pushVariable.read();
150 auto versionNumberOnRuntimeError = pushVariable.getVersionNumber();
151 BOOST_CHECK_EQUAL(pushVariable, 101);
152 BOOST_CHECK(pushVariable.dataValidity() == ctk::DataValidity::faulty);
153 BOOST_CHECK(versionNumberOnRuntimeError > versionNumberBeforeRuntimeError);
154 }
155
156 /********************************************************************************************************************/
166 std::cout << "B_2_2_3 - skip poll type reads" << std::endl;
167
168 // initialize to known value in deviceBackend register
169 write(exceptionDummyRegister, 100);
170 pollVariable.read();
171 auto versionNumberBeforeRuntimeError = pollVariable.getVersionNumber();
172
173 // modify value in register after breaking the device
174 deviceBackend->throwExceptionOpen = true;
175 deviceBackend->throwExceptionRead = true;
176 write(exceptionDummyRegister, 10);
177
178 // This read should be skipped but obtain a new version number
179 pollVariable.read();
180 auto versionNumberOnRuntimeError = pollVariable.getVersionNumber();
181 BOOST_CHECK_EQUAL(pollVariable, 100);
182 BOOST_CHECK(pollVariable.dataValidity() == ctk::DataValidity::faulty);
183 BOOST_CHECK(versionNumberOnRuntimeError > versionNumberBeforeRuntimeError);
184
185 // This read should be skipped too, this time without a new version number
186 pollVariable.read();
187 BOOST_CHECK_EQUAL(pollVariable, 100);
188 BOOST_CHECK(pollVariable.dataValidity() == ctk::DataValidity::faulty);
189 BOOST_CHECK_EQUAL(pollVariable.getVersionNumber(), versionNumberOnRuntimeError);
190 }
191
192 /********************************************************************************************************************/
202 BOOST_FIXTURE_TEST_CASE(B_2_2_3_TriggerFanOut, Fixture) {
203 std::cout << "B_2_2_3_TriggerFanOut - skip poll type reads (in TriggerFanOut)" << std::endl;
204
205 // initialize to known value in deviceBackend register
206 triggeredInput.readLatest(); // empty queue (initial value)
207
208 write(exceptionDummy2Register, 666);
209 deviceBackend3->triggerInterrupt(1);
210 triggeredInput.read();
211 BOOST_REQUIRE_EQUAL(triggeredInput, 666);
212
213 // breaking the device and modify value
214 deviceBackend2->throwExceptionOpen = true;
215 deviceBackend2->throwExceptionRead = true;
216 write(exceptionDummy2Register, 667);
217
218 // Trigger readout of poll-type inside TriggerFanOut (should be skipped - VersionNumber is invisible in this context)
219 deviceBackend3->triggerInterrupt(1);
220 triggeredInput.read();
221 BOOST_CHECK_EQUAL(triggeredInput, 666);
222 BOOST_CHECK(triggeredInput.dataValidity() == ctk::DataValidity::faulty);
223
224 // A second read should be skipped, too
225 deviceBackend3->triggerInterrupt(1);
226 triggeredInput.read();
227 BOOST_CHECK_EQUAL(triggeredInput, 666);
228 BOOST_CHECK(triggeredInput.dataValidity() == ctk::DataValidity::faulty);
229 }
230
231 /********************************************************************************************************************/
241 BOOST_FIXTURE_TEST_CASE(B_2_2_4_blocking, Fixture) {
242 std::cout << "B_2_2_4_blocking - first skip of blocking read" << std::endl;
243
244 pushVariable.readLatest();
245
246 // go to exception state
247 ctk::VersionNumber version = {};
248 deviceBackend->throwExceptionOpen = true;
249 deviceBackend->throwExceptionRead = true;
250 write(exceptionDummyRegister, 456);
251 deviceBackend->triggerInterrupt(1);
252
253 // as soon as the fault state has arrived, the operation is skipped
254 pushVariable.read();
255 BOOST_CHECK_NE(pushVariable, 456); // value did not come through
256 BOOST_CHECK(pushVariable.dataValidity() == ctk::DataValidity::faulty);
257 auto versionNumberOnRuntimeError = pushVariable.getVersionNumber();
258 BOOST_CHECK(versionNumberOnRuntimeError > version);
259 }
260
261 /********************************************************************************************************************/
271 BOOST_FIXTURE_TEST_CASE(B_2_2_4_nonBlocking, Fixture) {
272 std::cout << "B_2_2_4_nonBlocking - first skip of readNonBlocking" << std::endl;
273
274 pushVariable.readLatest();
275
276 // go to exception state
277 ctk::VersionNumber version = {};
278 deviceBackend->throwExceptionOpen = true;
279 deviceBackend->throwExceptionRead = true;
280 write(exceptionDummyRegister, 123);
281 deviceBackend->triggerInterrupt(1);
282
283 // as soon as the fault state has arrived, the operation is skipped
284 CHECK_TIMEOUT(pushVariable.readNonBlocking() == true, 10000);
285 BOOST_CHECK_NE(pushVariable, 123); // value did not come through
286 BOOST_CHECK(pushVariable.dataValidity() == ctk::DataValidity::faulty);
287 auto versionNumberOnRuntimeError = pushVariable.getVersionNumber();
288 BOOST_CHECK(versionNumberOnRuntimeError > version);
289 }
290
291 /********************************************************************************************************************/
302 std::cout << "B_2_2_4_any - first skip of readAny" << std::endl;
303
304 pushVariable.readLatest();
305
306 ChimeraTK::ReadAnyGroup group({pushVariable});
307
308 // go to exception state
309 ctk::VersionNumber version = {};
310 deviceBackend->throwExceptionOpen = true;
311 deviceBackend->throwExceptionRead = true;
312 write(exceptionDummyRegister, 456);
313 deviceBackend->triggerInterrupt(1);
314
315 // as soon as the fault state has arrived, the operation is skipped
316 group.readAny();
317 BOOST_CHECK_NE(pushVariable, 456); // value did not come through
318 BOOST_CHECK(pushVariable.dataValidity() == ctk::DataValidity::faulty);
319 auto versionNumberOnRuntimeError = pushVariable.getVersionNumber();
320 BOOST_CHECK(versionNumberOnRuntimeError > version);
321 }
322
323 /********************************************************************************************************************/
334 std::cout << "B_2_2_4_latest - first skip of readLatest" << std::endl;
335
336 pushVariable.readLatest();
337
338 // go to exception state
339 ctk::VersionNumber version = {};
340 deviceBackend->throwExceptionOpen = true;
341 deviceBackend->throwExceptionRead = true;
342 write(exceptionDummyRegister, 234);
343 deviceBackend->triggerInterrupt(1);
344
345 // as soon as the fault state has arrived, the operation is skipped
346 CHECK_TIMEOUT(pushVariable.readLatest() == true, 10000);
347 BOOST_CHECK_NE(pushVariable, 234); // value did not come through
348 BOOST_CHECK(pushVariable.dataValidity() == ctk::DataValidity::faulty);
349 auto versionNumberOnRuntimeError = pushVariable.getVersionNumber();
350 BOOST_CHECK(versionNumberOnRuntimeError > version);
351 }
352
353 /********************************************************************************************************************/
364 std::cout << "B_2_2_4_ThFO - first skip read in ThreadedFanOut" << std::endl;
365
366 // remove initial value from control system
367 pushVariable3copy.readLatest();
368 pushVariable3.readLatest();
369
370 // go to exception state
371 ctk::VersionNumber version = {};
372 deviceBackend2->throwExceptionOpen = true;
373 deviceBackend2->throwExceptionRead = true;
374 write(exceptionDummy2Register, 345);
375 deviceBackend2->triggerInterrupt(1);
376
377 // as soon as the fault state has arrived, the operation is skipped
378 pushVariable3.read();
379 BOOST_CHECK_NE(pushVariable3, 345); // value did not come through
380 BOOST_CHECK(pushVariable3.dataValidity() == ctk::DataValidity::faulty);
381 BOOST_CHECK(pushVariable3.getVersionNumber() > version);
382
383 // same state is visible at control system's copy
384 pushVariable3copy.read();
385 BOOST_CHECK_NE(pushVariable3copy, 345); // value did not come through
386 BOOST_CHECK(pushVariable3copy.dataValidity() == ctk::DataValidity::faulty);
387 BOOST_CHECK(pushVariable3copy.getVersionNumber() > version);
388 }
389
390 /********************************************************************************************************************/
401 std::cout << "B_2_2_4_TrFO - first skip read in TriggerFanOut on the trigger variable" << std::endl;
402
403 triggeredInput.readLatest();
404
405 // initialize to known value in deviceBackend register
406 write(exceptionDummy2Register, 668);
407 ctk::VersionNumber versionBeforeException = {};
408 deviceBackend3->triggerInterrupt(1);
409 triggeredInput.read();
410
411 // breaking the device and modify value
412 deviceBackend3->throwExceptionOpen = true;
413 deviceBackend3->throwExceptionRead = true;
414 write(exceptionDummy2Register, 669);
415 pollVariable3.read(); // make sure framework sees exception
416
417 // as soon as the fault state has arrived, the operation is skipped (inside the TriggerFanOut), so we get the
418 // updated value (remember: the updated value comes from another device which is not broken)
419 triggeredInput.read();
420 BOOST_CHECK_EQUAL(triggeredInput, 669);
421 BOOST_CHECK(triggeredInput.dataValidity() == ctk::DataValidity::faulty);
422 BOOST_CHECK(triggeredInput.getVersionNumber() > versionBeforeException);
423 }
424
425 /********************************************************************************************************************/
434 BOOST_FIXTURE_TEST_CASE(B_2_2_4_1_nonBlocking, Fixture) {
435 std::cout << "B_2_2_4_1_nonBlocking - following skip readNonBlocking" << std::endl;
436
437 pushVariable.readLatest();
438
439 // go to exception state
440 deviceBackend->throwExceptionOpen = true;
441 deviceBackend->throwExceptionRead = true;
442 write(exceptionDummyRegister, 100);
443 deviceBackend->triggerInterrupt(1);
444
445 // perform first skipped operation
446 pushVariable.read();
447 auto versionNumberOnRuntimeError = pushVariable.getVersionNumber();
448
449 // subsequent calls to readLatest on runtime error are skipped.
450 for(size_t i = 0; i < 5; ++i) {
451 usleep(1000);
452 BOOST_CHECK_EQUAL(pushVariable.readNonBlocking(), false);
453 BOOST_CHECK(versionNumberOnRuntimeError == pushVariable.getVersionNumber());
454 BOOST_CHECK(pushVariable.dataValidity() == ctk::DataValidity::faulty);
455 }
456 }
457
458 /********************************************************************************************************************/
467 BOOST_FIXTURE_TEST_CASE(B_2_2_4_1_latest, Fixture) {
468 std::cout << "B_2_2_4_1_latest - following skip readLatest" << std::endl;
469
470 pushVariable.readLatest();
471
472 // go to exception state
473 deviceBackend->throwExceptionOpen = true;
474 deviceBackend->throwExceptionRead = true;
475 write(exceptionDummyRegister, 100);
476 deviceBackend->triggerInterrupt(1);
477
478 // perform first skipped operation
479 pushVariable.read();
480 auto versionNumberOnRuntimeError = pushVariable.getVersionNumber();
481
482 // subsequent calls to readLatest on runtime error are skipped.
483 for(size_t i = 0; i < 5; ++i) {
484 usleep(1000);
485 BOOST_CHECK_EQUAL(pushVariable.readLatest(), false);
486 BOOST_CHECK(versionNumberOnRuntimeError == pushVariable.getVersionNumber());
487 BOOST_CHECK(pushVariable.dataValidity() == ctk::DataValidity::faulty);
488 }
489 }
490
491 /********************************************************************************************************************/
499 std::cout << "B_2_2_4_2 - freeze blocking read" << std::endl;
500
501 pushVariable.readLatest();
502
503 // go to exception state
504 deviceBackend->throwExceptionOpen = true;
505 deviceBackend->throwExceptionRead = true;
506 write(exceptionDummyRegister, 100);
507 deviceBackend->triggerInterrupt(1);
508
509 // perform first skipped operation
510 pushVariable.read();
511
512 // subsequent read operations should be frozen
513 deviceBackend->triggerInterrupt(1);
514 auto f = std::async(std::launch::async, [&]() { pushVariable.read(); });
515 BOOST_CHECK(f.wait_for(std::chrono::milliseconds(100)) == std::future_status::timeout);
516
517 // FIXME: This should not be necessary. Bug in ApplicationCore's shutdown procedure!?
518 deviceBackend->throwExceptionRead = false;
519 deviceBackend->throwExceptionOpen = false;
520 }
521
522 /********************************************************************************************************************/
530 std::cout << "B_2_2_4_3 - value after recovery" << std::endl;
531
532 pushVariable.readLatest();
533
534 // Normal behaviour
535 write(exceptionDummyRegister, 66);
536 deviceBackend->triggerInterrupt(1);
537 pushVariable.read();
538
539 // Change value while in exception state
540 deviceBackend->throwExceptionOpen = true;
541 deviceBackend->throwExceptionRead = true;
542 write(exceptionDummyRegister, 77);
543 deviceBackend->triggerInterrupt(1);
544
545 pushVariable.read();
546 BOOST_CHECK_EQUAL(pushVariable, 66);
547
548 // Recover from exception state
549 deviceBackend->throwExceptionRead = false;
550 deviceBackend->throwExceptionOpen = false;
551
552 // Now the value needs to be read
553 CHECK_TIMEOUT(pushVariable.readNonBlocking() == true, 10000);
554 BOOST_CHECK_EQUAL(pushVariable, 77);
555 }
556
557 /********************************************************************************************************************/
565 std::cout << "B_2_2_5 - version numbers across PVs" << std::endl;
566
567 pushVariable.readLatest();
568
569 // Go to exception state, report it explicitly
570 ctk::VersionNumber someVersionBeforeReporting = {};
571 deviceBackend->throwExceptionOpen = true; // required to make sure device stays down
572 application.group1.device.reportException("explicit report by test");
573 ctk::VersionNumber someVersionAfterReporting = {};
574
575 // Check push variable
576 pushVariable.read();
577 auto exceptionVersion = pushVariable.getVersionNumber();
578 BOOST_CHECK(exceptionVersion > someVersionBeforeReporting);
579 BOOST_CHECK(exceptionVersion < someVersionAfterReporting);
580
581 // Check poll variable
582 pollVariable.read();
583 BOOST_CHECK(pollVariable.getVersionNumber() == exceptionVersion);
584 }
585
586 /********************************************************************************************************************/
594 std::cout << "B_2_2_6 - data buffer not updated" << std::endl;
595
596 pushVariable.readLatest();
597
598 // Write both variables once (without error state)
599 write(exceptionDummyRegister, 66);
600 deviceBackend->triggerInterrupt(1);
601 pushVariable.read();
602 BOOST_CHECK_EQUAL(pushVariable, 66);
603
604 write(exceptionDummyRegister, 67);
605 pollVariable.read();
606 BOOST_CHECK_EQUAL(pollVariable, 67);
607
608 // Go to exception state, report it explicitly
609 write(exceptionDummyRegister, 68);
610 deviceBackend->throwExceptionOpen = true;
611 deviceBackend->throwExceptionRead = true;
612 pollVariable.read();
613
614 // Check push variable
615 pushVariable = 42;
616 BOOST_REQUIRE(pushVariable.dataValidity() == ctk::DataValidity::ok);
617 pushVariable.read();
618 BOOST_REQUIRE(pushVariable.dataValidity() == ctk::DataValidity::faulty);
619 BOOST_CHECK_EQUAL(pushVariable, 42);
620
621 // Check poll variable
622 pollVariable = 43;
623 pollVariable.read();
624 BOOST_CHECK_EQUAL(pollVariable, 43);
625 }
626
627 /********************************************************************************************************************/
636 std::cout << "B_2_3_3 - return value of write" << std::endl;
637
638 // trigger runtime error
639 deviceBackend->throwExceptionOpen = true;
640 deviceBackend->throwExceptionRead = true;
641 pollVariable.read();
642
643 // multiple writes on faulty device.
644 outputVariable2 = 100;
645 auto testval = outputVariable2.write();
646 BOOST_CHECK_EQUAL(testval, false); // data not lost
647
648 outputVariable2 = 101;
649 BOOST_CHECK_EQUAL(outputVariable2.write(), true); // data lost
650 }
651
652 /********************************************************************************************************************/
660 std::cout << "B_2_3_5 - write before deviceBecameFunctional" << std::endl;
661
662 // trigger runtime error
663 deviceBackend->throwExceptionOpen = true;
664 deviceBackend->throwExceptionRead = true;
665 pollVariable.read();
666
667 // write on faulty device.
668 outputVariable2 = 987;
669 outputVariable2.write();
670
671 // recover device
672 deviceBackend->throwExceptionRead = false;
673 deviceBackend->throwExceptionOpen = false;
674 deviceBecameFunctional.read();
675
676 // check result (must be immediately present, so don't use CHECK_EQUAL_TIMEOUT!)
677 BOOST_CHECK_EQUAL(exceptionDummyRegister2[0], 987);
678 }
679
680 /********************************************************************************************************************/
688 std::cout << "B_2_5 - isReadable/isWriteable/isReadOnly" << std::endl;
689
690 // trigger runtime error
691 deviceBackend->throwExceptionOpen = true;
692 deviceBackend->throwExceptionRead = true;
693 pollVariable.read();
694
695 // Note: only test what is not anyway clear by the abstractor type. The others need to be implemented by the
696 // abstractor directly.
697 BOOST_CHECK(pollVariable.isReadable());
698
699 BOOST_CHECK(pushVariable.isReadable());
700
701 BOOST_CHECK(outputVariable2.isWriteable());
702 BOOST_CHECK(!outputVariable2.isReadOnly());
703 }
704
705 /********************************************************************************************************************/
719 std::cout << "B_3_2_2 - initialisation handlers" << std::endl;
720
721 // device opened for first time
722 BOOST_CHECK(initHandler1Called);
723 BOOST_CHECK(initHandler2Called);
724 initHandler1Called = false;
725 initHandler2Called = false;
726
727 // trigger runtime error
728 deviceBackend->throwExceptionOpen = true;
729 deviceBackend->throwExceptionRead = true;
730 pollVariable.read();
731
732 // init handlers should not yet be called
733 usleep(10000);
734 BOOST_CHECK(!initHandler1Called);
735 BOOST_CHECK(!initHandler2Called);
736
737 // trigger recovery, but let first init handler throw
738 initHandler1Throws = true;
739 deviceBackend->throwExceptionRead = false;
740 deviceBackend->throwExceptionOpen = false;
741
742 // init handler 1 must be called eventually, but not init handler 2
743 CHECK_TIMEOUT(initHandler1Called, 10000);
744 usleep(10000);
745 BOOST_CHECK(!initHandler2Called);
746
747 // let the first init handler complete, but not the second one
748 initHandler2Throws = true;
749 initHandler1Called = false;
750 initHandler1Throws = false;
751 CHECK_TIMEOUT(initHandler1Called, 10000);
752 CHECK_TIMEOUT(initHandler2Called, 10000);
753 }
754
755 /********************************************************************************************************************/
764 std::cout << "B_3_2_3 - delayed writes" << std::endl;
765
766 // trigger runtime error
767 deviceBackend->throwExceptionOpen = true;
768 deviceBackend->throwExceptionRead = true;
769 pollVariable.read();
771 (status.readNonBlocking(), status == 1), 10000); // no test intended, just wait until error is reported
772
773 // get current write count for each register (as a reference)
774 auto wcReg2 = deviceBackend->getWriteCount("REG2");
775 auto wcReg3 = deviceBackend->getWriteCount("REG3");
776 auto wcRegV = deviceBackend->getWriteCount("REGV");
777
778 // multiple writes to different registers on faulty device
779 outputVariable2 = 801;
780 outputVariable2.write();
781 outputVariable3 = 802;
782 outputVariable3.write();
783 outputVariable2 = 803; // write a second time, overwriting the first value
784 outputVariable2.write();
785 outputVariableV.write(); // write the Void-typed register
786
787 // check that values are not yet written to the device
788 usleep(10000);
789 BOOST_CHECK_NE(exceptionDummyRegister2[0], 803);
790 BOOST_CHECK_NE(exceptionDummyRegister3[0], 802);
791
792 // recover device for reading/opening but not yet for writing
793 initHandler1Called = false;
794 deviceBackend->throwExceptionRead = false;
795 deviceBackend->throwExceptionWrite = true;
796 deviceBackend->throwExceptionOpen = false;
797
798 // wait until the write exception has been thrown
799 deviceBackend->throwExceptionCounter = 0;
800 CHECK_TIMEOUT(deviceBackend->throwExceptionCounter > 0, 10000);
801 BOOST_CHECK_NE(exceptionDummyRegister2[0], 803);
802 BOOST_CHECK_NE(exceptionDummyRegister3[0], 802);
803
804 // check that write attempt has happened after initialisation handlers are called
805 BOOST_CHECK(initHandler1Called);
806
807 // now let write operations complete
808 deviceBackend->throwExceptionWrite = false;
809
810 // check that values finally are written to the device
811 CHECK_EQUAL_TIMEOUT((exceptionDummyRegister2.getBufferLock(), exceptionDummyRegister2[0]), 803, 10000);
812 CHECK_EQUAL_TIMEOUT((exceptionDummyRegister3.getBufferLock(), exceptionDummyRegister3[0]), 802, 10000);
813
814 // check order of writes
815 auto woReg2 = deviceBackend->getWriteOrder("REG2");
816 auto woReg3 = deviceBackend->getWriteOrder("REG3");
817 BOOST_CHECK_GT(woReg2, woReg3);
818
819 // check each register is written only once ("only the latest written value [...] prevails"), except the Void register
820 BOOST_CHECK_EQUAL(deviceBackend->getWriteCount("REG2") - wcReg2, 1);
821 BOOST_CHECK_EQUAL(deviceBackend->getWriteCount("REG3") - wcReg3, 1);
822
823 // The Void-typed register must have not been written.
824 BOOST_CHECK_EQUAL(deviceBackend->getWriteCount("REGV"), wcRegV);
825 }
826
827 /********************************************************************************************************************/
835 std::cout << "B_3_2_5 - reactivate async reads" << std::endl;
836
837 // Test async read after first open
838 BOOST_CHECK(deviceBackend->asyncReadActivated());
839
840 // Cause runtime error
841 deviceBackend->throwExceptionOpen = true;
842 deviceBackend->throwExceptionRead = true;
843 pollVariable.read();
844
845 // Write to register to latest test order of recovery procedure
846 outputVariable2.write();
847
848 // Just to make sure the test is sensitive
849 assert(deviceBackend->asyncReadActivated() == false);
850
851 auto reg2WriteCountBeforeRecovery = deviceBackend->getWriteCount("REG2");
852
853 // Recover from exception state
854 initHandler1Called = false;
855 deviceBackend->throwExceptionRead = false;
856 deviceBackend->throwExceptionOpen = false;
857
858 // Test async read after recovery
859 CHECK_TIMEOUT(deviceBackend->asyncReadActivated(), 10000);
860 BOOST_CHECK_EQUAL(deviceBackend->getWriteCount("REG2"), reg2WriteCountBeforeRecovery + 1);
861 }
862
863 /********************************************************************************************************************/
871 std::cout << "B_3_2_6 - deviceBecameFunctional" << std::endl;
872
873 // (Note: deviceBecameFunctional is read inside the fixture for the first time!)
874 BOOST_CHECK(deviceBackend->asyncReadActivated());
875 BOOST_CHECK(initHandler1Called);
876
877 // Cause runtime error
878 deviceBackend->throwExceptionOpen = true;
879 deviceBackend->throwExceptionRead = true;
880 pollVariable.read();
881
882 // Make sure deviceBecameFunctional is not written at the wrong time
883 usleep(10000);
884 BOOST_CHECK(deviceBecameFunctional.readNonBlocking() == false);
885
886 // Recover from exception state
887 deviceBackend->throwExceptionRead = false;
888 deviceBackend->throwExceptionOpen = false;
889
890 // Check that deviceBecameFunctional is written after recovery
891 CHECK_TIMEOUT(deviceBecameFunctional.readNonBlocking() == true, 10000);
892
893 // Make sure deviceBecameFunctional is not written another time
894 usleep(10000);
895 BOOST_CHECK(deviceBecameFunctional.readNonBlocking() == false);
896 }
897
898 /********************************************************************************************************************/
906 std::cout << "B_4_1 - broken devices don't affect unrelated modules" << std::endl;
907
908 pushVariable.readLatest();
909
910 // verify the 3 ApplicationModules work
911 write(exceptionDummyRegister, 101);
912 deviceBackend->triggerInterrupt(1);
913 pushVariable.read();
914 BOOST_CHECK_EQUAL(pushVariable, 101);
915
916 write(exceptionDummyRegister, 102);
917 pollVariable.read();
918 BOOST_CHECK_EQUAL(pollVariable, 102);
919
920 outputVariable2 = 103;
921 outputVariable2.write();
922 BOOST_CHECK_EQUAL(int(exceptionDummyRegister2), 103);
923
924 // make sure test is effective (device2 is still in error condition)
925 status2.readLatest();
926 assert(status2 == 1);
927 }
928
929 /********************************************************************************************************************/
936 std::cout << "B_5 - Manual exception reporting" << std::endl;
937
938 status.readLatest();
939
940 // Go to exception state, report it explicitly
941 application.group1.device.reportException("explicit report by test");
942
943 // Check that the device went into the error state
944 BOOST_CHECK(status.readAndGet() == 1);
945
946 // As the device itself did not have an error, it recovers immediatelystate
947 BOOST_CHECK(status.readAndGet() == 0);
948 }
949
950 /********************************************************************************************************************/
957 std::cout << "B_5_1 - Manual reporting calls setException()" << std::endl;
958
959 pushVariable.readLatest();
960
961 // Go to exception state, report it explicitly
962 application.group1.device.reportException("explicit report by test");
963
964 // Check push variable. It must see one with invalid data, and as we have not prevented immediate recovery,
965 // see the new initial value.
966 pushVariable.read();
967 BOOST_CHECK(pushVariable.dataValidity() == ChimeraTK::DataValidity::faulty);
968 pushVariable.read();
969 BOOST_CHECK(pushVariable.dataValidity() == ChimeraTK::DataValidity::ok);
970 }
971
972 /********************************************************************************************************************/
973
974 BOOST_AUTO_TEST_SUITE_END()
975
976} // namespace Tests::testExceptionHandling
VersionNumber version
#define CHECK_EQUAL_TIMEOUT(left, right, maxMilliseconds)
InvalidityTracer application module.
BOOST_FIXTURE_TEST_CASE(B_2_1, Fixture)
B.2.1
#define CHECK_TIMEOUT(condition, maxMilliseconds)