ChimeraTK-DeviceAccess  03.18.00
UnifiedBackendTest.h
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 #pragma once
4 
5 #include "Device.h"
6 #include "DeviceBackendImpl.h"
7 
8 #include <boost/fusion/include/at_key.hpp>
9 #include <boost/test/unit_test.hpp>
10 
11 #include <functional>
12 #include <list>
13 #include <numeric>
14 #include <string>
15 #include <thread>
16 #include <utility>
17 
18 // disable shadow warning, boost::mpl::for_each is triggering this warning on Ubuntu 16.04
19 #pragma GCC diagnostic ignored "-Wshadow"
20 
21 namespace ChimeraTK {
22 
26  enum class TestCapability {
27  unspecified,
28  enabled,
29  disabled
30  };
31 
44  template<TestCapability _syncRead = TestCapability::enabled,
45  TestCapability _forceDataLossWrite = TestCapability::unspecified,
46  TestCapability _asyncReadInconsistency = TestCapability::unspecified,
49  TestCapability _writeNeverLosesData = TestCapability::unspecified,
52  TestCapability _testCatalogue = TestCapability::enabled,
53  TestCapability _setRemoteValueIncrementsVersion = TestCapability::enabled>
55  constexpr TestCapabilities() = default;
56 
62  constexpr TestCapabilities<TestCapability::disabled, _forceDataLossWrite, _asyncReadInconsistency, _switchReadOnly,
63  _switchWriteOnly, _writeNeverLosesData, _testWriteOnly, _testReadOnly, _testRawTransfer, _testCatalogue,
64  _setRemoteValueIncrementsVersion>
65  disableSyncRead() const {
66  return {};
67  }
68 
70  constexpr TestCapabilities<_syncRead, TestCapability::enabled, _asyncReadInconsistency, _switchReadOnly,
71  _switchWriteOnly, TestCapability::disabled, _testWriteOnly, _testReadOnly, _testRawTransfer, _testCatalogue,
72  _setRemoteValueIncrementsVersion>
74  static_assert(_writeNeverLosesData != TestCapability::enabled,
75  "enableTestWriteNeverLosesData() and enableForceDataLossWrite() are mutually exclusive.");
76  return {};
77  }
78  constexpr TestCapabilities<_syncRead, TestCapability::disabled, _asyncReadInconsistency, _switchReadOnly,
79  _switchWriteOnly, _writeNeverLosesData, _testWriteOnly, _testReadOnly, _testRawTransfer, _testCatalogue,
80  _setRemoteValueIncrementsVersion>
82  return {};
83  }
84 
86  constexpr TestCapabilities<_syncRead, _forceDataLossWrite, TestCapability::enabled, _switchReadOnly,
87  _switchWriteOnly, _writeNeverLosesData, _testWriteOnly, _testReadOnly, _testRawTransfer, _testCatalogue,
88  _setRemoteValueIncrementsVersion>
90  return {};
91  }
92  constexpr TestCapabilities<_syncRead, _forceDataLossWrite, TestCapability::disabled, _switchReadOnly,
93  _switchWriteOnly, _writeNeverLosesData, _testWriteOnly, _testReadOnly, _testRawTransfer, _testCatalogue,
94  _setRemoteValueIncrementsVersion>
96  return {};
97  }
98 
100  constexpr TestCapabilities<_syncRead, _forceDataLossWrite, _asyncReadInconsistency, TestCapability::enabled,
101  _switchWriteOnly, _writeNeverLosesData, _testWriteOnly, _testReadOnly, _testRawTransfer, _testCatalogue,
102  _setRemoteValueIncrementsVersion>
104  return {};
105  }
106  constexpr TestCapabilities<_syncRead, _forceDataLossWrite, _asyncReadInconsistency, TestCapability::disabled,
107  _switchWriteOnly, _writeNeverLosesData, _testWriteOnly, _testReadOnly, _testRawTransfer, _testCatalogue,
108  _setRemoteValueIncrementsVersion>
110  return {};
111  }
112 
114  constexpr TestCapabilities<_syncRead, _forceDataLossWrite, _asyncReadInconsistency, _switchReadOnly,
115  TestCapability::enabled, _writeNeverLosesData, _testWriteOnly, _testReadOnly, _testRawTransfer, _testCatalogue,
116  _setRemoteValueIncrementsVersion>
118  return {};
119  }
120  constexpr TestCapabilities<_syncRead, _forceDataLossWrite, _asyncReadInconsistency, _switchReadOnly,
121  TestCapability::disabled, _writeNeverLosesData, _testWriteOnly, _testReadOnly, _testRawTransfer, _testCatalogue,
122  _setRemoteValueIncrementsVersion>
124  return {};
125  }
126 
130  constexpr TestCapabilities<_syncRead, TestCapability::disabled, _asyncReadInconsistency, _switchReadOnly,
131  _switchWriteOnly, TestCapability::enabled, _testWriteOnly, _testReadOnly, _testRawTransfer, _testCatalogue,
132  _setRemoteValueIncrementsVersion>
134  static_assert(_forceDataLossWrite != TestCapability::enabled,
135  "enableTestWriteNeverLosesData() and enableForceDataLossWrite() are mutualy exclusive.");
136  return {};
137  }
138  constexpr TestCapabilities<_syncRead, _forceDataLossWrite, _asyncReadInconsistency, _switchReadOnly,
139  _switchWriteOnly, TestCapability::disabled, _testWriteOnly, _testReadOnly, _testRawTransfer, _testCatalogue,
140  _setRemoteValueIncrementsVersion>
142  return {};
143  }
144 
146  constexpr TestCapabilities<_syncRead, _forceDataLossWrite, _asyncReadInconsistency, _switchReadOnly,
147  _switchWriteOnly, _writeNeverLosesData, TestCapability::enabled, _testReadOnly, _testRawTransfer,
148  _testCatalogue, _setRemoteValueIncrementsVersion>
150  return {};
151  }
152  constexpr TestCapabilities<_syncRead, _forceDataLossWrite, _asyncReadInconsistency, _switchReadOnly,
153  _switchWriteOnly, _writeNeverLosesData, TestCapability::disabled, _testReadOnly, _testRawTransfer,
154  _testCatalogue, _setRemoteValueIncrementsVersion>
156  return {};
157  }
158 
160  constexpr TestCapabilities<_syncRead, _forceDataLossWrite, _asyncReadInconsistency, _switchReadOnly,
161  _switchWriteOnly, _writeNeverLosesData, _testWriteOnly, TestCapability::enabled, _testRawTransfer,
162  _testCatalogue, _setRemoteValueIncrementsVersion>
164  return {};
165  }
166  constexpr TestCapabilities<_syncRead, _forceDataLossWrite, _asyncReadInconsistency, _switchReadOnly,
167  _switchWriteOnly, _writeNeverLosesData, _testWriteOnly, TestCapability::disabled, _testRawTransfer,
168  _testCatalogue, _setRemoteValueIncrementsVersion>
170  return {};
171  }
172 
174  constexpr TestCapabilities<_syncRead, _forceDataLossWrite, _asyncReadInconsistency, _switchReadOnly,
175  _switchWriteOnly, _writeNeverLosesData, _testWriteOnly, _testReadOnly, TestCapability::disabled, _testCatalogue,
176  _setRemoteValueIncrementsVersion>
178  return {};
179  }
180  constexpr TestCapabilities<_syncRead, _forceDataLossWrite, _asyncReadInconsistency, _switchReadOnly,
181  _switchWriteOnly, _writeNeverLosesData, _testWriteOnly, _testReadOnly, TestCapability::enabled, _testCatalogue,
182  _setRemoteValueIncrementsVersion>
184  return {};
185  }
186 
188  constexpr TestCapabilities<_syncRead, _forceDataLossWrite, _asyncReadInconsistency, _switchReadOnly,
189  _switchWriteOnly, _writeNeverLosesData, _testWriteOnly, _testReadOnly, _testRawTransfer,
190  TestCapability::disabled, _setRemoteValueIncrementsVersion>
192  return {};
193  }
194  constexpr TestCapabilities<_syncRead, _forceDataLossWrite, _asyncReadInconsistency, _switchReadOnly,
195  _switchWriteOnly, _writeNeverLosesData, _testWriteOnly, _testReadOnly, _testRawTransfer,
196  TestCapability::enabled, _setRemoteValueIncrementsVersion>
198  return {};
199  }
200 
202  constexpr TestCapabilities<_syncRead, _forceDataLossWrite, _asyncReadInconsistency, _switchReadOnly,
203  _switchWriteOnly, _writeNeverLosesData, _testWriteOnly, _testReadOnly, _testRawTransfer, _testCatalogue,
206  return {};
207  }
208  constexpr TestCapabilities<_syncRead, _forceDataLossWrite, _asyncReadInconsistency, _switchReadOnly,
209  _switchWriteOnly, _writeNeverLosesData, _testWriteOnly, _testReadOnly, _testRawTransfer, _testCatalogue,
212  return {};
213  }
214 
215  static constexpr TestCapability syncRead{_syncRead};
216  static constexpr TestCapability forceDataLossWrite{_forceDataLossWrite};
217  static constexpr TestCapability asyncReadInconsistency{_asyncReadInconsistency};
218  static constexpr TestCapability switchReadOnly{_switchReadOnly};
219  static constexpr TestCapability switchWriteOnly{_switchWriteOnly};
220  static constexpr TestCapability writeNeverLosesData{_writeNeverLosesData};
221  static constexpr TestCapability testWriteOnly{_testWriteOnly};
222  static constexpr TestCapability testReadOnly{_testReadOnly};
223  static constexpr TestCapability testRawTransfer{_testRawTransfer};
224  static constexpr TestCapability testCatalogue{_testCatalogue};
225  static constexpr TestCapability setRemoteValueIncrementsVersion{_setRemoteValueIncrementsVersion};
226  };
227 
258  template<typename VECTOR_OF_REGISTERS_T = boost::mpl::vector<>>
260  public:
351  template<typename REG_T>
355  return x;
356  }
357 
361  class EnableDisableActionList : public std::list<std::pair<std::function<void(void)>, std::function<void(void)>>> {
362  public:
363  using std::list<std::pair<std::function<void(void)>, std::function<void(void)>>>::list;
364  };
365 
372  void runTests(const std::string& cdd_, const std::string& cdd2_ = "");
373 
381  return *this;
382  }
383 
384  protected:
385  void test_B_3_1_2_1();
386  void test_NOSPEC_write();
387  void test_B_3_2_1_2();
388  void test_B_3_2_2();
389  void test_B_4_2_4();
390  void test_B_6_4();
391  void test_B_7_2();
392  void test_B_8_2();
393  void test_B_8_2_1();
394  void test_B_8_3();
395  void test_B_8_4();
396  void test_B_8_5();
397  void test_B_8_5_1();
398  void test_B_8_5_2();
399  void test_B_8_5_3();
400  void test_B_8_6_6();
401  void test_B_9_1();
402  void test_B_9_2_2();
403  void test_B_9_3_1();
404  void test_B_9_3_2();
405  void test_B_9_4_1();
406  void test_B_9_5();
408  void test_B_11_2_1();
409  void test_B_11_2_2();
410  void test_B_11_6();
411  void test_C_5_2_1_2();
412  void test_C_5_2_2_2();
413  void test_C_5_2_3_2();
414  void test_C_5_2_5_2();
415  void test_C_5_2_6_2();
416  void test_C_5_2_7_2();
417  void test_C_5_3();
418  void test_C_5_3_2();
419  void test_C_5_3_3();
425 
428 
430  template<typename REG_T>
431  bool isRead(REG_T x = {}) {
432  if(x.capabilities.syncRead == TestCapability::disabled) return false;
433  if(x.capabilities.testWriteOnly == TestCapability::enabled) return false;
434  return x.isReadable();
435  }
436  template<typename REG_T>
437  bool isWrite(REG_T x = {}) {
438  if(x.capabilities.testReadOnly == TestCapability::enabled) return false;
439  return x.isWriteable();
440  }
441  template<typename REG_T>
442  bool isSyncRead(REG_T x = {}) {
443  if(x.capabilities.testWriteOnly == TestCapability::enabled) return false;
444  return x.isReadable() && !x.supportedFlags().has(ChimeraTK::AccessMode::wait_for_new_data);
445  }
446  template<typename REG_T>
447  bool isAsyncRead(REG_T x = {}) {
448  if(x.capabilities.testWriteOnly == TestCapability::enabled) return false;
449  return x.isReadable() && x.supportedFlags().has(ChimeraTK::AccessMode::wait_for_new_data);
450  }
451  template<typename REG_T>
452  bool isRaw(REG_T x = {}) {
453  return x.supportedFlags().has(ChimeraTK::AccessMode::raw);
454  }
455  template<typename REG_T>
456  bool isReadOnly(REG_T x = {}) {
457  return !x.isWriteable() && x.isReadable();
458  }
459  template<typename REG_T>
460  bool isWriteOnly(REG_T x = {}) {
461  return x.isWriteable() && !x.isReadable();
462  }
463 
465  VECTOR_OF_REGISTERS_T registers;
466 
468  std::string cdd, cdd2;
469 
472 
475  explicit ExceptionReportingBackend(boost::shared_ptr<DeviceBackend> target) : _target(std::move(target)) {}
476  ~ExceptionReportingBackend() override = default;
477 
478  void setExceptionImpl() noexcept override {
479  _hasSeenException = true;
480  _target->setException(getActiveExceptionMessage());
481  // do not keep the ExceptionReportingBackend in exception state, otherwise further exceptions do not cause this
482  // function to be executed.
484  }
485 
488  bool ret = _hasSeenException;
489  _hasSeenException = false;
490  return ret;
491  }
492 
493  void open() override {}
494  void close() override {}
495  std::string readDeviceInfo() override { return ""; }
496 
497  RegisterCatalogue getRegisterCatalogue() const override { throw; }
498 
499  private:
500  boost::shared_ptr<DeviceBackend> _target;
501  bool _hasSeenException{false};
502  };
503 
504  // Proxy for calling setForceDataLossWrite() only if allowed by capabilities.
505  template<typename T, bool condition = (T::capabilities.forceDataLossWrite == TestCapability::enabled)>
507  setForceDataLossWrite_proxy_helper(T t, bool enable) { t.setForceDataLossWrite(enable); }
508  };
509 
510  template<typename T>
513  std::cout << "Unexpected use of disabled capability." << std::endl;
514  std::terminate();
515  }
516  };
517 
518  template<typename T>
519  void setForceDataLossWrite(T t, bool enable) {
521  }
522 
523  // Proxy for calling forceAsyncReadInconsistency() only if allowed by capabilities.
524  template<typename T, bool condition = (T::capabilities.asyncReadInconsistency == TestCapability::enabled)>
526  explicit forceAsyncReadInconsistency_proxy_helper(T t) { t.forceAsyncReadInconsistency(); }
527  };
528 
529  template<typename T>
532  std::cout << "Unexpected use of disabled capability." << std::endl;
533  std::terminate();
534  }
535  };
536 
537  template<typename T>
540  }
541 
542  // Proxy for calling switchReadOnly() only if allowed by capabilities.
543  template<typename T, bool condition = (T::capabilities.switchReadOnly == TestCapability::enabled)>
545  switchReadOnly_proxy_helper(T t, bool enable) { t.switchReadOnly(enable); }
546  };
547 
548  template<typename T>
551  std::cout << "Unexpected use of disabled capability." << std::endl;
552  std::terminate();
553  }
554  };
555 
556  template<typename T>
557  void switchReadOnly(T t, bool enable) {
559  }
560 
561  // Proxy for calling switchWriteOnly() only if allowed by capabilities.
562  template<typename T, bool condition = (T::capabilities.switchWriteOnly == TestCapability::enabled)>
564  switchWriteOnly_proxy_helper(T t, bool enable) { t.switchWriteOnly(enable); }
565  };
566 
567  template<typename T>
570  std::cout << "Unexpected use of disabled capability." << std::endl;
571  std::terminate();
572  }
573  };
574 
575  template<typename T>
576  void switchWriteOnly(T t, bool enable) {
578  }
579 
580  // Proxy for getting the writeQueueLength only if allowed by capabilities.
581  template<typename T,
582  bool condition = (T::capabilities.forceDataLossWrite == TestCapability::enabled ||
583  T::capabilities.writeNeverLosesData == TestCapability::enabled)>
585  explicit writeQueueLength_proxy_helper(T t) { result = t.writeQueueLength(); }
586  size_t result;
587  };
588 
589  template<typename T>
592  std::cout << "Unexpected use of disabled capability." << std::endl;
593  std::terminate();
594  }
595  size_t result{0};
596  };
597 
598  template<typename T>
599  size_t writeQueueLength(T t) {
601  }
602 
603  // Proxy for getting nValuesToTest() if defined, and otherwise use the default
604  template<typename T>
606  using one = char;
607  struct two {
608  char x[2];
609  };
610 
611  template<typename C>
612  static one test(decltype(&C::nValuesToTest));
613  template<typename C>
614  static two test(...);
615 
616  public:
617  enum { value = sizeof(test<T>(0)) == sizeof(char) };
618  };
619 
620  template<typename T, bool hasFn = has_nValuesToTest<T>::value>
622  explicit nValuesToTest_proxy_helper(T t) { result = t.nValuesToTest(); }
623  size_t result;
624  };
625  template<typename T>
626  struct nValuesToTest_proxy_helper<T, false> {
627  explicit nValuesToTest_proxy_helper(T) { result = 2; }
628  size_t result;
629  };
630 
631  template<typename T>
632  size_t nValuesToTest(T t) {
634  }
635  };
636 
637  /********************************************************************************************************************/
638  /********************************************************************************************************************/
639  /*
640  * Implementations below this point.
641  *
642  * This is made header-only to avoid the need to link against BOOST unit test libraries for the DeviceAccess
643  * runtime library, or to introduce another library shipped with DeviceAccess just for this class.
644  */
645  /********************************************************************************************************************/
646  /********************************************************************************************************************/
647 
648  // Helper template function to compare values appropriately for the type
649  template<typename UserType>
650  bool compareHelper(UserType a, UserType b) {
651  return a == b;
652  }
653 
654  template<>
655  inline bool compareHelper<double>(double a, double b) {
656  return std::abs(a - b) <= std::numeric_limits<double>::epsilon() * 10. * std::max(std::abs(a), std::abs(b));
657  }
658 
659  template<>
660  inline bool compareHelper<float>(float a, float b) {
661  return std::abs(a - b) <= std::numeric_limits<float>::epsilon() * 10.F * std::max(std::abs(a), std::abs(b));
662  }
663 
664  // Turn off the linter warning. It shows for strings only and we can't change the template signature here.
665  template<>
666  inline bool compareHelper<std::string>(std::string a, std::string b) { // NOLINT(performance-unnecessary-value-param)
667  return a == b;
668  }
669 } // namespace ChimeraTK
670 
671 namespace std {
672  inline std::string to_string(const std::string& v) {
673  return v;
674  }
675  inline std::string to_string(const char*& v) {
676  return {v};
677  }
678 } // namespace std
679 
680 namespace ChimeraTK {
681 
682  /********************************************************************************************************************/
683 
684  // Helper macro to compare the value on an accessor and the expected 2D value
685  // Note: we use a macro and not a function, so BOOST_ERROR prints us the line number of the actual test!
686 
687  // As the function only works with the correct objects and it is unlikely that expressions are used as input
688  // parameters, we turn off the linter warning about parentheses around the macro arguments. The parentheses would
689  // make the code harder to read.
690 
691  // NOLINTBEGIN(bugprone-macro-parentheses)
692 #define CHECK_EQUALITY(accessor, expectedValue) \
693  { \
694  typedef typename decltype(expectedValue)::value_type::value_type CHECK_EQUALITY_UserType; \
695  std::string fail; \
696  BOOST_CHECK_EQUAL(accessor.getNChannels(), expectedValue.size()); \
697  BOOST_CHECK_EQUAL(accessor.getNElementsPerChannel(), expectedValue[0].size()); \
698  bool CHECK_EQUALITY_warnExpectedZero = true; \
699  for(size_t CHECK_EQUALITY_i = 0; CHECK_EQUALITY_i < expectedValue.size(); ++CHECK_EQUALITY_i) { \
700  for(size_t CHECK_EQUALITY_k = 0; CHECK_EQUALITY_k < expectedValue[0].size(); ++CHECK_EQUALITY_k) { \
701  if(CHECK_EQUALITY_warnExpectedZero && \
702  !compareHelper(expectedValue[CHECK_EQUALITY_i][CHECK_EQUALITY_k], CHECK_EQUALITY_UserType())) { \
703  /* non-zero value found in expectedValue, no need to warn about all-zero expected value */ \
704  CHECK_EQUALITY_warnExpectedZero = false; \
705  } \
706  if(!compareHelper( \
707  accessor[CHECK_EQUALITY_i][CHECK_EQUALITY_k], expectedValue[CHECK_EQUALITY_i][CHECK_EQUALITY_k])) { \
708  if(fail.empty()) { \
709  fail = "Accessor content differs from expected value. First difference at index [" + \
710  std::to_string(CHECK_EQUALITY_i) + "][" + std::to_string(CHECK_EQUALITY_k) + \
711  "]: " + std::to_string(accessor[CHECK_EQUALITY_i][CHECK_EQUALITY_k]) + \
712  " != " + std::to_string(expectedValue[CHECK_EQUALITY_i][CHECK_EQUALITY_k]); \
713  } \
714  } \
715  } \
716  } \
717  if(!fail.empty()) { \
718  BOOST_ERROR(fail); \
719  } \
720  if(CHECK_EQUALITY_warnExpectedZero && !std::is_same<CHECK_EQUALITY_UserType, ChimeraTK::Boolean>::value) { \
721  BOOST_ERROR("Comparison with all-zero expectedValue! Test may be insensitive! Check the " \
722  "generateValue() implementations!"); \
723  } \
724  } \
725  (void)(0)
726 
727 // Similar to CHECK_EQUALITY, but compares two 2D vectors
728 #define CHECK_EQUALITY_VECTOR(foundValue, expectedValue) \
729  { \
730  typedef typename decltype(expectedValue)::value_type::value_type CHECK_EQUALITY_UserType; \
731  std::string fail; \
732  BOOST_CHECK_EQUAL(foundValue.size(), expectedValue.size()); \
733  BOOST_CHECK_EQUAL(foundValue[0].size(), expectedValue[0].size()); \
734  bool CHECK_EQUALITY_warnExpectedZero = true; \
735  for(size_t CHECK_EQUALITY_i = 0; CHECK_EQUALITY_i < expectedValue.size(); ++CHECK_EQUALITY_i) { \
736  for(size_t CHECK_EQUALITY_k = 0; CHECK_EQUALITY_k < expectedValue[0].size(); ++CHECK_EQUALITY_k) { \
737  if(CHECK_EQUALITY_warnExpectedZero && \
738  !compareHelper(expectedValue[CHECK_EQUALITY_i][CHECK_EQUALITY_k], CHECK_EQUALITY_UserType())) { \
739  /* non-zero value found in expectedValue, no need to warn about all-zero expected value */ \
740  CHECK_EQUALITY_warnExpectedZero = false; \
741  } \
742  if(!compareHelper( \
743  foundValue[CHECK_EQUALITY_i][CHECK_EQUALITY_k], expectedValue[CHECK_EQUALITY_i][CHECK_EQUALITY_k])) { \
744  if(fail.empty()) { \
745  fail = "Data content differs from expected value. First difference at index [" + \
746  std::to_string(CHECK_EQUALITY_i) + "][" + std::to_string(CHECK_EQUALITY_k) + \
747  "]: " + std::to_string(foundValue[CHECK_EQUALITY_i][CHECK_EQUALITY_k]) + \
748  " != " + std::to_string(expectedValue[CHECK_EQUALITY_i][CHECK_EQUALITY_k]); \
749  } \
750  } \
751  } \
752  } \
753  if(!fail.empty()) { \
754  BOOST_ERROR(fail); \
755  } \
756  if(CHECK_EQUALITY_warnExpectedZero && !std::is_same<CHECK_EQUALITY_UserType, ChimeraTK::Boolean>::value) { \
757  BOOST_ERROR("Comparison with all-zero expectedValue! Test may be insensitive! Check the " \
758  "generateValue() implementations!"); \
759  } \
760  } \
761  (void)(0)
762 
763 // Similar to CHECK_EQUALITY, but runs readLatest() on the accessor in a loop until the expected value has arrived, for
764 // at most maxMilliseconds. If the expected value is not seen within that time, an error is risen.
765 #define CHECK_EQUALITY_TIMEOUT(accessor, expectedValue, maxMilliseconds) \
766  { \
767  typedef typename decltype(expectedValue)::value_type::value_type CHECK_EQUALITY_UserType; \
768  std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); \
769  bool CHECK_EQUALITY_warnExpectedZero = true; \
770  while(true) { \
771  accessor.readLatest(); \
772  std::string fail; \
773  BOOST_CHECK_EQUAL(accessor.getNChannels(), expectedValue.size()); \
774  BOOST_CHECK_EQUAL(accessor.getNElementsPerChannel(), expectedValue[0].size()); \
775  for(size_t CHECK_EQUALITY_i = 0; CHECK_EQUALITY_i < expectedValue.size(); ++CHECK_EQUALITY_i) { \
776  for(size_t CHECK_EQUALITY_k = 0; CHECK_EQUALITY_k < expectedValue[0].size(); ++CHECK_EQUALITY_k) { \
777  if(CHECK_EQUALITY_warnExpectedZero && \
778  !compareHelper(expectedValue[CHECK_EQUALITY_i][CHECK_EQUALITY_k], CHECK_EQUALITY_UserType())) { \
779  /* non-zero value found in expectedValue, no need to warn about all-zero expected value */ \
780  CHECK_EQUALITY_warnExpectedZero = false; \
781  } \
782  if(!compareHelper( \
783  accessor[CHECK_EQUALITY_i][CHECK_EQUALITY_k], expectedValue[CHECK_EQUALITY_i][CHECK_EQUALITY_k])) { \
784  if(fail.empty()) { \
785  fail = "Accessor content differs from expected value. First difference at index [" + \
786  std::to_string(CHECK_EQUALITY_i) + "][" + std::to_string(CHECK_EQUALITY_k) + \
787  "]: " + std::to_string(accessor[CHECK_EQUALITY_i][CHECK_EQUALITY_k]) + \
788  " != " + std::to_string(expectedValue[CHECK_EQUALITY_i][CHECK_EQUALITY_k]); \
789  } \
790  } \
791  } \
792  } \
793  if(fail.empty()) break; \
794  bool timeout_reached = (std::chrono::steady_clock::now() - t0) > std::chrono::milliseconds(maxMilliseconds); \
795  BOOST_CHECK_MESSAGE(!timeout_reached, fail); \
796  if(timeout_reached) break; \
797  usleep(10000); \
798  } \
799  if(CHECK_EQUALITY_warnExpectedZero && !std::is_same<CHECK_EQUALITY_UserType, ChimeraTK::Boolean>::value) { \
800  BOOST_ERROR("Comparison with all-zero expectedValue! Test may be insensitive! Check the " \
801  "generateValue() implementations!"); \
802  } \
803  } \
804  (void)(0)
805 
806 // Similar to CHECK_EQUALITY_TIMEOUT, but compares two 2D vectors
807 #define CHECK_EQUALITY_VECTOR_TIMEOUT(foundValue, expectedValue, maxMilliseconds) \
808  { \
809  typedef typename decltype(expectedValue)::value_type::value_type CHECK_EQUALITY_UserType; \
810  std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); \
811  bool CHECK_EQUALITY_warnExpectedZero = true; \
812  while(true) { \
813  std::string fail; \
814  auto CHECK_EQUALITY_value = foundValue; /* Copy value for consistency and performance in the following code */ \
815  BOOST_CHECK_EQUAL(theValue.size(), expectedValue.size()); \
816  BOOST_CHECK_EQUAL(theValue[0].size(), expectedValue[0].size()); \
817  for(size_t CHECK_EQUALITY_i = 0; CHECK_EQUALITY_i < expectedValue.size(); ++CHECK_EQUALITY_i) { \
818  for(size_t CHECK_EQUALITY_k = 0; CHECK_EQUALITY_k < expectedValue[0].size(); ++CHECK_EQUALITY_k) { \
819  if(CHECK_EQUALITY_warnExpectedZero && \
820  !compareHelper(expectedValue[CHECK_EQUALITY_i][CHECK_EQUALITY_k], CHECK_EQUALITY_UserType())) { \
821  /* non-zero value found in expectedValue, no need to warn about all-zero expected value */ \
822  CHECK_EQUALITY_warnExpectedZero = false; \
823  } \
824  if(!compareHelper(CHECK_EQUALITY_value[CHECK_EQUALITY_i][CHECK_EQUALITY_k], \
825  expectedValue[CHECK_EQUALITY_i][CHECK_EQUALITY_k])) { \
826  if(fail.empty()) { \
827  fail = "Data content differs from expected value. First difference at index [" + \
828  std::to_string(CHECK_EQUALITY_i) + "][" + std::to_string(CHECK_EQUALITY_k) + \
829  "]: " + std::to_string(CHECK_EQUALITY_value[CHECK_EQUALITY_i][CHECK_EQUALITY_k]) + \
830  " != " + std::to_string(expectedValue[CHECK_EQUALITY_i][CHECK_EQUALITY_k]); \
831  } \
832  } \
833  } \
834  } \
835  if(fail.empty()) break; \
836  bool timeout_reached = (std::chrono::steady_clock::now() - t0) > std::chrono::milliseconds(maxMilliseconds); \
837  BOOST_CHECK_MESSAGE(!timeout_reached, fail); \
838  if(timeout_reached) break; \
839  usleep(10000); \
840  } \
841  if(CHECK_EQUALITY_warnExpectedZero && !std::is_same<CHECK_EQUALITY_UserType, ChimeraTK::Boolean>::value) { \
842  BOOST_ERROR("Comparison with all-zero expectedValue! Test may be insensitive! Check the " \
843  "generateValue() implementations!"); \
844  } \
845  } \
846  (void)(0)
847  // NOLINTEND(bugprone-macro-parentheses)
848 
849 #define CHECK_TIMEOUT(condition, maxMilliseconds) \
850  { \
851  std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); \
852  while(!(condition)) { \
853  bool timeout_reached = (std::chrono::steady_clock::now() - t0) > std::chrono::milliseconds(maxMilliseconds); \
854  BOOST_CHECK(!timeout_reached); \
855  if(timeout_reached) break; \
856  usleep(1000); \
857  } \
858  } \
859  (void)0
860 
861  /********************************************************************************************************************/
862 
863  template<typename VECTOR_OF_REGISTERS_T>
864  void UnifiedBackendTest<VECTOR_OF_REGISTERS_T>::runTests(const std::string& cdd_, const std::string& cdd2_) {
865  cdd = cdd_;
866  cdd2 = cdd2_;
867  std::cout << "=== UnifiedBackendTest for " << cdd;
868  if(!cdd2.empty()) std::cout << " and " << cdd2;
869  std::cout << std::endl;
870 
871  size_t nSyncReadRegisters = 0;
872  size_t nAsyncReadRegisters = 0;
873  size_t nWriteRegisters = 0;
874  size_t nRawRegisters = 0;
875  size_t nReadOnlyRegisters = 0;
876  size_t nWriteOnlyRegisters = 0;
877  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
878  if(this->isAsyncRead(x)) ++nAsyncReadRegisters;
879  if(this->isSyncRead(x)) ++nSyncReadRegisters;
880  if(this->isWrite(x)) ++nWriteRegisters;
881  if(this->isWrite(x) && !this->isRead(x)) ++nWriteOnlyRegisters;
882  if(!this->isWrite(x) && this->isRead(x)) ++nReadOnlyRegisters;
883  if(this->isRaw(x)) ++nRawRegisters;
884  if(x.capabilities.forceDataLossWrite == TestCapability::unspecified) {
885  std::cout << "WARNING: Register " << x.path() << " has unspecified capability forceDataLossWrite!" << std::endl;
886  }
887  if(x.capabilities.asyncReadInconsistency == TestCapability::unspecified) {
888  std::cout << "WARNING: Register " << x.path() << " has unspecified capability asyncReadInconsistency!"
889  << std::endl;
890  }
891  if(x.capabilities.switchReadOnly == TestCapability::unspecified) {
892  std::cout << "WARNING: Register " << x.path() << " has unspecified capability switchReadOnly!" << std::endl;
893  }
894  if(x.capabilities.switchWriteOnly == TestCapability::unspecified) {
895  std::cout << "WARNING: Register " << x.path() << " has unspecified capability switchWriteOnly!" << std::endl;
896  }
897  if(x.capabilities.writeNeverLosesData == TestCapability::unspecified) {
898  std::cout << "WARNING: Register " << x.path() << " has unspecified capability writeNeverLosesData!"
899  << std::endl;
900  }
901  });
902 
903  std::cout << "Using " << nSyncReadRegisters << " synchronous and " << nAsyncReadRegisters
904  << " asynchronous read and " << nWriteRegisters << " write test registers." << std::endl;
905  std::cout << "Of those are " << nRawRegisters << " supporting raw mode, " << nReadOnlyRegisters
906  << " are read-only and " << nWriteOnlyRegisters << " write-only." << std::endl;
907 
908  if(nSyncReadRegisters + nAsyncReadRegisters + nWriteRegisters == 0) {
909  std::cout << "ERROR: No test registers specified. Cannot perform tests." << std::endl;
910  std::exit(1);
911  }
912 
913  if(nSyncReadRegisters + nAsyncReadRegisters == 0) {
914  std::cout << "WARNING: No read test registers specified. This is acceptable only if the backend does not "
915  << "support reading at all." << std::endl;
916  }
917  else if(nSyncReadRegisters == 0) {
918  std::cout
919  << "WARNING: No synchronous read test registers specified. This is acceptable only if the backend has only "
920  << "registers which support AccessMode::wait_for_new_data." << std::endl;
921  }
922  else if(nAsyncReadRegisters == 0) {
923  std::cout
924  << "WARNING: No asynchronous read test registers specified. This is acceptable only if the backend does not "
925  << "support AccessMode::wait_for_new_data at all." << std::endl;
926  }
927  if(nWriteRegisters == 0) {
928  std::cout << "WARNING: No write test registers specified. This is acceptable only if the backend does not "
929  << "support writing at all." << std::endl;
930  }
931 
932  if(nRawRegisters == 0) {
933  std::cout << "WARNING: No raw registers specified. This is acceptable only if the backend does not "
934  << "support raw access mode at all." << std::endl;
935  }
936  if(nReadOnlyRegisters == 0) {
937  std::cout << "WARNING: No read-only registers specified." << std::endl;
938  }
939  if(nWriteOnlyRegisters == 0) {
940  std::cout << "WARNING: No write-only registers specified." << std::endl;
941  }
942 
943  // run the tests
944  test_B_3_1_2_1();
945  test_NOSPEC_write();
946  test_B_3_2_1_2();
947  test_B_3_2_2();
948  test_B_4_2_4();
949  test_B_6_4();
950  test_B_7_2();
951  test_B_8_2();
952  test_B_8_2_1();
953  test_B_8_3();
954  test_B_8_4();
955  test_B_8_5();
956  test_B_8_5_1();
957  test_B_8_5_2();
958  test_B_8_5_3();
959  test_B_8_6_6();
960  test_B_9_1();
961  test_B_9_2_2();
962  test_B_9_3_1();
963  test_B_9_3_2();
964  test_B_9_4_1();
965  test_B_9_5();
966  test_NOSPEC_newVersionAfterOpen();
967  test_B_11_2_1();
968  test_B_11_2_2();
969  test_B_11_6();
970  test_C_5_2_1_2();
971  test_C_5_2_2_2();
972  test_C_5_2_3_2();
973  test_C_5_2_5_2();
974  test_C_5_2_6_2();
975  test_C_5_2_7_2();
976  test_C_5_3();
977  test_C_5_3_2();
978  test_C_5_3_3();
979  test_NOSPEC_valueAfterConstruction();
980  test_NOSPEC_backendNotClosedAfterException();
981  test_NOSPEC_rawTransfer();
982  test_NOSPEC_catalogueRaw();
983  test_NOSPEC_catalogueReadWrite();
984  }
985 
986  /********************************************************************************************************************/
987 
988  template<typename VECTOR_OF_REGISTERS_T>
990  auto stopTime = std::chrono::steady_clock::now() + std::chrono::seconds(60);
991  for(size_t i = 0;; ++i) {
992  try {
993  d.open();
994  break;
995  }
996  catch(ChimeraTK::runtime_error&) {
997  usleep(10000); // 10ms
998  // try minimum 10 times for at least 60 seconds
999  if((i > 10) && (std::chrono::steady_clock::now() > stopTime)) {
1000  BOOST_FAIL("Device did not recover within 60 seconds after forced ChimeraTK::runtime_error.");
1001  }
1002  }
1003  }
1004  }
1005 
1006  /********************************************************************************************************************/
1007 
1012  template<typename VECTOR_OF_REGISTERS_T>
1014  std::cout << "--- test_B_3_1_2_1 - synchronous read" << std::endl;
1015  Device d(cdd);
1016 
1017  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
1018  if(!this->isRead(x)) return;
1019  typedef typename decltype(x)::minimumUserType UserType;
1020  auto registerName = x.path();
1021  std::cout << "... registerName = " << registerName << std::endl;
1022  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName);
1023 
1024  // Set remote value to be read.
1025  x.setRemoteValue();
1026  std::vector<std::vector<UserType>> v1 = x.template getRemoteValue<UserType>();
1027 
1028  // open the device
1029  d.open();
1030 
1031  // Read value
1032  reg.read();
1033 
1034  // Check application buffer
1035  CHECK_EQUALITY(reg, v1);
1036  BOOST_CHECK(reg.dataValidity() == DataValidity::ok);
1037 
1038  // Set an intermediate remote value to be overwritten next
1039  x.setRemoteValue();
1040  usleep(100000); // give potential race conditions a chance to pop up more easily...
1041 
1042  std::vector<std::vector<UserType>> v2;
1043  for(size_t iter = 0; iter < this->nValuesToTest(x); ++iter) {
1044  // Set another remote value to be read.
1045  x.setRemoteValue();
1046  v2 = x.template getRemoteValue<UserType>();
1047 
1048  // Read second value
1049  reg.read();
1050 
1051  // Check application buffer
1052  CHECK_EQUALITY(reg, v2);
1053  BOOST_CHECK(reg.dataValidity() == DataValidity::ok);
1054  }
1055 
1056  // Reading again without changing remote value does not block and gives the same value
1057  reg.read();
1058 
1059  // Check application buffer
1060  CHECK_EQUALITY(reg, v2);
1061  BOOST_CHECK(reg.dataValidity() == DataValidity::ok);
1062 
1063  // close device again
1064  d.close();
1065  });
1066  }
1067 
1068  /********************************************************************************************************************/
1069 
1074  template<typename VECTOR_OF_REGISTERS_T>
1076  std::cout << "--- test_NOSPEC_write - write" << std::endl;
1077  Device d(cdd);
1078 
1079  // open the device
1080  d.open();
1081 
1082  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
1083  if(!this->isWrite(x)) return;
1084  typedef typename decltype(x)::minimumUserType UserType;
1085  auto registerName = x.path();
1086 
1087  std::cout << "... registerName = " << registerName << std::endl;
1088  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName);
1089 
1090  // write some value
1091  for(size_t iter = 0; iter < this->nValuesToTest(x); ++iter) {
1092  auto theValue = x.template generateValue<UserType>();
1093 
1094  reg = theValue;
1095  reg.write();
1096 
1097  // check remote value (with timeout, because the write might complete asynchronously)
1098  CHECK_EQUALITY_VECTOR_TIMEOUT(x.template getRemoteValue<UserType>(), theValue, 10000);
1099  }
1100  });
1101 
1102  // close device again
1103  d.close();
1104  }
1105 
1106  /********************************************************************************************************************/
1107 
1114  template<typename VECTOR_OF_REGISTERS_T>
1116  std::cout << "--- test_B_3_2_1_2 - write() does not destroy application buffer" << std::endl;
1117  Device d(cdd);
1118 
1119  // open the device
1120  d.open();
1121 
1122  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
1123  if(!this->isWrite(x)) return;
1124  typedef typename decltype(x)::minimumUserType UserType;
1125  auto registerName = x.path();
1126  std::cout << "... registerName = " << registerName << std::endl;
1127  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName);
1128 
1129  // write some value
1130  auto theValue = x.template generateValue<UserType>();
1131  reg = theValue;
1132  VersionNumber ver;
1133  reg.write(ver);
1134 
1135  // check that application data buffer is not changed (non-destructive write, B.3.2.1.2)
1136  BOOST_CHECK(reg.getNChannels() == theValue.size());
1137  BOOST_CHECK(reg.getNElementsPerChannel() == theValue[0].size());
1138  CHECK_EQUALITY(reg, theValue);
1139 
1140  // check the version number
1141  BOOST_CHECK(reg.getVersionNumber() == ver);
1142  });
1143 
1144  // close device again
1145  d.close();
1146  }
1147 
1148  /********************************************************************************************************************/
1149 
1154  template<typename VECTOR_OF_REGISTERS_T>
1156  std::cout << "--- test_B_3_2_2 - destructive write" << std::endl;
1157  Device d(cdd);
1158 
1159  // open the device
1160  d.open();
1161 
1162  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
1163  if(!this->isWrite(x)) return;
1164  typedef typename decltype(x)::minimumUserType UserType;
1165  auto registerName = x.path();
1166 
1167  std::cout << "... registerName = " << registerName << std::endl;
1168  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName);
1169 
1170  // write some value destructively
1171  auto theValue = x.template generateValue<UserType>();
1172  reg = theValue;
1173  VersionNumber ver;
1174  reg.writeDestructively(ver);
1175 
1176  // check that application data buffer shape is not changed (content may be lost)
1177  BOOST_CHECK(reg.getNChannels() == theValue.size());
1178  BOOST_CHECK(reg.getNElementsPerChannel() == theValue[0].size());
1179 
1180  // check the version number
1181  BOOST_CHECK(reg.getVersionNumber() == ver);
1182 
1183  // check remote value
1184  CHECK_EQUALITY_VECTOR_TIMEOUT(x.template getRemoteValue<UserType>(), theValue, 10000);
1185  });
1186 
1187  // close device again
1188  d.close();
1189  }
1190 
1191 /**********************************************************************************************************************/
1192 
1193 // Turn off the warning about parameter parentheses. They probably are even wrong for template arguments,
1194 // and clutter the readability for variable names.
1195 // NOLINTBEGIN(bugprone-macro-parentheses)
1196 
1198 #define ALTER_AND_STORE_APPLICATION_BUFFER(UserType, accessor) \
1199  std::vector<std::vector<UserType>> STORE_APPLICATION_BUFFER_data; \
1200  VersionNumber STORE_APPLICATION_BUFFER_version; \
1201  DataValidity STORE_APPLICATION_BUFFER_validity; \
1202  for(size_t i = 0; i < accessor.getNChannels(); ++i) { \
1203  if constexpr(std::is_arithmetic_v<UserType>) \
1204  std::iota(accessor[i].begin(), accessor[i].end(), std::numeric_limits<UserType>::min() + 1); \
1205  if constexpr(std::is_same_v<std::string, UserType>) std::fill(accessor[i].begin(), accessor[i].end(), "FACECAFE"); \
1206  STORE_APPLICATION_BUFFER_data.push_back(accessor[i]); \
1207  } \
1208  STORE_APPLICATION_BUFFER_version = accessor.getVersionNumber(); \
1209  STORE_APPLICATION_BUFFER_validity = accessor.dataValidity()
1210 
1211 #define CHECK_APPLICATION_BUFFER(UserType, accessor) \
1212  CHECK_EQUALITY(accessor, STORE_APPLICATION_BUFFER_data); \
1213  BOOST_CHECK(STORE_APPLICATION_BUFFER_version == accessor.getVersionNumber()); \
1214  BOOST_CHECK(STORE_APPLICATION_BUFFER_validity == accessor.dataValidity())
1215 
1216  // NOLINTEND(bugprone-macro-parentheses)
1217 
1222  template<typename VECTOR_OF_REGISTERS_T>
1224  std::cout << "--- test_B_4_2_4 - transfer implementations do not change the application buffer" << std::endl;
1225  Device d(cdd);
1226 
1227  // open the device
1228  d.open();
1229 
1230  std::cout << "... writeTransfer()" << std::endl;
1231  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
1232  if(!this->isWrite(x)) return;
1233  typedef typename decltype(x)::minimumUserType UserType;
1234  auto registerName = x.path();
1235  std::cout << "... registerName = " << registerName << std::endl;
1236  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName);
1237  auto te = reg.getHighLevelImplElement();
1238 
1239  // write some value, calling the stages manually
1240  auto theValue = x.template generateValue<UserType>();
1241  reg = theValue;
1242  VersionNumber ver;
1243  te->preWrite(TransferType::write, ver);
1244  ALTER_AND_STORE_APPLICATION_BUFFER(UserType, reg);
1245  te->writeTransfer(ver);
1246  CHECK_APPLICATION_BUFFER(UserType, reg);
1247  te->postWrite(TransferType::write, ver);
1248  });
1249 
1250  std::cout << "... writeTransferDestructively()" << std::endl;
1251  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
1252  if(!this->isWrite(x)) return;
1253  typedef typename decltype(x)::minimumUserType UserType;
1254  auto registerName = x.path();
1255  std::cout << "... registerName = " << registerName << std::endl;
1256  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName);
1257  auto te = reg.getHighLevelImplElement();
1258 
1259  // write some value, calling the stages manually
1260  auto theValue = x.template generateValue<UserType>();
1261  reg = theValue;
1262  VersionNumber ver;
1263  te->preWrite(TransferType::writeDestructively, ver);
1264  ALTER_AND_STORE_APPLICATION_BUFFER(UserType, reg);
1265  te->writeTransferDestructively(ver);
1266  CHECK_APPLICATION_BUFFER(UserType, reg);
1267  te->postWrite(TransferType::writeDestructively, ver);
1268  });
1269 
1270  std::cout << "... readTransferSynchronously()" << std::endl;
1271  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
1272  if(!this->isRead(x)) return;
1273  typedef typename decltype(x)::minimumUserType UserType;
1274  auto registerName = x.path();
1275  std::cout << "... registerName = " << registerName << std::endl;
1276  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName);
1277  auto te = reg.getHighLevelImplElement();
1278 
1279  // read some value, calling the stages manually
1280  auto theValue = x.template generateValue<UserType>();
1281  reg = theValue;
1282  VersionNumber ver;
1283  ALTER_AND_STORE_APPLICATION_BUFFER(UserType, reg);
1284  te->preRead(TransferType::read);
1285  CHECK_APPLICATION_BUFFER(UserType, reg);
1286  te->readTransfer();
1287  CHECK_APPLICATION_BUFFER(UserType, reg);
1288  te->postRead(TransferType::read, true);
1289  });
1290 
1291  // close device again
1292  d.close();
1293  }
1294 
1295  /********************************************************************************************************************/
1296 
1301  template<typename VECTOR_OF_REGISTERS_T>
1303  if(_testOnlyTransferElement) return;
1304  std::cout << "--- test_B_6_4 - application buffer unchanged after exception" << std::endl;
1305  Device d(cdd);
1306 
1307  std::cout << "... synchronous read " << std::endl;
1308  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
1309  if(!this->isRead(x)) return;
1310  typedef typename decltype(x)::minimumUserType UserType;
1311  auto registerName = x.path();
1312  int someNumber = 42;
1313 
1314  std::cout << " registerName = " << registerName << std::endl;
1315  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName);
1316 
1317  // alter the application buffer to make sure it is not changed under exception
1318  reg[0][0] = numericToUserType<UserType>(someNumber);
1319  reg.setDataValidity(DataValidity::ok);
1320  BOOST_CHECK(reg.getVersionNumber() == VersionNumber(nullptr)); // (quasi-assertion for the test)
1321 
1322  // trigger logic error
1323  BOOST_CHECK_THROW(reg.read(), logic_error); // (no check intended, just catch)
1324 
1325  // check that the application buffer has not changed after exception
1326  BOOST_CHECK(reg[0][0] == numericToUserType<UserType>(someNumber));
1327  BOOST_CHECK(reg.dataValidity() == DataValidity::ok);
1328  BOOST_CHECK(reg.getVersionNumber() == VersionNumber(nullptr));
1329 
1330  for(size_t i = 0; i < x.nRuntimeErrorCases(); ++i) {
1331  std::cout << " -> runtime_error case: " << i << std::endl;
1332  // open the device, then let it throw runtime_error exceptions
1333  d.open();
1334 
1335  // enable runtime errors
1336  x.setForceRuntimeError(true, i);
1337 
1338  // trigger runtime_error
1339  BOOST_CHECK_THROW(reg.read(), runtime_error); // (no check intended, just catch)
1340 
1341  // check that the application buffer has not changed after exception
1342  BOOST_CHECK(reg[0][0] == numericToUserType<UserType>(someNumber));
1343  BOOST_CHECK(reg.dataValidity() == DataValidity::ok);
1344  BOOST_CHECK(reg.getVersionNumber() == VersionNumber(nullptr));
1345 
1346  // disable runtime errors
1347  x.setForceRuntimeError(false, i);
1348 
1349  // recover
1350  this->recoverDevice(d);
1351 
1352  // close device again
1353  d.close();
1354  }
1355  });
1356 
1357  std::cout << "... asynchronous read " << std::endl;
1358  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
1359  if(!this->isAsyncRead(x)) return;
1360  typedef typename decltype(x)::minimumUserType UserType;
1361  auto registerName = x.path();
1362  int someNumber = 42;
1363 
1364  std::cout << " registerName = " << registerName << std::endl;
1365  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName, 0, 0, {AccessMode::wait_for_new_data});
1366 
1367  // alter the application buffer to make sure it is not changed under exception
1368  reg[0][0] = numericToUserType<UserType>(someNumber);
1369  reg.setDataValidity(DataValidity::ok);
1370  BOOST_CHECK(reg.getVersionNumber() == VersionNumber(nullptr)); // (quasi-assertion for the test)
1371 
1372  // trigger logic error via read()
1373  BOOST_CHECK_THROW(reg.read(), logic_error); // (no check intended, just catch)
1374 
1375  // check that the application buffer has not changed after exception
1376  BOOST_CHECK(reg[0][0] == numericToUserType<UserType>(someNumber));
1377  BOOST_CHECK(reg.dataValidity() == DataValidity::ok);
1378  BOOST_CHECK(reg.getVersionNumber() == VersionNumber(nullptr));
1379 
1380  // trigger logic error via readNonBlocking()
1381  BOOST_CHECK_THROW(reg.readNonBlocking(), logic_error); // (no check intended, just catch)
1382 
1383  // check that the application buffer has not changed after exception
1384  BOOST_CHECK(reg[0][0] == numericToUserType<UserType>(someNumber));
1385  BOOST_CHECK(reg.dataValidity() == DataValidity::ok);
1386  BOOST_CHECK(reg.getVersionNumber() == VersionNumber(nullptr));
1387 
1388  for(size_t i = 0; i < x.nRuntimeErrorCases(); ++i) {
1389  std::cout << " -> runtime_error case: " << i << std::endl;
1390  // open the device, then let it throw runtime_error exceptions
1391  d.open();
1392  d.activateAsyncRead();
1393  reg.read(); // initial value
1394 
1395  // enable runtime errors
1396  x.setForceRuntimeError(true, i);
1397 
1398  // alter the application buffer to make sure it is not changed under exception
1399  reg[0][0] = numericToUserType<UserType>(someNumber);
1400  reg.setDataValidity(DataValidity::ok);
1401  auto ver = reg.getVersionNumber();
1402 
1403  // trigger runtime_error via read
1404  BOOST_CHECK_THROW(reg.read(), runtime_error); // (no check intended, just catch)
1405 
1406  // check that the application buffer has not changed after exception
1407  BOOST_CHECK(reg[0][0] == numericToUserType<UserType>(someNumber));
1408  BOOST_CHECK(reg.dataValidity() == DataValidity::ok);
1409  BOOST_CHECK(reg.getVersionNumber() == ver);
1410 
1411  // recover to get another exception
1412  x.setForceRuntimeError(false, i);
1413  this->recoverDevice(d);
1414  d.activateAsyncRead();
1415  reg.read(); // initial value
1416  x.setForceRuntimeError(true, i);
1417 
1418  // alter the application buffer to make sure it is not changed under exception
1419  reg[0][0] = numericToUserType<UserType>(someNumber);
1420  reg.setDataValidity(DataValidity::ok);
1421  ver = reg.getVersionNumber();
1422 
1423  // trigger runtime_error via readNonBlocking
1424  try {
1425  while(!reg.readNonBlocking()) usleep(10000);
1426  }
1427  catch(runtime_error&) {
1428  }
1429 
1430  // check that the application buffer has not changed after exception
1431  BOOST_CHECK(reg[0][0] == numericToUserType<UserType>(someNumber));
1432  BOOST_CHECK(reg.dataValidity() == DataValidity::ok);
1433  BOOST_CHECK(reg.getVersionNumber() == ver);
1434 
1435  // disable exceptions on read
1436  x.setForceRuntimeError(false, i);
1437 
1438  // recover
1439  this->recoverDevice(d);
1440 
1441  // close device again
1442  d.close();
1443  }
1444  });
1445 
1446  std::cout << "... write " << std::endl;
1447  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
1448  if(!this->isWrite(x)) return;
1449  typedef typename decltype(x)::minimumUserType UserType;
1450  auto registerName = x.path();
1451  int someNumber = 42;
1452 
1453  std::cout << " registerName = " << registerName << std::endl;
1454  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName);
1455 
1456  // alter the application buffer to make sure it is not changed under exception
1457  reg[0][0] = numericToUserType<UserType>(someNumber);
1458  reg.setDataValidity(DataValidity::ok);
1459  BOOST_CHECK(reg.getVersionNumber() == VersionNumber(nullptr)); // (quasi-assertion for the test)
1460 
1461  // trigger logic error
1462  BOOST_CHECK_THROW(reg.write(), logic_error); // (no check intended, just catch)
1463 
1464  // check that the application buffer has not changed after exception
1465  BOOST_CHECK(reg[0][0] == numericToUserType<UserType>(someNumber));
1466  BOOST_CHECK(reg.dataValidity() == DataValidity::ok);
1467  BOOST_CHECK(reg.getVersionNumber() == VersionNumber(nullptr));
1468 
1469  for(size_t i = 0; i < x.nRuntimeErrorCases(); ++i) {
1470  std::cout << " -> runtime_error case: " << i << std::endl;
1471  // open the device, then let it throw runtime_error exceptions
1472  d.open();
1473 
1474  // enable exceptions on read
1475  x.setForceRuntimeError(true, i);
1476 
1477  // trigger runtime_error
1478  BOOST_CHECK_THROW(reg.write(), runtime_error); // (no check intended, just catch)
1479 
1480  // check that the application buffer has not changed after exception
1481  BOOST_CHECK(reg[0][0] == numericToUserType<UserType>(someNumber));
1482  BOOST_CHECK(reg.dataValidity() == DataValidity::ok);
1483  BOOST_CHECK(reg.getVersionNumber() == VersionNumber(nullptr));
1484 
1485  // disable exceptions on read
1486  x.setForceRuntimeError(false, i);
1487 
1488  // recover
1489  this->recoverDevice(d);
1490 
1491  // close device again
1492  d.close();
1493  }
1494  });
1495  }
1496 
1497  /********************************************************************************************************************/
1498 
1503  template<typename VECTOR_OF_REGISTERS_T>
1505  std::cout << "--- test_B_7_2 - data loss in write" << std::endl;
1506  Device d(cdd);
1507 
1508  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
1509  if(!this->isWrite(x)) return;
1510  if(x.capabilities.forceDataLossWrite == TestCapability::enabled) {
1511  typedef typename decltype(x)::minimumUserType UserType;
1512  auto registerName = x.path();
1513  std::cout << "... registerName = " << registerName << " (data loss expected)" << std::endl;
1514 
1515  // enable test condition
1516  size_t attempts = this->writeQueueLength(x) + 1;
1517  this->setForceDataLossWrite(x, true);
1518 
1519  // open the device
1520  d.open();
1521 
1522  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName);
1523 
1524  // write some value the requested number of attempts
1525  for(size_t i = 0; i < attempts; ++i) {
1526  auto theValue = x.template generateValue<UserType>();
1527  reg = theValue;
1528  VersionNumber someVersion;
1529  bool dataLost = reg.write(someVersion);
1530  if(i < attempts - 1) {
1531  BOOST_CHECK(dataLost == false);
1532  }
1533  else {
1534  BOOST_CHECK(dataLost == true);
1535  }
1536  // User buffer must be intact even when value was lost somewhere
1537  CHECK_EQUALITY(reg, theValue);
1538  BOOST_CHECK(reg.dataValidity() == DataValidity::ok);
1539  BOOST_CHECK(reg.getVersionNumber() == someVersion);
1540  }
1541 
1542  // disable test condition
1543  this->setForceDataLossWrite(x, false);
1544 
1545  // check remote value, must be the last written value
1546  auto v1 = x.template getRemoteValue<UserType>();
1547  CHECK_EQUALITY(reg, v1);
1548 
1549  // close device again
1550  d.close();
1551  }
1552  else if(x.capabilities.writeNeverLosesData == TestCapability::enabled) {
1553  typedef typename decltype(x)::minimumUserType UserType;
1554  auto registerName = x.path();
1555  std::cout << "... registerName = " << registerName << " (data loss never expected)" << std::endl;
1556 
1557  // obtain number of attempts to make
1558  size_t attempts = this->writeQueueLength(x) + 1;
1559 
1560  // open the device
1561  d.open();
1562 
1563  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName);
1564 
1565  // write some value the requested number of attempts
1566  for(size_t i = 0; i < attempts; ++i) {
1567  auto theValue = x.template generateValue<UserType>();
1568  reg = theValue;
1569  VersionNumber someVersion;
1570  bool dataLost = reg.write(someVersion);
1571  BOOST_CHECK(dataLost == false);
1572  }
1573 
1574  // check remote value, must be the last written value
1575  auto v1 = x.template getRemoteValue<UserType>();
1576  CHECK_EQUALITY(reg, v1);
1577 
1578  // close device again
1579  d.close();
1580  }
1581  });
1582  }
1583 
1584  /********************************************************************************************************************/
1585 
1593  template<typename VECTOR_OF_REGISTERS_T>
1595  std::cout << "--- test_B_8_2 - async read fills _readQueue" << std::endl;
1596  Device d(cdd);
1597 
1598  // open the device and activate asynchronous reads
1599  d.open();
1600  d.activateAsyncRead();
1601 
1602  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
1603  if(!this->isAsyncRead(x)) return;
1604  typedef typename decltype(x)::minimumUserType UserType;
1605  auto registerName = x.path();
1606  VersionNumber someVersion{nullptr};
1607 
1608  std::cout << "... registerName = " << registerName << std::endl;
1609  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName, 0, 0, {AccessMode::wait_for_new_data});
1610  // a helper to the same register to make sure the sending thread has distributed into the queues before
1611  // sending the next data
1612  auto helper = d.getTwoDRegisterAccessor<UserType>(registerName, 0, 0, {AccessMode::wait_for_new_data});
1613 
1614  // Initial value (stays unchecked here)
1615  reg.read();
1616  usleep(100000); // give potential race conditions a chance to pop up more easily...
1617  BOOST_CHECK(reg.readNonBlocking() == false);
1618 
1619  // Set remote value to be read.
1620  x.setRemoteValue();
1621  auto v1 = x.template getRemoteValue<UserType>();
1622 
1623  // Read the value
1624  reg.read();
1625  usleep(100000); // give potential race conditions a chance to pop up more easily...
1626  BOOST_CHECK(reg.readNonBlocking() == false);
1627 
1628  // Check application buffer
1629  CHECK_EQUALITY(reg, v1);
1630  BOOST_CHECK(reg.dataValidity() == DataValidity::ok);
1631  BOOST_CHECK(reg.getVersionNumber() > someVersion);
1632  someVersion = reg.getVersionNumber();
1633 
1634  // Clear the queue of the helper so it afterwards will block until the data from the next setRemoteValue() has arrived
1635  helper.readLatest();
1636 
1637  // Set multiple remote values in a row - they will be queued
1638  x.setRemoteValue();
1639  auto v2 = x.template getRemoteValue<UserType>();
1640  helper.read();
1641 
1642  x.setRemoteValue();
1643  auto v3 = x.template getRemoteValue<UserType>();
1644  helper.read();
1645 
1646  x.setRemoteValue();
1647  auto v4 = x.template getRemoteValue<UserType>();
1648  helper.read();
1649 
1650  // Read and check second value
1651  reg.read();
1652  CHECK_EQUALITY(reg, v2);
1653  BOOST_CHECK(reg.dataValidity() == DataValidity::ok);
1654  BOOST_CHECK(reg.getVersionNumber() > someVersion);
1655  someVersion = reg.getVersionNumber();
1656 
1657  // Read and check third value
1658  reg.read();
1659  CHECK_EQUALITY(reg, v3);
1660  BOOST_CHECK(reg.dataValidity() == DataValidity::ok);
1661  BOOST_CHECK(reg.getVersionNumber() > someVersion);
1662  someVersion = reg.getVersionNumber();
1663 
1664  // Read and check fourth value
1665  reg.read();
1666  CHECK_EQUALITY(reg, v4);
1667  BOOST_CHECK(reg.dataValidity() == DataValidity::ok);
1668  BOOST_CHECK(reg.getVersionNumber() > someVersion);
1669  someVersion = reg.getVersionNumber();
1670 
1671  // No more data available
1672  BOOST_CHECK(reg.readNonBlocking() == false);
1673  CHECK_EQUALITY(reg, v4); // application buffer is unchanged (SPEC???)
1674  BOOST_CHECK(reg.dataValidity() == DataValidity::ok);
1675  BOOST_CHECK(reg.getVersionNumber() == someVersion);
1676  });
1677 
1678  // close device again
1679  d.close();
1680  }
1681 
1682  /********************************************************************************************************************/
1683 
1692  template<typename VECTOR_OF_REGISTERS_T>
1694  std::cout << "--- test_B_8_2_1 - _readQueue overrun" << std::endl;
1695  Device d(cdd);
1696 
1697  // open the device
1698  d.open();
1699 
1700  // Activate async read
1701  d.activateAsyncRead();
1702 
1703  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
1704  if(!this->isAsyncRead(x)) return;
1705  typedef typename decltype(x)::minimumUserType UserType;
1706  auto registerName = x.path();
1707  VersionNumber someVersion{nullptr};
1708 
1709  std::cout << "... registerName = " << registerName << std::endl;
1710  auto overrunningReg = d.getTwoDRegisterAccessor<UserType>(registerName, 0, 0, {AccessMode::wait_for_new_data});
1711  // same register as the overrunningReg. They are both filled by the same thread, so we know that data has been
1712  // pushed into the queue where we want to force an overrun
1713  auto referenceReg = d.getTwoDRegisterAccessor<UserType>(registerName, 0, 0, {AccessMode::wait_for_new_data});
1714 
1715  // Initial value (stays unchecked here)
1716  overrunningReg.read();
1717  referenceReg.read();
1718  usleep(100000); // give potential race conditions a chance to pop up more easily...
1719  BOOST_CHECK(overrunningReg.readNonBlocking() == false);
1720 
1721  // Provoke queue overflow by filling many values. We are only interested in the last one.
1722  for(size_t i = 0; i < 10; ++i) {
1723  x.setRemoteValue();
1724  // wait until it arrives in the reference reg so we are sure the other queue is actually overrunning
1725  referenceReg.read();
1726  }
1727  auto val = x.template getRemoteValue<UserType>();
1728 
1729  // Read last written value (B.8.2.1)
1730  BOOST_CHECK(overrunningReg.readLatest() == true);
1731  // Use check with timeout. It might be that the referenceReg was filled, but the overrunning reg not yet because
1732  // the thread was scheduled out
1733  CHECK_EQUALITY_TIMEOUT(overrunningReg, val, 10000);
1734  BOOST_CHECK(overrunningReg.dataValidity() == DataValidity::ok);
1735  BOOST_CHECK(overrunningReg.getVersionNumber() > someVersion);
1736  someVersion = overrunningReg.getVersionNumber();
1737  });
1738 
1739  // close device again
1740  d.close();
1741  }
1742 
1743  /********************************************************************************************************************/
1744 
1749  template<typename VECTOR_OF_REGISTERS_T>
1751  std::cout << "--- test_B_8_3 - new runtime errors are put to _readQueue in async reads" << std::endl;
1752  Device d(cdd);
1753  d.open();
1754 
1755  // Activate async read
1756  d.activateAsyncRead();
1757 
1758  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
1759  if(!this->isAsyncRead(x)) return;
1760  typedef typename decltype(x)::minimumUserType UserType;
1761  auto registerName = x.path();
1762  VersionNumber someVersion{nullptr};
1763 
1764  std::cout << "... registerName = " << registerName << std::endl;
1765 
1766  // obtain accessor for the test
1767  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName, 0, 0, {AccessMode::wait_for_new_data});
1768 
1769  for(size_t i = 0; i < x.nRuntimeErrorCases(); ++i) {
1770  std::cout << " -> runtime_error case: " << i << std::endl;
1771  // read initial value
1772  reg.read();
1773 
1774  // execute preRead without exception state
1775  reg.getHighLevelImplElement()->preRead(TransferType::read);
1776 
1777  // enable exceptions on read
1778  x.setForceRuntimeError(true, i);
1779 
1780  // Check for runtime_error as it is popped of the queue
1781  BOOST_CHECK_THROW(reg.getHighLevelImplElement()->readTransfer(), ChimeraTK::runtime_error);
1782 
1783  // Need to report the exception to the exception backend, because this is normally done in
1784  // TransferElement::read() etc.
1785  d.setException("Some message");
1786 
1787  // complete the operation
1788  reg.getHighLevelImplElement()->postRead(TransferType::read, false);
1789 
1790  // disable exceptions on read
1791  x.setForceRuntimeError(false, i);
1792 
1793  // recover
1794  this->recoverDevice(d);
1795  // Activate async read again
1796  d.activateAsyncRead();
1797  }
1798  });
1799 
1800  // close device again
1801  d.close();
1802  }
1803 
1804  /********************************************************************************************************************/
1805 
1810  template<typename VECTOR_OF_REGISTERS_T>
1812  if(_testOnlyTransferElement) return;
1813  std::cout << "--- test_B_8_4 - async read consistency heartbeat" << std::endl;
1814 
1815  Device d(cdd);
1816 
1817  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
1818  if(!this->isAsyncRead(x) || x.capabilities.asyncReadInconsistency != TestCapability::enabled) return;
1819  typedef typename decltype(x)::minimumUserType UserType;
1820  auto registerName = x.path();
1821  VersionNumber someVersion{nullptr};
1822 
1823  std::cout << "... registerName = " << registerName << std::endl;
1824 
1825  // open the device
1826  d.open();
1827 
1828  // Activate async read
1829  d.activateAsyncRead();
1830 
1831  // Set remote value to be read.
1832  x.setRemoteValue();
1833  auto v1 = x.template getRemoteValue<UserType>();
1834 
1835  // Obtain accessor
1836  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName, 0, 0, {AccessMode::wait_for_new_data});
1837 
1838  // Read and check initial value
1839  reg.read();
1840  CHECK_EQUALITY(reg, v1);
1841  BOOST_CHECK(reg.dataValidity() == DataValidity::ok);
1842  BOOST_CHECK(reg.getVersionNumber() > someVersion);
1843  someVersion = reg.getVersionNumber();
1844 
1845  // Provoke inconsistency
1846  this->forceAsyncReadInconsistency(x);
1847 
1848  // Wait for the exception which informs about the problem
1849  BOOST_CHECK_THROW(reg.read(), ChimeraTK::runtime_error);
1850 
1851  // Recover the device
1852  this->recoverDevice(d);
1853  auto v2 = x.template getRemoteValue<UserType>();
1854 
1855  // Activate async read again
1856  d.activateAsyncRead();
1857 
1858  // Read and check value
1859  reg.read();
1860  CHECK_EQUALITY(reg, v2);
1861  BOOST_CHECK(reg.dataValidity() == DataValidity::ok);
1862  BOOST_CHECK(reg.getVersionNumber() > someVersion);
1863  someVersion = reg.getVersionNumber();
1864 
1865  // close device again
1866  d.close();
1867  });
1868  }
1869  /********************************************************************************************************************/
1870 
1876  template<typename VECTOR_OF_REGISTERS_T>
1878  if(_testOnlyTransferElement) return;
1879  std::cout << "--- test_B_8_5 - no async transfers until activateAsyncRead()" << std::endl;
1880  Device d(cdd);
1881 
1882  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
1883  if(!this->isAsyncRead(x)) return;
1884  typedef typename decltype(x)::minimumUserType UserType;
1885  auto registerName = x.path();
1886  std::cout << "... registerName = " << registerName << std::endl;
1887 
1888  // First step: measure time until initial value arrives, so we know how long to wait to exclude that an initial
1889  // value arrives wrongly.
1890  std::chrono::duration<double> timeToInitialValue{};
1891  {
1892  // start time measurement
1893  auto t0 = std::chrono::steady_clock::now();
1894 
1895  // open the device
1896  d.open();
1897  d.activateAsyncRead();
1898 
1899  // obtain accessor
1900  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName, 0, 0, {AccessMode::wait_for_new_data});
1901 
1902  // make a successful read (initial value) to make sure the exception state is gone
1903  reg.read();
1904 
1905  // finish time measurement
1906  auto t1 = std::chrono::steady_clock::now();
1907  timeToInitialValue = t1 - t0;
1908 
1909  // close device again
1910  d.close();
1911  }
1912 
1913  // Second step: Check if no data arrives without activateAsyncRead()
1914  {
1915  // open the device, but don't call activateAsyncRead() yet
1916  d.open();
1917  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName, 0, 0, {AccessMode::wait_for_new_data});
1918 
1919  // wait 2 times longer than the time until initial value was received before
1920  std::this_thread::sleep_for(timeToInitialValue * 2);
1921 
1922  // no value must have arrived
1923  BOOST_CHECK(reg.readNonBlocking() == false);
1924 
1925  // Check again for possible side effects (automatic subscription on read) of reg.readNonBlocking()
1926  // wait 2 times longer than the time until initial value was received before
1927  std::this_thread::sleep_for(timeToInitialValue * 2);
1928 
1929  // no value must have arrived
1930  BOOST_CHECK(reg.readNonBlocking() == false);
1931 
1932  // close device again
1933  d.close();
1934  }
1935  });
1936  }
1937 
1938  /********************************************************************************************************************/
1939 
1944  template<typename VECTOR_OF_REGISTERS_T>
1946  if(_testOnlyTransferElement) return;
1947  std::cout << "--- test_B_8_5_1 - activateAsynchronousRead" << std::endl;
1948  Device d(cdd);
1949  Device d2;
1950  if(!cdd2.empty()) {
1951  d2.open(cdd2);
1952  d2.close();
1953  }
1954 
1955  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
1956  if(!this->isAsyncRead(x)) return;
1957  typedef typename decltype(x)::minimumUserType UserType;
1958  auto registerName = x.path();
1959  std::cout << "... registerName = " << registerName << std::endl;
1960  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName, 0, 0, {AccessMode::wait_for_new_data});
1961 
1963  if(!cdd2.empty()) {
1964  d2.open();
1965  reg2.replace(d2.getTwoDRegisterAccessor<UserType>(registerName, 0, 0, {AccessMode::wait_for_new_data}));
1966  if(!cdd2.empty()) BOOST_CHECK(reg2.readNonBlocking() == false);
1967  }
1968 
1969  // Set remote value to be read.
1970  x.setRemoteValue();
1971  auto v1 = x.template getRemoteValue<UserType>();
1972 
1973  // open the device
1974  d.open();
1975  if(!cdd2.empty()) BOOST_CHECK(reg2.readNonBlocking() == false);
1976 
1977  // Activate async read
1978  d.activateAsyncRead();
1979 
1980  // Read initial value
1981  reg.read();
1982 
1983  // Check application buffer
1984  CHECK_EQUALITY(reg, v1);
1985 
1986  if(!cdd2.empty()) {
1987  // wait a bit, check that accessor of second device does not receive data
1988  using namespace std::chrono_literals;
1989  std::this_thread::sleep_for(10ms);
1990  BOOST_CHECK(reg2.readNonBlocking() == false);
1991 
1992  // activate async read on second device and check again
1993  d2.activateAsyncRead();
1994  reg2.read();
1995  CHECK_EQUALITY(reg2, v1);
1996  }
1997 
1998  // close device again
1999  d.close();
2000  });
2001  }
2002 
2003  /********************************************************************************************************************/
2004 
2009  template<typename VECTOR_OF_REGISTERS_T>
2011  if(_testOnlyTransferElement) return;
2012  std::cout << "--- test_B_8_5_2 - initial value" << std::endl;
2013  Device d(cdd);
2014  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
2015  if(!this->isAsyncRead(x)) return;
2016  typedef typename decltype(x)::minimumUserType UserType;
2017  auto registerName = x.path();
2018  std::cout << "... registerName = " << registerName << std::endl;
2019 
2020  // First check: initial value is correctly arriving
2021  {
2022  // Set remote value to be read.
2023  x.setRemoteValue();
2024  auto v1 = x.template getRemoteValue<UserType>();
2025 
2026  // open the device and activate async read
2027  d.open();
2028  d.activateAsyncRead();
2029 
2030  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName, 0, 0, {AccessMode::wait_for_new_data});
2031 
2032  // Read initial value
2033  reg.read();
2034 
2035  // Check application buffer
2036  CHECK_EQUALITY(reg, v1);
2037 
2038  // close device again
2039  d.close();
2040  }
2041 
2042  // Second check: Concurrent updates do not cause inconsistency. Note: This test cannot possibly cover all
2043  // potential scenarios for race conditions, hence only one simple scenario is tested.
2044  {
2045  // Set initial remote value, to make sure it is different from the next remote value set below
2046  x.setRemoteValue();
2047 
2048  // open the device and activate async read
2049  d.open();
2050  d.activateAsyncRead();
2051 
2052  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName, 0, 0, {AccessMode::wait_for_new_data});
2053 
2054  // Concurrently set another remote value to be read (while presumably the subscription is still being made).
2055  x.setRemoteValue();
2056  auto v2 = x.template getRemoteValue<UserType>();
2057 
2058  // Check that the second value arrives at some point (with timeout)
2059  CHECK_EQUALITY_TIMEOUT(reg, v2, 30000);
2060 
2061  // close device again
2062  d.close();
2063  }
2064  });
2065  }
2066 
2067  /********************************************************************************************************************/
2068 
2073  template<typename VECTOR_OF_REGISTERS_T>
2075  std::cout << "--- test_B_8_5_3 - accessors created after activateAsyncRead() are immediately active" << std::endl;
2076  Device d(cdd);
2077 
2078  // open the device and activate async read
2079  d.open();
2080  d.activateAsyncRead();
2081 
2082  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
2083  if(!this->isAsyncRead(x)) return;
2084  typedef typename decltype(x)::minimumUserType UserType;
2085  auto registerName = x.path();
2086 
2087  std::cout << "... registerName = " << registerName << " (activated async read)" << std::endl;
2088  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName, 0, 0, {AccessMode::wait_for_new_data});
2089 
2090  // Initial value should arrive
2091  CHECK_TIMEOUT(reg.readNonBlocking() == true, 30000);
2092  });
2093 
2094  // close device again
2095  d.close();
2096  }
2097 
2098  /********************************************************************************************************************/
2099 
2104  template<typename VECTOR_OF_REGISTERS_T>
2106  std::cout << "--- test_B_8_6_6 - interrupt()" << std::endl;
2107 
2108  Device d(cdd);
2109  auto backend = d.getBackend();
2110  d.open();
2111 
2112  // Activate async read
2113  d.activateAsyncRead();
2114 
2115  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
2116  if(!this->isAsyncRead(x)) return;
2117  typedef typename decltype(x)::minimumUserType UserType;
2118  auto registerName = x.path();
2119  VersionNumber someVersion{nullptr};
2120 
2121  std::cout << "... registerName = " << registerName << std::endl;
2122  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName, 0, 0, {AccessMode::wait_for_new_data});
2123  reg.read(); // initial value
2124 
2125  for(size_t i = 0; i < 2; ++i) {
2126  // execute blocking read in another thread
2127  boost::thread anotherThread([&] {
2128  reg.read();
2129  BOOST_ERROR("boost::thread_interrupt exception expected but not thrown.");
2130  });
2131 
2132  // interrupt the blocking operation
2133  reg.getHighLevelImplElement()->interrupt();
2134 
2135  // make sure the other thread can terminate
2136  anotherThread.join();
2137 
2138  // check accessor is still working
2139  x.setRemoteValue();
2140  auto v1 = x.template getRemoteValue<UserType>();
2141  reg.read();
2142  CHECK_EQUALITY(reg, v1);
2143  BOOST_CHECK(reg.dataValidity() == DataValidity::ok);
2144  BOOST_CHECK(reg.getVersionNumber() > someVersion);
2145  someVersion = reg.getVersionNumber();
2146  }
2147  });
2148 
2149  d.close();
2150  }
2151 
2152  /********************************************************************************************************************/
2153 
2159  template<typename VECTOR_OF_REGISTERS_T>
2161  if(_testOnlyTransferElement) return;
2162  std::cout << "--- test_B_9_1 - reporting exceptions to exception backend" << std::endl;
2163  Device d(cdd);
2164 
2165  // open the device, then let it throw runtime_error exceptions
2166  d.open();
2167 
2168  std::cout << "... synchronous read" << std::endl;
2169  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
2170  if(!this->isRead(x)) return;
2171  if(x.nRuntimeErrorCases() == 0) return;
2172  typedef typename decltype(x)::minimumUserType UserType;
2173  auto registerName = x.path();
2174  std::cout << " registerName = " << registerName << std::endl;
2175  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName);
2176 
2177  // set exception reporting backend
2178  auto erb = boost::make_shared<ExceptionReportingBackend>(d.getBackend());
2179  reg.getHighLevelImplElement()->setExceptionBackend(erb);
2180 
2181  for(size_t i = 0; i < x.nRuntimeErrorCases(); ++i) {
2182  std::cout << " -> runtime_error case: " << i << std::endl;
2183  // enable exceptions on read
2184  x.setForceRuntimeError(true, i);
2185 
2186  // Runtime error should be reported via setException()
2187  BOOST_CHECK(!erb->hasSeenException());
2188  BOOST_CHECK_THROW(reg.read(), runtime_error);
2189  BOOST_CHECK(erb->hasSeenException());
2190 
2191  // disable exceptions on read
2192  x.setForceRuntimeError(false, i);
2193 
2194  // recover
2195  this->recoverDevice(d);
2196 
2197  // make a successful read to make sure the exception state is gone. no reporting must take place here.
2198  BOOST_CHECK_NO_THROW(reg.read());
2199  BOOST_CHECK(!erb->hasSeenException());
2200  }
2201  });
2202 
2203  std::cout << "... asynchronous read" << std::endl;
2204  d.activateAsyncRead();
2205  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
2206  if(!this->isAsyncRead(x)) return;
2207  if(x.nRuntimeErrorCases() == 0) return;
2208  typedef typename decltype(x)::minimumUserType UserType;
2209  auto registerName = x.path();
2210  std::cout << " registerName = " << registerName << std::endl;
2211  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName, 0, 0, {AccessMode::wait_for_new_data});
2212  reg.read(); // initial value
2213 
2214  // set exception reporting backend
2215  auto erb = boost::make_shared<ExceptionReportingBackend>(d.getBackend());
2216  reg.getHighLevelImplElement()->setExceptionBackend(erb);
2217 
2218  for(size_t i = 0; i < x.nRuntimeErrorCases(); ++i) {
2219  std::cout << " -> runtime_error case: " << i << std::endl;
2220  // enable exceptions on read
2221  x.setForceRuntimeError(true, i);
2222 
2223  // Runtime error should be reported via setException()
2224  BOOST_CHECK(!erb->hasSeenException());
2225  BOOST_CHECK_THROW(reg.read(), runtime_error);
2226  BOOST_CHECK(erb->hasSeenException());
2227 
2228  // disable exceptions on read
2229  x.setForceRuntimeError(false, i);
2230 
2231  // recover
2232  this->recoverDevice(d);
2233  d.activateAsyncRead(); // turn async read back on
2234 
2235  // make a successful readNonBlocking (no data) to make sure the exception state is gone. no reporting must take
2236  // place here.
2237  BOOST_CHECK_NO_THROW(reg.readNonBlocking());
2238  BOOST_CHECK(!erb->hasSeenException());
2239  }
2240  });
2241 
2242  std::cout << "... write" << std::endl;
2243  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
2244  if(!this->isWrite(x)) return;
2245  if(x.nRuntimeErrorCases() == 0) return;
2246  typedef typename decltype(x)::minimumUserType UserType;
2247  auto registerName = x.path();
2248  std::cout << " registerName = " << registerName << std::endl;
2249  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName);
2250 
2251  // set exception reporting backend
2252  auto erb = boost::make_shared<ExceptionReportingBackend>(d.getBackend());
2253  reg.getHighLevelImplElement()->setExceptionBackend(erb);
2254 
2255  for(size_t i = 0; i < x.nRuntimeErrorCases(); ++i) {
2256  std::cout << " -> runtime_error case: " << i << std::endl;
2257  // enable exceptions on write
2258  x.setForceRuntimeError(true, i);
2259 
2260  // Runtime error should be reported via setException()
2261  BOOST_CHECK(!erb->hasSeenException());
2262  BOOST_CHECK_THROW(reg.write(), runtime_error);
2263  BOOST_CHECK(erb->hasSeenException());
2264 
2265  // disable exceptions on write
2266  x.setForceRuntimeError(false, i);
2267 
2268  // recover
2269  this->recoverDevice(d);
2270 
2271  // make a successful write to make sure the exception state is gone. no reporting must take place here.
2272  BOOST_CHECK_NO_THROW(reg.write());
2273  BOOST_CHECK(!erb->hasSeenException());
2274  }
2275  });
2276 
2277  std::cout << "... isReadable" << std::endl;
2278  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
2279  if(x.nRuntimeErrorCases() == 0) return;
2280  typedef typename decltype(x)::minimumUserType UserType;
2281  auto registerName = x.path();
2282  std::cout << " registerName = " << registerName;
2283  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName);
2284 
2285  // set exception reporting backend
2286  auto erb = boost::make_shared<ExceptionReportingBackend>(d.getBackend());
2287  reg.getHighLevelImplElement()->setExceptionBackend(erb);
2288 
2289  bool didThrow = false;
2290  for(size_t i = 0; i < x.nRuntimeErrorCases(); ++i) {
2291  std::cout << " -> runtime_error case: " << i << std::endl;
2292  // enable exceptions on write
2293  x.setForceRuntimeError(true, i);
2294 
2295  // Runtime error should be reported via setException()
2296  BOOST_CHECK(!erb->hasSeenException());
2297  try {
2298  [[maybe_unused]] auto result = reg.isReadable();
2299  }
2300  catch(...) {
2301  didThrow = true;
2302  BOOST_CHECK(erb->hasSeenException());
2303  }
2304 
2305  // disable exceptions on write
2306  x.setForceRuntimeError(false, i);
2307 
2308  // recover
2309  this->recoverDevice(d);
2310  }
2311 
2312  if(!didThrow) {
2313  std::cout << " (doesn't throw)" << std::endl;
2314  }
2315  else {
2316  std::cout << " (throws)" << std::endl;
2317  }
2318  });
2319 
2320  std::cout << "... isWriteable" << std::endl;
2321  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
2322  if(x.nRuntimeErrorCases() == 0) return;
2323  typedef typename decltype(x)::minimumUserType UserType;
2324  auto registerName = x.path();
2325  std::cout << " registerName = " << registerName;
2326  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName);
2327 
2328  // set exception reporting backend
2329  auto erb = boost::make_shared<ExceptionReportingBackend>(d.getBackend());
2330  reg.getHighLevelImplElement()->setExceptionBackend(erb);
2331 
2332  bool didThrow = false;
2333  for(size_t i = 0; i < x.nRuntimeErrorCases(); ++i) {
2334  std::cout << " -> runtime_error case: " << i << std::endl;
2335  // enable exceptions on write
2336  x.setForceRuntimeError(true, i);
2337 
2338  // Runtime error should be reported via setException()
2339  BOOST_CHECK(!erb->hasSeenException());
2340  try {
2341  [[maybe_unused]] auto result = reg.isWriteable();
2342  }
2343  catch(...) {
2344  didThrow = true;
2345  BOOST_CHECK(erb->hasSeenException());
2346  }
2347 
2348  // disable exceptions on write
2349  x.setForceRuntimeError(false, i);
2350 
2351  // recover
2352  this->recoverDevice(d);
2353  }
2354 
2355  if(!didThrow) {
2356  std::cout << " (doesn't throw)" << std::endl;
2357  }
2358  else {
2359  std::cout << " (throws)" << std::endl;
2360  }
2361  });
2362 
2363  std::cout << "... isReadOnly" << std::endl;
2364  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
2365  typedef typename decltype(x)::minimumUserType UserType;
2366  auto registerName = x.path();
2367  std::cout << " registerName = " << registerName;
2368  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName);
2369 
2370  // set exception reporting backend
2371  auto erb = boost::make_shared<ExceptionReportingBackend>(d.getBackend());
2372  reg.getHighLevelImplElement()->setExceptionBackend(erb);
2373 
2374  bool didThrow = false;
2375  for(size_t i = 0; i < x.nRuntimeErrorCases(); ++i) {
2376  std::cout << " -> runtime_error case: " << i << std::endl;
2377  // enable exceptions on write
2378  x.setForceRuntimeError(true, i);
2379 
2380  // Runtime error should be reported via setException()
2381  BOOST_CHECK(!erb->hasSeenException());
2382  try {
2383  [[maybe_unused]] auto result = reg.isReadOnly();
2384  }
2385  catch(...) {
2386  didThrow = true;
2387  BOOST_CHECK(erb->hasSeenException());
2388  }
2389 
2390  // disable exceptions on write
2391  x.setForceRuntimeError(false, i);
2392 
2393  // recover
2394  this->recoverDevice(d);
2395  }
2396 
2397  if(!didThrow) {
2398  std::cout << " (doesn't throw)" << std::endl;
2399  }
2400  else {
2401  std::cout << " (throws)" << std::endl;
2402  }
2403  });
2404  // close device again
2405  d.close();
2406  }
2407 
2408  /********************************************************************************************************************/
2409 
2414  template<typename VECTOR_OF_REGISTERS_T>
2416  if(_testOnlyTransferElement) return;
2417  std::cout << "--- test_B_9_2_2 - repeated setException() has no effect" << std::endl;
2418  Device d(cdd);
2419  d.open();
2420  d.activateAsyncRead();
2421 
2422  // obtain accessors and read initial value
2423  std::list<TransferElementAbstractor> accessors;
2424  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
2425  if(!this->isAsyncRead(x)) return;
2426  typedef typename decltype(x)::minimumUserType UserType;
2427  auto registerName = x.path();
2428  std::cout << "... registerName = " << registerName << std::endl;
2429 
2430  // obtain accessor for the test
2431  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName, 0, 0, {AccessMode::wait_for_new_data});
2432  accessors.push_back(reg);
2433 
2434  // read initial value
2435  reg.read();
2436  });
2437 
2438  // enter exception state
2439  d.setException("Some message");
2440 
2441  // each accessor has now an exception in the queue -> remove from queue
2442  for(auto& accessor : accessors) {
2443  BOOST_CHECK_THROW(accessor.read(), runtime_error); // (no test intended, just catch)
2444  }
2445 
2446  // call setException repeatedly
2447  d.setException("Some message");
2448  d.setException("Some message");
2449 
2450  // give potential race conditions a chance...
2451  usleep(10000);
2452 
2453  // each accessor still must not have any more exceptions in the queue
2454  for(auto& accessor : accessors) {
2455  BOOST_CHECK(accessor.readNonBlocking() == false);
2456  }
2457 
2458  // close device again
2459  d.close();
2460  }
2461 
2462  /********************************************************************************************************************/
2463 
2468  template<typename VECTOR_OF_REGISTERS_T>
2470  if(_testOnlyTransferElement) return;
2471  std::cout << "--- test_B_9_3_1 - setException() disables asynchronous read transfers" << std::endl;
2472  Device d(cdd);
2473  d.open();
2474  d.activateAsyncRead();
2475 
2476  // obtain accessors and read initial value
2477  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
2478  if(!this->isAsyncRead(x)) return;
2479  typedef typename decltype(x)::minimumUserType UserType;
2480  auto registerName = x.path();
2481  std::cout << "... registerName = " << registerName << std::endl;
2482 
2483  // obtain accessor for the test
2484  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName, 0, 0, {AccessMode::wait_for_new_data});
2485 
2486  // read initial value
2487  reg.read();
2488 
2489  // enter exception state
2490  d.setException("Some message");
2491 
2492  // get the exception off the queue. From this point on no other data must be received.
2493  BOOST_CHECK_THROW(reg.read(), runtime_error); // (no test intended, just catch)
2494 
2495  // send value, must not be received
2496  x.setRemoteValue();
2497 
2498  // give potential race conditions a chance...
2499  usleep(100000);
2500 
2501  // no value expected
2502  BOOST_CHECK(reg.readNonBlocking() == false);
2503 
2504  // recover device
2505  this->recoverDevice(d);
2506  d.activateAsyncRead(); // re-activate async read after recovery
2507  });
2508 
2509  // close device again
2510  d.close();
2511  }
2512 
2513  /********************************************************************************************************************/
2514 
2519  template<typename VECTOR_OF_REGISTERS_T>
2521  if(_testOnlyTransferElement) return;
2522  std::cout << "--- test_B_9_3_2 - exactly one runtime_error in the _readQueue per async read accessor" << std::endl;
2523  Device d(cdd);
2524  d.open();
2525  d.activateAsyncRead();
2526 
2527  // obtain accessors and read initial value
2528  std::list<TransferElementAbstractor> accessors;
2529  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
2530  if(!this->isAsyncRead(x)) return;
2531  typedef typename decltype(x)::minimumUserType UserType;
2532  auto registerName = x.path();
2533  std::cout << "... registerName = " << registerName << std::endl;
2534 
2535  // obtain accessor for the test
2536  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName, 0, 0, {AccessMode::wait_for_new_data});
2537  accessors.push_back(reg);
2538 
2539  // read initial value
2540  reg.read();
2541  });
2542 
2543  // enter exception state
2544  d.setException("Some message");
2545 
2546  usleep(10000); // give potential race conditions a chance...
2547 
2548  // each accessor must have exactly one exception in the queue
2549  for(auto& accessor : accessors) {
2550  // call the read stages explicitly to make sure the exception is thrown in the right place
2551  accessor.getHighLevelImplElement()->preRead(TransferType::read);
2552  BOOST_CHECK_THROW(accessor.getHighLevelImplElement()->readTransfer(), runtime_error);
2553  accessor.getHighLevelImplElement()->postRead(TransferType::read, false);
2554  // no more exceptions in the queue are allowed
2555  BOOST_CHECK(accessor.readNonBlocking() == false);
2556  }
2557 
2558  // close device again
2559  d.close();
2560  }
2561 
2562  /********************************************************************************************************************/
2563 
2568  template<typename VECTOR_OF_REGISTERS_T>
2570  if(_testOnlyTransferElement) return;
2571  std::cout
2572  << "--- test_B_9_4_1 - doReadTransferSynchronously throws runtime_error after setException() until recovery"
2573  << std::endl;
2574  Device d(cdd);
2575  d.open();
2576 
2577  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
2578  if(!this->isRead(x)) return;
2579  typedef typename decltype(x)::minimumUserType UserType;
2580  auto registerName = x.path();
2581  std::cout << "... registerName = " << registerName << std::endl;
2582  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName);
2583 
2584  // put backend into exception state
2585  d.setException("Some message");
2586 
2587  // Check for runtime_error where it is now expected
2588  BOOST_CHECK_THROW(reg.read(), runtime_error);
2589 
2590  // recover
2591  this->recoverDevice(d);
2592 
2593  // make a successful read to make sure the exception state is gone
2594  BOOST_CHECK_NO_THROW(reg.read());
2595  });
2596 
2597  d.close();
2598  }
2599 
2600  /********************************************************************************************************************/
2601 
2606  template<typename VECTOR_OF_REGISTERS_T>
2608  if(_testOnlyTransferElement) return;
2609  std::cout << "--- test_B_9_5 - write operations throw after setException()" << std::endl;
2610  Device d(cdd);
2611  d.open();
2612 
2613  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
2614  if(!this->isWrite(x)) return;
2615  typedef typename decltype(x)::minimumUserType UserType;
2616  auto registerName = x.path();
2617  std::cout << "... registerName = " << registerName << std::endl;
2618  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName);
2619 
2620  // put backend into exception state
2621  d.setException("Some message");
2622 
2623  // Check for runtime_error where it is now expected
2624  BOOST_CHECK_THROW(reg.write(), runtime_error);
2625 
2626  // recover
2627  this->recoverDevice(d);
2628 
2629  // make a successful read to make sure the exception state is gone
2630  BOOST_CHECK_NO_THROW(reg.write());
2631  });
2632 
2633  d.close();
2634  }
2635 
2636  /********************************************************************************************************************/
2637 
2642  template<typename VECTOR_OF_REGISTERS_T>
2644  std::cout << "--- test_B_11_2_1 - version number bigger for newer values" << std::endl;
2645  Device d(cdd);
2646 
2647  // open the device
2648  d.open();
2649 
2650  // synchronous read
2651  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
2652  if(!this->isRead(x)) return;
2653  if(x.capabilities.setRemoteValueIncrementsVersion == TestCapability::disabled) return;
2654  typedef typename decltype(x)::minimumUserType UserType;
2655  auto registerName = x.path();
2656  VersionNumber someVersion{nullptr};
2657 
2658  std::cout << "... registerName = " << registerName << std::endl;
2659  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName);
2660 
2661  for(size_t i = 0; i < 2; ++i) {
2662  // Set remote value to be read.
2663  x.setRemoteValue();
2664 
2665  // Read value
2666  reg.read();
2667 
2668  // Check application buffer
2669  BOOST_CHECK(reg.getVersionNumber() > someVersion);
2670  someVersion = reg.getVersionNumber();
2671  }
2672  });
2673 
2674  // asynchronous read
2675  d.activateAsyncRead();
2676  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
2677  if(!this->isAsyncRead(x)) return;
2678  typedef typename decltype(x)::minimumUserType UserType;
2679  auto registerName = x.path();
2680 
2681  std::cout << "... registerName = " << registerName << " (async)" << std::endl;
2682  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName, 0, 0, {AccessMode::wait_for_new_data});
2683 
2684  reg.read(); // initial value
2685  VersionNumber someVersion = reg.getVersionNumber();
2686 
2687  // Backends do not guarantee that the same data and version number are not send twice. Especially for the polled
2688  // initial value there is the intrinsic race condition that the subscription and the according pull are happening
2689  // concurrently and the data and version number are seen twice.
2690  // For the polling backends there is no guarantee that the poll and the data are really matching, so it
2691  // might be that the same data is seen twice for different version numbers.
2692  //
2693  // What is guaranteed if we read after each setRemoteValue(): Once we see the new data, it has to have a new
2694  // version number.
2695 
2696  for(size_t i = 0; i < 2; ++i) {
2697  x.setRemoteValue();
2698  auto val = x.template getRemoteValue<UserType>();
2699  CHECK_EQUALITY_TIMEOUT(reg, val, 30000);
2700  BOOST_TEST(reg.getVersionNumber() > someVersion);
2701  someVersion = reg.getVersionNumber();
2702  }
2703  });
2704 
2705  // close device
2706  d.close();
2707  }
2708 
2709  /********************************************************************************************************************/
2710 
2718  template<typename VECTOR_OF_REGISTERS_T>
2720  std::cout << "--- test_NOSPEC_newVersionsAfterOpen - version numbers after open() are newer" << std::endl;
2721  Device d(cdd);
2722 
2723  // Application can create version numbers any time.
2724  VersionNumber someVersion{};
2725 
2726  // Open the device. All versions from the backend must be newer than someVersion from now on.
2727  d.open();
2728 
2729  // synchronous read
2730  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
2731  if(!this->isRead(x)) return;
2732  typedef typename decltype(x)::minimumUserType UserType;
2733  auto registerName = x.path();
2734 
2735  std::cout << "... registerName = " << registerName << std::endl;
2736  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName);
2737 
2738  // Set remote value to be read.
2739  x.setRemoteValue();
2740 
2741  // Read value
2742  reg.read();
2743 
2744  // Check application buffer
2745  BOOST_CHECK(reg.getVersionNumber() > someVersion);
2746  });
2747 
2748  // asynchronous read 1: activate before creating accessor
2749  d.activateAsyncRead();
2750  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
2751  if(!this->isAsyncRead(x)) return;
2752  typedef typename decltype(x)::minimumUserType UserType;
2753  auto registerName = x.path();
2754 
2755  std::cout << "... registerName = " << registerName << " (async1)" << std::endl;
2756 
2757  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName, 0, 0, {AccessMode::wait_for_new_data});
2758 
2759  reg.read();
2760 
2761  // Check application buffer
2762  BOOST_CHECK(reg.getVersionNumber() > someVersion);
2763  });
2764 
2765  // close device
2766  d.close();
2767 
2768  // asynchronous read 2: activate after creating accessor
2769  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
2770  if(!this->isAsyncRead(x)) return;
2771  typedef typename decltype(x)::minimumUserType UserType;
2772  auto registerName = x.path();
2773 
2774  someVersion = {};
2775  d.open();
2776 
2777  std::cout << "... registerName = " << registerName << " (async2)" << std::endl;
2778 
2779  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName, 0, 0, {AccessMode::wait_for_new_data});
2780 
2781  d.activateAsyncRead();
2782 
2783  reg.read();
2784 
2785  // Check application buffer
2786  BOOST_CHECK(reg.getVersionNumber() > someVersion);
2787 
2788  d.close();
2789  });
2790  }
2791 
2792  /********************************************************************************************************************/
2793 
2802  template<typename VECTOR_OF_REGISTERS_T>
2804  if(_testOnlyTransferElement) return;
2805  std::cout << "--- test_B_11_2_2 - consistent data gets same VersionNumber" << std::endl;
2806  Device d(cdd);
2807 
2808  // open the device and activate async read
2809  d.open();
2810  d.activateAsyncRead();
2811 
2812  // CASE 1: consistency with the same register in async read
2813  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
2814  if(!this->isAsyncRead(x)) return;
2815  typedef typename decltype(x)::minimumUserType UserType;
2816  auto registerName = x.path();
2817  std::cout << "... registerName = " << registerName << std::endl;
2818 
2819  // Set remote value to be read.
2820  x.setRemoteValue();
2821 
2822  // Obtain accessor
2823  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName, 0, 0, {AccessMode::wait_for_new_data});
2824 
2825  // Read the initial value
2826  reg.read();
2827 
2828  // Read through second accessor
2829  auto reg2 = d.getTwoDRegisterAccessor<UserType>(registerName, 0, 0, {AccessMode::wait_for_new_data});
2830  reg2.read();
2831 
2832  // Version must be identical to the version of the first accessor
2833  // Initial values are not necessarily consistent, so this check is skipped.
2834  // BOOST_CHECK_EQUAL(reg2.getVersionNumber(), reg.getVersionNumber());
2835 
2836  // Change value, must be seen by both accessors, again same version expected
2837  x.setRemoteValue();
2838 
2839  // We have no guarantee that there is only one value in the queue. The setRemoteValue also send the data, and
2840  // depending on the subscription status of reg and reg2 they might see this value in addition to initial value,
2841  // with unknown timing (extra threads and maybe processes involved). We wait until we get the data that was last
2842  // written on both accessors, and then compare the version number.
2843  auto val = x.template getRemoteValue<UserType>();
2844 
2845  CHECK_EQUALITY_TIMEOUT(reg, val, 30000);
2846  CHECK_EQUALITY_TIMEOUT(reg2, val, 30000);
2847  BOOST_CHECK_EQUAL(reg.getVersionNumber(), reg2.getVersionNumber());
2848  });
2849 
2850  // close device again
2851  d.close();
2852  }
2853 
2854  /********************************************************************************************************************/
2855 
2860  template<typename VECTOR_OF_REGISTERS_T>
2862  std::cout << "--- B.11.6 - value after construction for the version number in the application buffer" << std::endl;
2863  Device d(cdd);
2864 
2865  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
2866  typedef typename decltype(x)::minimumUserType UserType;
2867  auto registerName = x.path();
2868  std::cout << "... registerName = " << registerName << std::endl;
2869  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName);
2870 
2871  // check "value after construction" for VersionNumber
2872  BOOST_CHECK(reg.getVersionNumber() == VersionNumber(nullptr));
2873  });
2874  }
2875 
2876  /********************************************************************************************************************/
2877 
2882  template<typename VECTOR_OF_REGISTERS_T>
2884  if(_testOnlyTransferElement) return;
2885  std::cout << "--- test_C_5_2_1_2 - logic_error for non-existing register" << std::endl;
2886 
2887  // Constructor must throw when device is closed
2888  {
2889  Device d(cdd);
2890  BOOST_CHECK_THROW(
2891  auto reg = d.getTwoDRegisterAccessor<int>("This_register_name_does_not_exist_for_sure/whileClosed"),
2892  logic_error);
2893  }
2894 
2895  // Constructor must throw when device is open
2896  {
2897  Device d(cdd);
2898  d.open();
2899  BOOST_CHECK_THROW(
2900  auto reg = d.getTwoDRegisterAccessor<int>("This_register_name_does_not_exist_for_sure/whileOpened"),
2901  logic_error);
2902  d.close();
2903  }
2904  }
2905 
2906  /********************************************************************************************************************/
2907 
2912  template<typename VECTOR_OF_REGISTERS_T>
2914  if(_testOnlyTransferElement) return;
2915  std::cout << "--- test_C_5_2_2_2 - logic_error for exceeding register size" << std::endl;
2916 
2917  // Collect register sizes
2918  std::map<std::string, size_t> sizeMap;
2919  {
2920  Device d(cdd);
2921  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
2922  typedef typename decltype(x)::minimumUserType UserType;
2923  auto registerName = x.path();
2924  std::cout << "... registerName = " << registerName << std::endl;
2925  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName);
2926  sizeMap[registerName] = reg.getNElementsPerChannel();
2927  });
2928  }
2929 
2930  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
2931  typedef typename decltype(x)::minimumUserType UserType;
2932  auto registerName = x.path();
2933  std::cout << "... registerName = " << registerName << std::endl;
2934  // number of elements too big
2935  {
2936  Device d(cdd);
2937  BOOST_CHECK_THROW(
2938  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName, sizeMap[registerName] + 1, 0), logic_error);
2939  }
2940  // one element, but behind the end
2941  {
2942  Device d(cdd);
2943  BOOST_CHECK_THROW(
2944  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName, 1, sizeMap[registerName]), logic_error);
2945  }
2946  // full length but offset by 1 element (so 1 element too long)
2947  {
2948  Device d(cdd);
2949  BOOST_CHECK_THROW(
2950  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName, sizeMap[registerName], 1), logic_error);
2951  }
2952  // full length by default(=0) but offset by 1 element (so 1 element too long)
2953  {
2954  Device d(cdd);
2955  BOOST_CHECK_THROW(auto reg = d.getTwoDRegisterAccessor<UserType>(registerName, 0, 1), logic_error);
2956  }
2957  // does not throw when full length and no offset specified
2958  {
2959  Device d(cdd);
2960  BOOST_CHECK_NO_THROW(auto reg = d.getTwoDRegisterAccessor<UserType>(registerName, sizeMap[registerName], 0));
2961  }
2962  // does not throw when one element shorter and offset of 1 specified (only if register is long enough)
2963  if(sizeMap[registerName] > 1) {
2964  Device d(cdd);
2965  BOOST_CHECK_NO_THROW(
2966  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName, sizeMap[registerName] - 1, 1));
2967  }
2968  });
2969  }
2970 
2971  /********************************************************************************************************************/
2972 
2977  template<typename VECTOR_OF_REGISTERS_T>
2979  std::cout << "--- test_C_5_2_3_2 - logic_error for wrong access mode flags" << std::endl;
2980 
2981  Device d(cdd);
2982  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
2983  if(x.supportedFlags().has(ChimeraTK::AccessMode::wait_for_new_data)) return;
2984  typedef typename decltype(x)::minimumUserType UserType;
2985  auto registerName = x.path();
2986  std::cout << "... registerName = " << registerName << " (wait_for_new_data throws)" << std::endl;
2987  BOOST_CHECK_THROW(
2988  d.getTwoDRegisterAccessor<UserType>(registerName, 0, 0, {AccessMode::wait_for_new_data}), logic_error);
2989  });
2990  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
2991  if(x.supportedFlags().has(ChimeraTK::AccessMode::raw)) return;
2992  typedef typename decltype(x)::minimumUserType UserType;
2993  auto registerName = x.path();
2994  std::cout << "... registerName = " << registerName << " (raw throws)" << std::endl;
2995  BOOST_CHECK_THROW(d.getTwoDRegisterAccessor<UserType>(registerName, 0, 0, {AccessMode::raw}), logic_error);
2996  });
2997  }
2998 
2999  /********************************************************************************************************************/
3000 
3005  template<typename VECTOR_OF_REGISTERS_T>
3007  if(_testOnlyTransferElement) return;
3008  std::cout << "--- test_C_5_2_5_2 - logic_error on operation while backend closed" << std::endl;
3009  Device d(cdd);
3010 
3011  std::cout << "... synchronous read" << std::endl;
3012  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
3013  if(!this->isRead(x)) return;
3014  typedef typename decltype(x)::minimumUserType UserType;
3015  auto registerName = x.path();
3016  std::cout << " registerName = " << registerName << std::endl;
3017  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName);
3018  BOOST_CHECK_THROW(reg.read(), logic_error);
3019  });
3020 
3021  std::cout << "... asynchronous read" << std::endl;
3022  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
3023  if(!this->isAsyncRead(x)) return;
3024  typedef typename decltype(x)::minimumUserType UserType;
3025  auto registerName = x.path();
3026  std::cout << " registerName = " << registerName << std::endl;
3027  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName, 0, 0, {AccessMode::wait_for_new_data});
3028  BOOST_CHECK_THROW(reg.read(), logic_error);
3029  BOOST_CHECK_THROW(reg.readNonBlocking(), logic_error);
3030  });
3031 
3032  std::cout << "... write" << std::endl;
3033  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
3034  if(!this->isWrite(x)) return;
3035  typedef typename decltype(x)::minimumUserType UserType;
3036  auto registerName = x.path();
3037  std::cout << " registerName = " << registerName << std::endl;
3038  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName);
3039  BOOST_CHECK_THROW(reg.write(), logic_error);
3040  });
3041  }
3042 
3043  /********************************************************************************************************************/
3044 
3049  template<typename VECTOR_OF_REGISTERS_T>
3051  std::cout << "--- test_C_5_2_6_2 - logic_error on read operation on write-only register" << std::endl;
3052  Device d(cdd);
3053 
3054  std::cout << "... synchronous read" << std::endl;
3055  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
3056  if(!this->isWriteOnly(x)) return;
3057  typedef typename decltype(x)::minimumUserType UserType;
3058  auto registerName = x.path();
3059  std::cout << " registerName = " << registerName << std::endl;
3060  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName);
3061  BOOST_CHECK_THROW(reg.read(), logic_error);
3062  });
3063 
3064  std::cout << "... asynchronous read" << std::endl;
3065  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
3066  if(!this->isWriteOnly(x)) return;
3067  typedef typename decltype(x)::minimumUserType UserType;
3068  auto registerName = x.path();
3069  std::cout << " registerName = " << registerName << std::endl;
3070  BOOST_CHECK_THROW(
3071  d.getTwoDRegisterAccessor<UserType>(registerName, 0, 0, {AccessMode::wait_for_new_data}), logic_error);
3072  });
3073  }
3074 
3075  /********************************************************************************************************************/
3076 
3081  template<typename VECTOR_OF_REGISTERS_T>
3083  std::cout << "--- test_C_5_2_7_2 - logic_error on write operation on read-only register" << std::endl;
3084  Device d(cdd);
3085 
3086  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
3087  if(!this->isReadOnly(x)) return;
3088  typedef typename decltype(x)::minimumUserType UserType;
3089  auto registerName = x.path();
3090  std::cout << " registerName = " << registerName << std::endl;
3091  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName);
3092  BOOST_CHECK_THROW(reg.write(), logic_error);
3093  });
3094  }
3095 
3096  /********************************************************************************************************************/
3097 
3103  template<typename VECTOR_OF_REGISTERS_T>
3105  std::cout << "--- test_C_5_3 - read-only/write-only information changes after runtime_error" << std::endl;
3106  Device d(cdd);
3107  d.open();
3108 
3109  // switch to read-only
3110  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
3111  if(!this->isRead(x) || !this->isWrite(x) || x.capabilities.switchReadOnly != TestCapability::enabled) return;
3112  typedef typename decltype(x)::minimumUserType UserType;
3113  auto registerName = x.path();
3114  std::cout << " registerName = " << registerName << std::endl;
3115  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName);
3116  this->switchReadOnly(x, true);
3117  BOOST_CHECK(reg.isWriteable() == true); // C.5.3
3118  BOOST_CHECK_THROW(reg.write(), runtime_error); // C.5.3.1
3119  BOOST_CHECK(reg.isWriteable() == false); // C.5.3
3120  this->switchReadOnly(x, false);
3121  });
3122 
3123  // switch to write-only (note: this test is untested, no backend supports this!)
3124  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
3125  if(!this->isRead(x) || !this->isWrite(x) || x.capabilities.switchWriteOnly != TestCapability::enabled) return;
3126  typedef typename decltype(x)::minimumUserType UserType;
3127  auto registerName = x.path();
3128  std::cout << " registerName = " << registerName << std::endl;
3129  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName);
3130  this->switchWriteOnly(x, true);
3131  BOOST_CHECK(reg.isReadable() == true); // C.5.3
3132  BOOST_CHECK_THROW(reg.read(), runtime_error); // C.5.3.1
3133  BOOST_CHECK(reg.isReadable() == false); // C.5.3
3134  this->switchWriteOnly(x, false);
3135  });
3136  }
3137 
3138  /********************************************************************************************************************/
3139 
3144  template<typename VECTOR_OF_REGISTERS_T>
3146  std::cout << "--- test_C_5_3_2 - read-only/write-only information cached per accessor" << std::endl;
3147  Device d(cdd);
3148  d.open();
3149 
3150  // switch to read-only
3151  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
3152  if(!this->isRead(x) || !this->isWrite(x) || x.capabilities.switchReadOnly != TestCapability::enabled) return;
3153  typedef typename decltype(x)::minimumUserType UserType;
3154  auto registerName = x.path();
3155  std::cout << " registerName = " << registerName << std::endl;
3156  auto reg1 = d.getTwoDRegisterAccessor<UserType>(registerName);
3157  auto reg2 = d.getTwoDRegisterAccessor<UserType>(registerName);
3158  this->switchReadOnly(x, true);
3159  BOOST_CHECK_THROW(reg1.write(), runtime_error); // no check intended, just catch
3160  BOOST_CHECK(reg2.isWriteable() == true);
3161  this->switchReadOnly(x, false);
3162  });
3163 
3164  // switch to write-only (note: this test is untested, no backend supports this!)
3165  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
3166  if(!this->isRead(x) || !this->isWrite(x) || x.capabilities.switchWriteOnly != TestCapability::enabled) return;
3167  typedef typename decltype(x)::minimumUserType UserType;
3168  auto registerName = x.path();
3169  std::cout << " registerName = " << registerName << std::endl;
3170  auto reg1 = d.getTwoDRegisterAccessor<UserType>(registerName);
3171  auto reg2 = d.getTwoDRegisterAccessor<UserType>(registerName);
3172  this->switchWriteOnly(x, true);
3173  BOOST_CHECK_THROW(reg1.read(), runtime_error); // no check intended, just catch
3174  BOOST_CHECK(reg2.isReadable() == true);
3175  this->switchWriteOnly(x, false);
3176  });
3177  }
3178 
3179  /********************************************************************************************************************/
3180 
3185  template<typename VECTOR_OF_REGISTERS_T>
3187  std::cout << "--- test_C_5_3_3 - read-only/write-only information always returned from cache if available"
3188  << std::endl;
3189  Device d(cdd);
3190  d.open();
3191 
3192  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
3193  typedef typename decltype(x)::minimumUserType UserType;
3194  auto registerName = x.path();
3195  std::cout << " registerName = " << registerName << std::endl;
3196  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName);
3197 
3198  // Obtain information. This also makes sure the TE caches it.
3199  auto isReadable = reg.isReadable();
3200  auto isWriteable = reg.isWriteable();
3201 
3202  for(size_t i = 0; i < x.nRuntimeErrorCases(); ++i) {
3203  std::cout << " -> runtime_error case: " << i << std::endl;
3204  // enable exceptions on read
3205  x.setForceRuntimeError(true, i);
3206 
3207  // Now isReadable and isWriteable are not able to communicate with the device but still should give the same
3208  // result.
3209  BOOST_CHECK(reg.isReadable() == isReadable);
3210  BOOST_CHECK(reg.isWriteable() == isWriteable);
3211 
3212  // disable exceptions on read
3213  x.setForceRuntimeError(false, i);
3214 
3215  // recover shouldn't even be necessary, since no communication happened
3216  BOOST_CHECK(d.isFunctional() == true);
3217  }
3218  });
3219  }
3220 
3221  /********************************************************************************************************************/
3222 
3227  template<typename VECTOR_OF_REGISTERS_T>
3229  std::cout << "--- test_NOSPEC_valueAfterConstruction - content of the application data buffer after construction."
3230  << std::endl;
3231  Device d(cdd);
3232 
3233  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
3234  typedef typename decltype(x)::minimumUserType UserType;
3235  auto registerName = x.path();
3236  std::cout << "... registerName = " << registerName << std::endl;
3237  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName);
3238 
3239  // check "value after construction" for value buffer
3240  std::vector<UserType> v(reg.getNElementsPerChannel(), UserType());
3241  for(size_t i = 0; i < reg.getNChannels(); ++i) BOOST_CHECK(reg[i] == v);
3242  });
3243  }
3244 
3245  /********************************************************************************************************************/
3246 
3251  template<typename VECTOR_OF_REGISTERS_T>
3253  if(_testOnlyTransferElement) return;
3254  std::cout << "--- test_NOSPEC_backendNotClosedAfterException - backend not closed after exception" << std::endl;
3255  Device d(cdd);
3256 
3257  // open the device, then let it throw runtime_error exceptions
3258  d.open();
3259 
3260  std::cout << "... synchronous read" << std::endl;
3261  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
3262  if(!this->isRead(x)) return;
3263  if(x.nRuntimeErrorCases() == 0) return;
3264  typedef typename decltype(x)::minimumUserType UserType;
3265  auto registerName = x.path();
3266  std::cout << " registerName = " << registerName << std::endl;
3267  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName);
3268 
3269  for(size_t i = 0; i < x.nRuntimeErrorCases(); ++i) {
3270  std::cout << " -> runtime_error case: " << i << std::endl;
3271  // enable exceptions on read
3272  x.setForceRuntimeError(true, i);
3273 
3274  // trigger runtime error
3275  BOOST_CHECK_THROW(reg.read(), runtime_error); // no test intended, just catch
3276 
3277  // check device is still open but in error state
3278  BOOST_CHECK(d.isOpened());
3279  BOOST_CHECK(!d.isFunctional());
3280 
3281  // check a failed attempt to recover does not change this
3282  BOOST_CHECK_THROW(d.open(), runtime_error); // no test intended, just catch
3283  BOOST_CHECK(d.isOpened());
3284  BOOST_CHECK(!d.isFunctional());
3285 
3286  // disable exceptions on read
3287  x.setForceRuntimeError(false, i);
3288 
3289  // recover
3290  this->recoverDevice(d);
3291  }
3292  });
3293 
3294  std::cout << "... asynchronous read" << std::endl;
3295  d.activateAsyncRead();
3296  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
3297  if(!this->isAsyncRead(x)) return;
3298  if(x.nRuntimeErrorCases() == 0) return;
3299  typedef typename decltype(x)::minimumUserType UserType;
3300  auto registerName = x.path();
3301  std::cout << " registerName = " << registerName << std::endl;
3302  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName, 0, 0, {AccessMode::wait_for_new_data});
3303 
3304  for(size_t i = 0; i < x.nRuntimeErrorCases(); ++i) {
3305  std::cout << " -> runtime_error case: " << i << std::endl;
3306  reg.read(); // initial value
3307 
3308  // enable exceptions on read
3309  x.setForceRuntimeError(true, i);
3310 
3311  // trigger runtime error
3312  BOOST_CHECK_THROW(reg.read(), runtime_error); // no test intended, just catch
3313 
3314  // check device is still open but in error state
3315  BOOST_CHECK(d.isOpened());
3316  BOOST_CHECK(!d.isFunctional());
3317 
3318  // check a failed attempt to recover does not change this
3319  BOOST_CHECK_THROW(d.open(), runtime_error); // no test intended, just catch
3320  BOOST_CHECK(d.isOpened());
3321  BOOST_CHECK(!d.isFunctional());
3322 
3323  // disable exceptions on read
3324  x.setForceRuntimeError(false, i);
3325 
3326  // recover
3327  this->recoverDevice(d);
3328  d.activateAsyncRead(); // turn async read back on
3329  }
3330  });
3331 
3332  std::cout << "... write" << std::endl;
3333  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
3334  if(!this->isWrite(x)) return;
3335  if(x.nRuntimeErrorCases() == 0) return;
3336  typedef typename decltype(x)::minimumUserType UserType;
3337  auto registerName = x.path();
3338  std::cout << " registerName = " << registerName << std::endl;
3339  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName);
3340 
3341  for(size_t i = 0; i < x.nRuntimeErrorCases(); ++i) {
3342  std::cout << " -> runtime_error case: " << i << std::endl;
3343  // enable exceptions on write
3344  x.setForceRuntimeError(true, i);
3345 
3346  // trigger runtime error
3347  BOOST_CHECK_THROW(reg.write(), runtime_error); // no test intended, just catch
3348 
3349  // check device is still open but in error state
3350  BOOST_CHECK(d.isOpened());
3351  BOOST_CHECK(!d.isFunctional());
3352 
3353  // check a failed attempt to recover does not change this
3354  BOOST_CHECK_THROW(d.open(), runtime_error); // no test intended, just catch
3355  BOOST_CHECK(d.isOpened());
3356  BOOST_CHECK(!d.isFunctional());
3357 
3358  // disable exceptions on write
3359  x.setForceRuntimeError(false, i);
3360 
3361  // recover
3362  this->recoverDevice(d);
3363  }
3364  });
3365 
3366  std::cout << "... isReadable" << std::endl;
3367  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
3368  if(x.nRuntimeErrorCases() == 0) return;
3369  typedef typename decltype(x)::minimumUserType UserType;
3370  auto registerName = x.path();
3371  std::cout << " registerName = " << registerName;
3372  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName);
3373 
3374  bool didThrow = false;
3375  for(size_t i = 0; i < x.nRuntimeErrorCases(); ++i) {
3376  std::cout << " -> runtime_error case: " << i << std::endl;
3377  // enable exceptions on write
3378  x.setForceRuntimeError(true, i);
3379 
3380  // attempt to trigger runtime error (no obligation to throw...)
3381  try {
3382  [[maybe_unused]] auto result = reg.isReadable();
3383  }
3384  catch(...) {
3385  didThrow = true;
3386  // check device is still open but in error state
3387  BOOST_CHECK(d.isOpened());
3388  BOOST_CHECK(!d.isFunctional());
3389 
3390  // check a failed attempt to recover does not change this
3391  BOOST_CHECK_THROW(d.open(), runtime_error); // no test intended, just catch
3392  BOOST_CHECK(d.isOpened());
3393  BOOST_CHECK(!d.isFunctional());
3394  }
3395 
3396  // disable exceptions on write
3397  x.setForceRuntimeError(false, i);
3398 
3399  // recover
3400  this->recoverDevice(d);
3401  }
3402 
3403  if(!didThrow) {
3404  std::cout << " (doesn't throw)" << std::endl;
3405  }
3406  else {
3407  std::cout << " (throws)" << std::endl;
3408  }
3409  });
3410 
3411  std::cout << "... isWriteable" << std::endl;
3412  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
3413  if(x.nRuntimeErrorCases() == 0) return;
3414  typedef typename decltype(x)::minimumUserType UserType;
3415  auto registerName = x.path();
3416  std::cout << " registerName = " << registerName;
3417  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName);
3418 
3419  bool didThrow = false;
3420  for(size_t i = 0; i < x.nRuntimeErrorCases(); ++i) {
3421  std::cout << " -> runtime_error case: " << i << std::endl;
3422  // enable exceptions on write
3423  x.setForceRuntimeError(true, i);
3424 
3425  // attempt to trigger runtime error (no obligation to throw...)
3426  try {
3427  [[maybe_unused]] auto result = reg.isWriteable();
3428  }
3429  catch(...) {
3430  didThrow = true;
3431  // check device is still open but in error state
3432  BOOST_CHECK(d.isOpened());
3433  BOOST_CHECK(!d.isFunctional());
3434 
3435  // check a failed attempt to recover does not change this
3436  BOOST_CHECK_THROW(d.open(), runtime_error); // no test intended, just catch
3437  BOOST_CHECK(d.isOpened());
3438  BOOST_CHECK(!d.isFunctional());
3439  }
3440 
3441  // disable exceptions on write
3442  x.setForceRuntimeError(false, i);
3443 
3444  // recover
3445  this->recoverDevice(d);
3446  }
3447 
3448  if(!didThrow) {
3449  std::cout << " (doesn't throw)" << std::endl;
3450  }
3451  else {
3452  std::cout << " (throws)" << std::endl;
3453  }
3454  });
3455 
3456  std::cout << "... isReadOnly" << std::endl;
3457  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
3458  typedef typename decltype(x)::minimumUserType UserType;
3459  auto registerName = x.path();
3460  std::cout << " registerName = " << registerName;
3461  auto reg = d.getTwoDRegisterAccessor<UserType>(registerName);
3462 
3463  bool didThrow = false;
3464  for(size_t i = 0; i < x.nRuntimeErrorCases(); ++i) {
3465  std::cout << " -> runtime_error case: " << i << std::endl;
3466  // enable exceptions on write
3467  x.setForceRuntimeError(true, i);
3468 
3469  // attempt to trigger runtime error (no obligation to throw...)
3470  try {
3471  [[maybe_unused]] auto result = reg.isReadOnly();
3472  }
3473  catch(...) {
3474  didThrow = true;
3475  // check device is still open but in error state
3476  BOOST_CHECK(d.isOpened());
3477  BOOST_CHECK(!d.isFunctional());
3478 
3479  // check a failed attempt to recover does not change this
3480  BOOST_CHECK_THROW(d.open(), runtime_error); // no test intended, just catch
3481  BOOST_CHECK(d.isOpened());
3482  BOOST_CHECK(!d.isFunctional());
3483  }
3484 
3485  // disable exceptions on write
3486  x.setForceRuntimeError(false, i);
3487 
3488  // recover
3489  this->recoverDevice(d);
3490  }
3491 
3492  if(!didThrow) {
3493  std::cout << " (doesn't throw)" << std::endl;
3494  }
3495  else {
3496  std::cout << " (throws)" << std::endl;
3497  }
3498  });
3499  // close device again
3500  d.close();
3501  }
3502 
3503  /********************************************************************************************************************/
3504 
3509  template<typename VECTOR_OF_REGISTERS_T>
3511  std::cout << "--- test_NOSPEC_rawTransfer - test creation and reading/writing with access mode raw." << std::endl;
3512  Device d(cdd);
3513  d.open();
3514 
3515  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
3516  // the test itself requires an extended interface, so the test can be disabled
3517  if constexpr(x.capabilities.testRawTransfer == TestCapability::enabled) {
3518  auto registerName = x.path();
3519  std::cout << "... registerName = " << registerName << std::endl;
3520 
3521  BOOST_REQUIRE_MESSAGE(this->isRaw(x),
3522  "Test configuration error: testRawTransfer is enabled for register without AccessMode::raw!");
3523 
3524  typedef typename decltype(x)::minimumUserType UserType;
3525  typedef typename decltype(x)::rawUserType RawType;
3526  // Use double as example for a not working user type
3527  BOOST_CHECK_THROW(
3528  d.getTwoDRegisterAccessor<double>(registerName, 0, 0, {AccessMode::raw}), ChimeraTK::logic_error);
3529  try {
3530  // test creation
3531  auto reg = d.getTwoDRegisterAccessor<RawType>(registerName, 0, 0, {AccessMode::raw});
3532  // the test itself requires an extended interface, so the test can be disabled
3533  if(x.isReadable()) {
3534  x.setRemoteValue();
3535  reg.read();
3536  auto expectedRawValue = x.template getRemoteValue<RawType>(/* getRaw = */ true);
3537  CHECK_EQUALITY(reg, expectedRawValue);
3538 
3539  auto expectedCookedValue = x.template getRemoteValue<UserType>();
3540  // fill into a vector<vector> and use CHECK_EQUALITY_VECTOR. This stops at the first mismatch, prints a good
3541  // error message, checks for all elements 0 etc.
3542  std::vector<std::vector<UserType>> readCookedValue;
3543  for(size_t channel = 0; channel < reg.getNChannels(); ++channel) {
3544  std::vector<UserType> readCookedChannel;
3545  for(size_t element = 0; element < reg.getNElementsPerChannel(); ++element) {
3546  readCookedChannel.push_back(reg.template getAsCooked<UserType>(channel, element));
3547  }
3548  readCookedValue.push_back(readCookedChannel);
3549  }
3550  CHECK_EQUALITY_VECTOR(readCookedValue, expectedCookedValue);
3551  }
3552  if(x.isWriteable()) {
3553  auto newValue = x.template generateValue<RawType>(/* getRaw = */ true);
3554  reg = newValue;
3555  reg.write();
3556  auto readbackValue = x.template getRemoteValue<RawType>(/* getRaw = */ true);
3557  CHECK_EQUALITY_VECTOR(readbackValue, newValue);
3558 
3559  // test setting as cooked
3560  auto newCookedValue = x.template generateValue<UserType>();
3561  for(size_t channel = 0; channel < reg.getNChannels(); ++channel) {
3562  for(size_t element = 0; element < reg.getNElementsPerChannel(); ++element) {
3563  reg.template setAsCooked<UserType>(channel, element, newCookedValue[channel][element]);
3564  }
3565  }
3566  reg.write();
3567 
3568  auto readbackCookedValue = x.template getRemoteValue<UserType>();
3569  CHECK_EQUALITY_VECTOR(readbackCookedValue, newCookedValue);
3570  }
3571  }
3572  catch(std::exception& e) {
3573  BOOST_CHECK_MESSAGE(false, std::string("Unexpected expeption: ") + e.what());
3574  }
3575  } // end of constexpr if
3576  if(this->isRaw(x)) {
3577  if(x.capabilities.testRawTransfer == TestCapability::disabled) {
3578  BOOST_REQUIRE_MESSAGE(false,
3579  "Test configuration error: testRawTransfer is disabled for register '" + std::string(x.path()) +
3580  "' with AccessMode::raw!");
3581  }
3582  else if(x.capabilities.testRawTransfer == TestCapability::unspecified) {
3583  std::cout << "WARNING: testRawTransfer capability unspecified for register '" + std::string(x.path()) +
3584  "' with AccessMode::raw. This will turn into a test configuration error in a future release!"
3585  << std::endl;
3586  }
3587  }
3588  else {
3589  if(x.capabilities.testRawTransfer == TestCapability::unspecified) {
3590  std::cout << "Warning: testRawTransfer capability unspecified for register '" + std::string(x.path()) +
3591  "' without AccessMode::raw. Please explicitly disable this test."
3592  << std::endl;
3593  }
3594  }
3595  });
3596  }
3597 
3598  /********************************************************************************************************************/
3599 
3604  template<typename VECTOR_OF_REGISTERS_T>
3606  std::cout << "--- test_NOSPEC_catalogueRaw - test catalogue entries for access mode raw." << std::endl;
3607  Device d(cdd);
3608 
3609  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
3610  if(x.capabilities.testCatalogue == TestCapability::disabled) {
3611  return;
3612  }
3613 
3614  auto registerName = x.path();
3615  std::cout << "... registerName = " << registerName << std::endl;
3616 
3617  // workaround for DUMMY_WRITABLE not having information in the catalogue yet
3618  if(std::string(registerName).find("DUMMY_WRITEABLE") != std::string::npos) {
3619  return;
3620  }
3621 
3622  if(std::string(registerName).find("DUMMY_INTERRUPT_") != std::string::npos) {
3623  return;
3624  }
3625 
3626  auto registerInfo = d.getRegisterCatalogue().getRegister(registerName);
3627 
3628  if(this->isRaw(x)) {
3629  BOOST_CHECK(registerInfo.getSupportedAccessModes().has(AccessMode::raw));
3630  BOOST_TEST(registerInfo.getDataDescriptor().rawDataType() != DataType::none);
3631  }
3632  else {
3633  BOOST_CHECK(not registerInfo.getSupportedAccessModes().has(AccessMode::raw));
3634  BOOST_CHECK((registerInfo.getDataDescriptor().rawDataType() == DataType::none) ||
3635  (registerInfo.getDataDescriptor().rawDataType() == DataType::Void));
3636  }
3637  });
3638  }
3639 
3644  template<typename VECTOR_OF_REGISTERS_T>
3646  std::cout << "--- test_NOSPEC_catalogueReadWrite- test catalogue and accessor entries for read/write." << std::endl;
3647  Device d(cdd);
3648 
3649  boost::mpl::for_each<VECTOR_OF_REGISTERS_T>([&](auto x) {
3650  if(x.capabilities.testCatalogue == TestCapability::disabled) {
3651  return;
3652  }
3653 
3654  typedef typename decltype(x)::minimumUserType UserType;
3655 
3656  auto registerName = x.path();
3657  std::cout << "... registerName = " << registerName << std::endl;
3658 
3659  auto registerInfo = d.getRegisterCatalogue().getRegister(registerName);
3660  auto accessor = d.getScalarRegisterAccessor<UserType>(registerName);
3661 
3662  BOOST_CHECK_EQUAL(this->isRead(x), registerInfo.isReadable());
3663  BOOST_CHECK_EQUAL(this->isRead(x), accessor.isReadable());
3664  BOOST_CHECK_EQUAL(this->isWrite(x), registerInfo.isWriteable());
3665  BOOST_CHECK_EQUAL(this->isWrite(x), accessor.isWriteable());
3666  });
3667  }
3668 
3669  /********************************************************************************************************************/
3678  /********************************************************************************************************************/
3679 
3686  /********************************************************************************************************************/
3687 
3696  /********************************************************************************************************************/
3697 
3698 } // namespace ChimeraTK
ChimeraTK::UnifiedBackendTest::ExceptionReportingBackend::hasSeenException
bool hasSeenException()
Check whether setException() has been called since the last call to hasSeenException().
Definition: UnifiedBackendTest.h:487
ChimeraTK::UnifiedBackendTest::isRead
bool isRead(REG_T x={})
Utility functions for register traits.
Definition: UnifiedBackendTest.h:431
ChimeraTK::TestCapabilities::disableSyncRead
constexpr TestCapabilities< TestCapability::disabled, _forceDataLossWrite, _asyncReadInconsistency, _switchReadOnly, _switchWriteOnly, _writeNeverLosesData, _testWriteOnly, _testReadOnly, _testRawTransfer, _testCatalogue, _setRemoteValueIncrementsVersion > disableSyncRead() const
Allows to prevent the test from executing any synchronous read tests.
Definition: UnifiedBackendTest.h:65
ChimeraTK::UnifiedBackendTest::ExceptionReportingBackend::getRegisterCatalogue
RegisterCatalogue getRegisterCatalogue() const override
Return the register catalogue with detailed information on all registers.
Definition: UnifiedBackendTest.h:497
CHECK_APPLICATION_BUFFER
#define CHECK_APPLICATION_BUFFER(UserType, accessor)
Definition: UnifiedBackendTest.h:1211
ChimeraTK::UnifiedBackendTest::nValuesToTest_proxy_helper::nValuesToTest_proxy_helper
nValuesToTest_proxy_helper(T t)
Definition: UnifiedBackendTest.h:622
ChimeraTK::UnifiedBackendTest::test_B_9_1
void test_B_9_1()
Test reporting exceptions to exception backend.
Definition: UnifiedBackendTest.h:2160
ChimeraTK::TwoDRegisterAccessor
Accessor class to read and write 2D registers.
Definition: ForwardDeclarations.h:20
ChimeraTK::UnifiedBackendTest::test_B_11_6
void test_B_11_6()
Test the value after construction for the version number in the application buffer.
Definition: UnifiedBackendTest.h:2861
ChimeraTK::TestCapability::enabled
@ enabled
Enable tests requiring this capability.
ChimeraTK::UnifiedBackendTest::has_nValuesToTest
Definition: UnifiedBackendTest.h:605
ChimeraTK::compareHelper< float >
bool compareHelper< float >(float a, float b)
Definition: UnifiedBackendTest.h:660
ChimeraTK::UnifiedBackendTest::nValuesToTest_proxy_helper< T, false >::result
size_t result
Definition: UnifiedBackendTest.h:628
ChimeraTK::UnifiedBackendTest::setForceDataLossWrite_proxy_helper
Definition: UnifiedBackendTest.h:506
ChimeraTK::TestCapabilities::testWriteOnly
static constexpr TestCapability testWriteOnly
Definition: UnifiedBackendTest.h:221
ChimeraTK::AccessMode::raw
@ raw
Raw access: disable any possible conversion from the original hardware data type into the given UserT...
ChimeraTK::UnifiedBackendTest::setForceDataLossWrite_proxy_helper::setForceDataLossWrite_proxy_helper
setForceDataLossWrite_proxy_helper(T t, bool enable)
Definition: UnifiedBackendTest.h:507
ChimeraTK::UnifiedBackendTest::EnableDisableActionList
"Strong typedef" for list of pairs of functors for enabling and disabling a test condition.
Definition: UnifiedBackendTest.h:361
ChimeraTK::UnifiedBackendTest::forceAsyncReadInconsistency_proxy_helper::forceAsyncReadInconsistency_proxy_helper
forceAsyncReadInconsistency_proxy_helper(T t)
Definition: UnifiedBackendTest.h:526
ChimeraTK::UnifiedBackendTest::ExceptionReportingBackend::setExceptionImpl
void setExceptionImpl() noexcept override
Function to be (optionally) implemented by backends if additional actions are needed when switching t...
Definition: UnifiedBackendTest.h:478
ChimeraTK::UnifiedBackendTest::isSyncRead
bool isSyncRead(REG_T x={})
Definition: UnifiedBackendTest.h:442
ChimeraTK::UnifiedBackendTest::test_B_8_3
void test_B_8_3()
Test new runtime errors are put to _readQueue in async reads.
Definition: UnifiedBackendTest.h:1750
ChimeraTK::UnifiedBackendTest::switchReadOnly_proxy_helper::switchReadOnly_proxy_helper
switchReadOnly_proxy_helper(T t, bool enable)
Definition: UnifiedBackendTest.h:545
ChimeraTK::UnifiedBackendTest::switchWriteOnly
void switchWriteOnly(T t, bool enable)
Definition: UnifiedBackendTest.h:576
ChimeraTK::UnifiedBackendTest::test_B_11_2_2
void test_B_11_2_2()
Test consistent data gets same VersionNumber.
Definition: UnifiedBackendTest.h:2803
ChimeraTK::UnifiedBackendTest::cdd
std::string cdd
CDD for backend to test.
Definition: UnifiedBackendTest.h:468
ChimeraTK::TestCapabilities::switchReadOnly
static constexpr TestCapability switchReadOnly
Definition: UnifiedBackendTest.h:218
ChimeraTK::UnifiedBackendTest::writeQueueLength_proxy_helper::result
size_t result
Definition: UnifiedBackendTest.h:586
ChimeraTK::TransferType::writeDestructively
@ writeDestructively
ChimeraTK::UnifiedBackendTest::test_C_5_2_3_2
void test_C_5_2_3_2()
Test logic_error for wrong access mode flags.
Definition: UnifiedBackendTest.h:2978
ChimeraTK::UnifiedBackendTest::testOnlyTransferElement
UnifiedBackendTest< VECTOR_OF_REGISTERS_T > & testOnlyTransferElement()
Call if not a real backend is tested but just a special TransferElement implementation.
Definition: UnifiedBackendTest.h:379
ChimeraTK::Device::close
void close()
Close the device.
Definition: Device.cc:66
ChimeraTK::Device::getBackend
boost::shared_ptr< DeviceBackend > getBackend()
Obtain the backend.
Definition: Device.cc:111
ChimeraTK::UnifiedBackendTest::switchReadOnly_proxy_helper< T, false >
Definition: UnifiedBackendTest.h:549
ChimeraTK::TestCapabilities::enableSetRemoteValueIncrementsVersion
constexpr TestCapabilities< _syncRead, _forceDataLossWrite, _asyncReadInconsistency, _switchReadOnly, _switchWriteOnly, _writeNeverLosesData, _testWriteOnly, _testReadOnly, _testRawTransfer, _testCatalogue, TestCapability::enabled > enableSetRemoteValueIncrementsVersion() const
Definition: UnifiedBackendTest.h:211
ChimeraTK::UnifiedBackendTest::nValuesToTest_proxy_helper::result
size_t result
Definition: UnifiedBackendTest.h:623
ChimeraTK::UnifiedBackendTest::writeQueueLength
size_t writeQueueLength(T t)
Definition: UnifiedBackendTest.h:599
ChimeraTK::TestCapabilities::disableAsyncReadInconsistency
constexpr TestCapabilities< _syncRead, _forceDataLossWrite, TestCapability::disabled, _switchReadOnly, _switchWriteOnly, _writeNeverLosesData, _testWriteOnly, _testReadOnly, _testRawTransfer, _testCatalogue, _setRemoteValueIncrementsVersion > disableAsyncReadInconsistency() const
Definition: UnifiedBackendTest.h:95
ChimeraTK::DeviceBackendImpl::setOpenedAndClearException
void setOpenedAndClearException() noexcept
Backends should call this function at the end of a (successful) open() call.
Definition: DeviceBackendImpl.cc:12
ChimeraTK::UnifiedBackendTest::isWrite
bool isWrite(REG_T x={})
Definition: UnifiedBackendTest.h:437
ChimeraTK::UnifiedBackendTest::forceAsyncReadInconsistency_proxy_helper
Definition: UnifiedBackendTest.h:525
ChimeraTK::UnifiedBackendTest::isWriteOnly
bool isWriteOnly(REG_T x={})
Definition: UnifiedBackendTest.h:460
ChimeraTK::UnifiedBackendTest::test_NOSPEC_rawTransfer
void test_NOSPEC_rawTransfer()
Test that the backend does not close itself after seeing an exception.
Definition: UnifiedBackendTest.h:3510
ChimeraTK::UnifiedBackendTest::recoverDevice
void recoverDevice(ChimeraTK::Device &d)
Utility functions for recurring tasks.
Definition: UnifiedBackendTest.h:989
ChimeraTK::TestCapabilities::writeNeverLosesData
static constexpr TestCapability writeNeverLosesData
Definition: UnifiedBackendTest.h:220
ChimeraTK::testable_rebot_sleep::now
boost::chrono::steady_clock::time_point now()
Definition: testableRebotSleep.cc:7
ChimeraTK::TestCapabilities::disableSwitchReadOnly
constexpr TestCapabilities< _syncRead, _forceDataLossWrite, _asyncReadInconsistency, TestCapability::disabled, _switchWriteOnly, _writeNeverLosesData, _testWriteOnly, _testReadOnly, _testRawTransfer, _testCatalogue, _setRemoteValueIncrementsVersion > disableSwitchReadOnly() const
Definition: UnifiedBackendTest.h:109
ChimeraTK::UnifiedBackendTest::test_NOSPEC_newVersionAfterOpen
void test_NOSPEC_newVersionAfterOpen()
Test versions after calling open() are newer than any version before.
Definition: UnifiedBackendTest.h:2719
ChimeraTK::UnifiedBackendTest::registers
VECTOR_OF_REGISTERS_T registers
boost::mpl::vector with all register descriptors
Definition: UnifiedBackendTest.h:465
ChimeraTK::UnifiedBackendTest::ExceptionReportingBackend
Special DeviceBackend used for testing the exception reporting to the backend.
Definition: UnifiedBackendTest.h:474
ChimeraTK::UnifiedBackendTest::test_B_8_5
void test_B_8_5()
Test no async transfers until activateAsyncRead().
Definition: UnifiedBackendTest.h:1877
ChimeraTK::UnifiedBackendTest::ExceptionReportingBackend::close
void close() override
Close the device.
Definition: UnifiedBackendTest.h:494
DeviceBackendImpl.h
ChimeraTK::UnifiedBackendTest::test_B_8_5_3
void test_B_8_5_3()
Accessors created after activateAsyncRead() are immediately active.
Definition: UnifiedBackendTest.h:2074
ChimeraTK::UnifiedBackendTest::test_B_9_4_1
void test_B_9_4_1()
Test doReadTransferSynchronously throws runtime_error after setException() until recovery.
Definition: UnifiedBackendTest.h:2569
ChimeraTK::UnifiedBackendTest::test_NOSPEC_valueAfterConstruction
void test_NOSPEC_valueAfterConstruction()
Test the content of the application data buffer after construction.
Definition: UnifiedBackendTest.h:3228
ChimeraTK::UnifiedBackendTest::test_B_3_1_2_1
void test_B_3_1_2_1()
Test synchronous read.
Definition: UnifiedBackendTest.h:1013
ChimeraTK::UnifiedBackendTest::test_NOSPEC_write
void test_NOSPEC_write()
Test write.
Definition: UnifiedBackendTest.h:1075
cdd
std::string cdd
Definition: testAsyncRead.cpp:25
ChimeraTK::TestCapabilities
Descriptor for the test capabilities for each register.
Definition: UnifiedBackendTest.h:54
ChimeraTK::TestCapabilities::enableTestWriteOnly
constexpr TestCapabilities< _syncRead, _forceDataLossWrite, _asyncReadInconsistency, _switchReadOnly, _switchWriteOnly, _writeNeverLosesData, TestCapability::enabled, _testReadOnly, _testRawTransfer, _testCatalogue, _setRemoteValueIncrementsVersion > enableTestWriteOnly() const
Enable/disable testing only write operations, even if the register is readable.
Definition: UnifiedBackendTest.h:149
ChimeraTK::UnifiedBackendTest::test_B_9_3_1
void test_B_9_3_1()
Test setException() disables asynchronous read transfers.
Definition: UnifiedBackendTest.h:2469
ChimeraTK::UnifiedBackendTest::ExceptionReportingBackend::readDeviceInfo
std::string readDeviceInfo() override
Return a device information string containing hardware details like the firmware version number or th...
Definition: UnifiedBackendTest.h:495
ChimeraTK::runtime_error
Exception thrown when a runtime error has occured.
Definition: Exception.h:18
ChimeraTK::RegisterCatalogue
Catalogue of register information.
Definition: RegisterCatalogue.h:20
ChimeraTK::UnifiedBackendTest::test_C_5_2_5_2
void test_C_5_2_5_2()
Test logic_error on operation while backend closed.
Definition: UnifiedBackendTest.h:3006
ChimeraTK::UnifiedBackendTest::writeQueueLength_proxy_helper< T, false >
Definition: UnifiedBackendTest.h:590
ChimeraTK::UnifiedBackendTest::switchWriteOnly_proxy_helper
Definition: UnifiedBackendTest.h:563
ChimeraTK::UnifiedBackendTest::forceAsyncReadInconsistency_proxy_helper< T, false >::forceAsyncReadInconsistency_proxy_helper
forceAsyncReadInconsistency_proxy_helper(T)
Definition: UnifiedBackendTest.h:531
ChimeraTK::UnifiedBackendTest::nValuesToTest_proxy_helper
Definition: UnifiedBackendTest.h:621
ChimeraTK::TestCapabilities::enableTestRawTransfer
constexpr TestCapabilities< _syncRead, _forceDataLossWrite, _asyncReadInconsistency, _switchReadOnly, _switchWriteOnly, _writeNeverLosesData, _testWriteOnly, _testReadOnly, TestCapability::enabled, _testCatalogue, _setRemoteValueIncrementsVersion > enableTestRawTransfer() const
Definition: UnifiedBackendTest.h:183
ChimeraTK::UnifiedBackendTest::isRaw
bool isRaw(REG_T x={})
Definition: UnifiedBackendTest.h:452
ChimeraTK::UnifiedBackendTest::writeQueueLength_proxy_helper::writeQueueLength_proxy_helper
writeQueueLength_proxy_helper(T t)
Definition: UnifiedBackendTest.h:585
ChimeraTK::UnifiedBackendTest::ExceptionReportingBackend::~ExceptionReportingBackend
~ExceptionReportingBackend() override=default
ChimeraTK::DeviceBackendImpl::getActiveExceptionMessage
std::string getActiveExceptionMessage() noexcept
Definition: DeviceBackendImpl.cc:50
ChimeraTK::TestCapabilities::disableTestCatalogue
constexpr TestCapabilities< _syncRead, _forceDataLossWrite, _asyncReadInconsistency, _switchReadOnly, _switchWriteOnly, _writeNeverLosesData, _testWriteOnly, _testReadOnly, _testRawTransfer, TestCapability::disabled, _setRemoteValueIncrementsVersion > disableTestCatalogue() const
Enable/disable testing of catalogue content.
Definition: UnifiedBackendTest.h:191
CHECK_TIMEOUT
#define CHECK_TIMEOUT(condition, maxMilliseconds)
Definition: UnifiedBackendTest.h:849
ChimeraTK::TestCapabilities::switchWriteOnly
static constexpr TestCapability switchWriteOnly
Definition: UnifiedBackendTest.h:219
CHECK_EQUALITY_VECTOR_TIMEOUT
#define CHECK_EQUALITY_VECTOR_TIMEOUT(foundValue, expectedValue, maxMilliseconds)
Definition: UnifiedBackendTest.h:807
ChimeraTK::TestCapabilities::disableTestWriteNeverLosesData
constexpr TestCapabilities< _syncRead, _forceDataLossWrite, _asyncReadInconsistency, _switchReadOnly, _switchWriteOnly, TestCapability::disabled, _testWriteOnly, _testReadOnly, _testRawTransfer, _testCatalogue, _setRemoteValueIncrementsVersion > disableTestWriteNeverLosesData() const
Definition: UnifiedBackendTest.h:141
ChimeraTK::compareHelper< double >
bool compareHelper< double >(double a, double b)
Definition: UnifiedBackendTest.h:655
ChimeraTK::TestCapabilities::disableTestReadOnly
constexpr TestCapabilities< _syncRead, _forceDataLossWrite, _asyncReadInconsistency, _switchReadOnly, _switchWriteOnly, _writeNeverLosesData, _testWriteOnly, TestCapability::disabled, _testRawTransfer, _testCatalogue, _setRemoteValueIncrementsVersion > disableTestReadOnly() const
Definition: UnifiedBackendTest.h:169
ChimeraTK::UnifiedBackendTest::isAsyncRead
bool isAsyncRead(REG_T x={})
Definition: UnifiedBackendTest.h:447
ChimeraTK::UnifiedBackendTest::test_C_5_2_7_2
void test_C_5_2_7_2()
Test logic_error on write operation on read-only register.
Definition: UnifiedBackendTest.h:3082
CHECK_EQUALITY_VECTOR
#define CHECK_EQUALITY_VECTOR(foundValue, expectedValue)
Definition: UnifiedBackendTest.h:728
ChimeraTK::RegisterCatalogue::getRegister
RegisterInfo getRegister(const RegisterPath &registerPathName) const
Get register information for a given full path name.
Definition: RegisterCatalogue.cc:43
ChimeraTK::DataValidity::ok
@ ok
ChimeraTK::UnifiedBackendTest::test_C_5_2_2_2
void test_C_5_2_2_2()
Test logic_error for exceeding register size.
Definition: UnifiedBackendTest.h:2913
ChimeraTK::UnifiedBackendTest::setForceDataLossWrite_proxy_helper< T, false >
Definition: UnifiedBackendTest.h:511
ChimeraTK::Device::activateAsyncRead
void activateAsyncRead() noexcept
Activate asyncronous read for all transfer elements where AccessMode::wait_for_new_data is set.
Definition: Device.cc:91
ChimeraTK::TestCapabilities::forceDataLossWrite
static constexpr TestCapability forceDataLossWrite
Definition: UnifiedBackendTest.h:216
ChimeraTK::UnifiedBackendTest::test_B_8_6_6
void test_B_8_6_6()
Test interrupt()
Definition: UnifiedBackendTest.h:2105
ChimeraTK::TestCapability::disabled
@ disabled
Disable tests requiring this capability and do not warn.
ChimeraTK::UnifiedBackendTest::ExceptionReportingBackend::ExceptionReportingBackend
ExceptionReportingBackend(boost::shared_ptr< DeviceBackend > target)
Definition: UnifiedBackendTest.h:475
ChimeraTK::AccessMode::wait_for_new_data
@ wait_for_new_data
Make any read blocking until new data has arrived since the last read.
ChimeraTK::UnifiedBackendTest::test_C_5_3_2
void test_C_5_3_2()
Test read-only/write-only information cached per accessor.
Definition: UnifiedBackendTest.h:3145
ChimeraTK::UnifiedBackendTest::test_B_8_4
void test_B_8_4()
Test async read consistency heartbeat.
Definition: UnifiedBackendTest.h:1811
Device.h
ChimeraTK::TestCapabilities::enableTestCatalogue
constexpr TestCapabilities< _syncRead, _forceDataLossWrite, _asyncReadInconsistency, _switchReadOnly, _switchWriteOnly, _writeNeverLosesData, _testWriteOnly, _testReadOnly, _testRawTransfer, TestCapability::enabled, _setRemoteValueIncrementsVersion > enableTestCatalogue() const
Definition: UnifiedBackendTest.h:197
ChimeraTK::TestCapability::unspecified
@ unspecified
Capability is not specified, hence it is disabled and a warning is printed. Usually default.
ChimeraTK::UnifiedBackendTest::test_B_3_2_2
void test_B_3_2_2()
Test destructive write.
Definition: UnifiedBackendTest.h:1155
ChimeraTK::UnifiedBackendTest::test_C_5_3_3
void test_C_5_3_3()
Test read-only/write-only information always returned from cache if available.
Definition: UnifiedBackendTest.h:3186
ChimeraTK::UnifiedBackendTest::test_B_9_3_2
void test_B_9_3_2()
Test exactly one runtime_error in the _readQueue per async read accessor.
Definition: UnifiedBackendTest.h:2520
ChimeraTK::UnifiedBackendTest::cdd2
std::string cdd2
Definition: UnifiedBackendTest.h:468
ChimeraTK::UnifiedBackendTest::writeQueueLength_proxy_helper< T, false >::writeQueueLength_proxy_helper
writeQueueLength_proxy_helper(T)
Definition: UnifiedBackendTest.h:591
ChimeraTK::Device::getRegisterCatalogue
RegisterCatalogue getRegisterCatalogue() const
Return the register catalogue with detailed information on all registers.
Definition: Device.cc:22
ChimeraTK::TestCapabilities::enableTestReadOnly
constexpr TestCapabilities< _syncRead, _forceDataLossWrite, _asyncReadInconsistency, _switchReadOnly, _switchWriteOnly, _writeNeverLosesData, _testWriteOnly, TestCapability::enabled, _testRawTransfer, _testCatalogue, _setRemoteValueIncrementsVersion > enableTestReadOnly() const
Enable/disable testing only read operations, even if the register is readable.
Definition: UnifiedBackendTest.h:163
ChimeraTK::TestCapabilities::disableTestRawTransfer
constexpr TestCapabilities< _syncRead, _forceDataLossWrite, _asyncReadInconsistency, _switchReadOnly, _switchWriteOnly, _writeNeverLosesData, _testWriteOnly, _testReadOnly, TestCapability::disabled, _testCatalogue, _setRemoteValueIncrementsVersion > disableTestRawTransfer() const
Enable/disable testing the raw accessors.
Definition: UnifiedBackendTest.h:177
ChimeraTK::UnifiedBackendTest::runTests
void runTests(const std::string &cdd_, const std::string &cdd2_="")
Execute all tests.
Definition: UnifiedBackendTest.h:864
ChimeraTK::UnifiedBackendTest::switchWriteOnly_proxy_helper::switchWriteOnly_proxy_helper
switchWriteOnly_proxy_helper(T t, bool enable)
Definition: UnifiedBackendTest.h:564
ChimeraTK::UnifiedBackendTest
Class to test any backend for correct behaviour.
Definition: UnifiedBackendTest.h:259
ChimeraTK::UnifiedBackendTest::test_B_11_2_1
void test_B_11_2_1()
Test version number bigger for newer values.
Definition: UnifiedBackendTest.h:2643
ChimeraTK::UnifiedBackendTest::forceAsyncReadInconsistency_proxy_helper< T, false >
Definition: UnifiedBackendTest.h:530
ChimeraTK::UnifiedBackendTest::test_B_3_2_1_2
void test_B_3_2_1_2()
Test write() does not destroy application buffer.
Definition: UnifiedBackendTest.h:1115
ChimeraTK::UnifiedBackendTest::switchReadOnly_proxy_helper< T, false >::switchReadOnly_proxy_helper
switchReadOnly_proxy_helper(T, bool)
Definition: UnifiedBackendTest.h:550
ChimeraTK::UnifiedBackendTest::nValuesToTest
size_t nValuesToTest(T t)
Definition: UnifiedBackendTest.h:632
ChimeraTK::TestCapabilities::setRemoteValueIncrementsVersion
static constexpr TestCapability setRemoteValueIncrementsVersion
Definition: UnifiedBackendTest.h:225
ChimeraTK::UnifiedBackendTest::forceAsyncReadInconsistency
void forceAsyncReadInconsistency(T t)
Definition: UnifiedBackendTest.h:538
ChimeraTK::UnifiedBackendTest::test_NOSPEC_catalogueReadWrite
void test_NOSPEC_catalogueReadWrite()
Test that the catalogue and accessor information for read and write are correct.
Definition: UnifiedBackendTest.h:3645
ChimeraTK::UnifiedBackendTest::test_B_8_5_1
void test_B_8_5_1()
Test activateAsynchronousRead.
Definition: UnifiedBackendTest.h:1945
ChimeraTK::TestCapabilities::syncRead
static constexpr TestCapability syncRead
Definition: UnifiedBackendTest.h:215
ChimeraTK::TestCapabilities::asyncReadInconsistency
static constexpr TestCapability asyncReadInconsistency
Definition: UnifiedBackendTest.h:217
ChimeraTK::TestCapabilities::disableForceDataLossWrite
constexpr TestCapabilities< _syncRead, TestCapability::disabled, _asyncReadInconsistency, _switchReadOnly, _switchWriteOnly, _writeNeverLosesData, _testWriteOnly, _testReadOnly, _testRawTransfer, _testCatalogue, _setRemoteValueIncrementsVersion > disableForceDataLossWrite() const
Definition: UnifiedBackendTest.h:81
ChimeraTK::Device
Class allows to read/write registers from device.
Definition: Device.h:39
ALTER_AND_STORE_APPLICATION_BUFFER
#define ALTER_AND_STORE_APPLICATION_BUFFER(UserType, accessor)
Helper macros for test_B_4_2_4.
Definition: UnifiedBackendTest.h:1198
ChimeraTK::TestCapabilities::enableAsyncReadInconsistency
constexpr TestCapabilities< _syncRead, _forceDataLossWrite, TestCapability::enabled, _switchReadOnly, _switchWriteOnly, _writeNeverLosesData, _testWriteOnly, _testReadOnly, _testRawTransfer, _testCatalogue, _setRemoteValueIncrementsVersion > enableAsyncReadInconsistency() const
See forceAsyncReadInconsistency() function in the register descriptor.
Definition: UnifiedBackendTest.h:89
ChimeraTK::TransferType::write
@ write
ChimeraTK::UnifiedBackendTest::switchWriteOnly_proxy_helper< T, false >::switchWriteOnly_proxy_helper
switchWriteOnly_proxy_helper(T, bool)
Definition: UnifiedBackendTest.h:569
ChimeraTK::DeviceBackendImpl
DeviceBackendImpl implements some basic functionality which should be available for all backends.
Definition: DeviceBackendImpl.h:25
ChimeraTK::UnifiedBackendTest::writeQueueLength_proxy_helper
Definition: UnifiedBackendTest.h:584
ChimeraTK::TestCapabilities::disableTestWriteOnly
constexpr TestCapabilities< _syncRead, _forceDataLossWrite, _asyncReadInconsistency, _switchReadOnly, _switchWriteOnly, _writeNeverLosesData, TestCapability::disabled, _testReadOnly, _testRawTransfer, _testCatalogue, _setRemoteValueIncrementsVersion > disableTestWriteOnly() const
Definition: UnifiedBackendTest.h:155
ChimeraTK::UnifiedBackendTest::test_C_5_2_1_2
void test_C_5_2_1_2()
Test logic_error for non-existing register.
Definition: UnifiedBackendTest.h:2883
ChimeraTK::Device::open
void open(std::string const &aliasName)
Open a device by the given alias name from the DMAP file.
Definition: Device.cc:58
ChimeraTK::TestCapabilities::enableForceDataLossWrite
constexpr TestCapabilities< _syncRead, TestCapability::enabled, _asyncReadInconsistency, _switchReadOnly, _switchWriteOnly, TestCapability::disabled, _testWriteOnly, _testReadOnly, _testRawTransfer, _testCatalogue, _setRemoteValueIncrementsVersion > enableForceDataLossWrite() const
See setForceDataLossWrite() function in the register descriptor.
Definition: UnifiedBackendTest.h:73
ChimeraTK::UnifiedBackendTest::test_NOSPEC_backendNotClosedAfterException
void test_NOSPEC_backendNotClosedAfterException()
Test that the backend does not close itself after seeing an exception.
Definition: UnifiedBackendTest.h:3252
ChimeraTK::UnifiedBackendTest::switchWriteOnly_proxy_helper< T, false >
Definition: UnifiedBackendTest.h:568
ChimeraTK::Device::getTwoDRegisterAccessor
TwoDRegisterAccessor< UserType > getTwoDRegisterAccessor(const RegisterPath &registerPathName, size_t numberOfElements=0, size_t elementsOffset=0, const AccessModeFlags &flags=AccessModeFlags({})) const
Get a TwoDRegisterAccessor object for the given register.
Definition: Device.h:283
ChimeraTK::TestCapabilities::enableSwitchWriteOnly
constexpr TestCapabilities< _syncRead, _forceDataLossWrite, _asyncReadInconsistency, _switchReadOnly, TestCapability::enabled, _writeNeverLosesData, _testWriteOnly, _testReadOnly, _testRawTransfer, _testCatalogue, _setRemoteValueIncrementsVersion > enableSwitchWriteOnly() const
See switchWriteOnly() function in the register descriptor.
Definition: UnifiedBackendTest.h:117
ChimeraTK::Device::getScalarRegisterAccessor
ScalarRegisterAccessor< UserType > getScalarRegisterAccessor(const RegisterPath &registerPathName, size_t wordOffsetInRegister=0, const AccessModeFlags &flags=AccessModeFlags({})) const
Get a ScalarRegisterObject object for the given register.
Definition: Device.h:263
ChimeraTK::UnifiedBackendTest::isReadOnly
bool isReadOnly(REG_T x={})
Definition: UnifiedBackendTest.h:456
ChimeraTK::UnifiedBackendTest::setForceDataLossWrite
void setForceDataLossWrite(T t, bool enable)
Definition: UnifiedBackendTest.h:519
ChimeraTK::UnifiedBackendTest::test_B_7_2
void test_B_7_2()
Test data loss in write.
Definition: UnifiedBackendTest.h:1504
ChimeraTK::UnifiedBackendTest::test_NOSPEC_catalogueRaw
void test_NOSPEC_catalogueRaw()
Test that the catalogue information for the raw accessor is correct.
Definition: UnifiedBackendTest.h:3605
CHECK_EQUALITY
#define CHECK_EQUALITY(accessor, expectedValue)
Definition: UnifiedBackendTest.h:692
ChimeraTK::VersionNumber
Class for generating and holding version numbers without exposing a numeric representation.
Definition: VersionNumber.h:23
ChimeraTK::Device::isOpened
bool isOpened() const
Check if the device is currently opened.
Definition: Device.cc:73
ChimeraTK::UnifiedBackendTest::test_B_4_2_4
void test_B_4_2_4()
Test transfer implementations do not change the application buffer.
Definition: UnifiedBackendTest.h:1223
ChimeraTK::UnifiedBackendTest::test_C_5_2_6_2
void test_C_5_2_6_2()
Test logic_error on read operation on write-only register.
Definition: UnifiedBackendTest.h:3050
ChimeraTK::UnifiedBackendTest::test_B_8_2_1
void test_B_8_2_1()
Test _readQueue overrun.
Definition: UnifiedBackendTest.h:1693
ChimeraTK::TestCapabilities::TestCapabilities
constexpr TestCapabilities()=default
ChimeraTK::TestCapabilities::testCatalogue
static constexpr TestCapability testCatalogue
Definition: UnifiedBackendTest.h:224
ChimeraTK::compareHelper
bool compareHelper(UserType a, UserType b)
Definition: UnifiedBackendTest.h:650
ChimeraTK::UnifiedBackendTest::nValuesToTest_proxy_helper< T, false >::nValuesToTest_proxy_helper
nValuesToTest_proxy_helper(T)
Definition: UnifiedBackendTest.h:627
CHECK_EQUALITY_TIMEOUT
#define CHECK_EQUALITY_TIMEOUT(accessor, expectedValue, maxMilliseconds)
Definition: UnifiedBackendTest.h:765
ChimeraTK::UnifiedBackendTest::test_C_5_3
void test_C_5_3()
Test read-only/write-only information changes after runtime_error.
Definition: UnifiedBackendTest.h:3104
ChimeraTK::TestCapabilities::enableTestWriteNeverLosesData
constexpr TestCapabilities< _syncRead, TestCapability::disabled, _asyncReadInconsistency, _switchReadOnly, _switchWriteOnly, TestCapability::enabled, _testWriteOnly, _testReadOnly, _testRawTransfer, _testCatalogue, _setRemoteValueIncrementsVersion > enableTestWriteNeverLosesData() const
Enable/disable test whether write transfers never report lost data (part of B.7.2).
Definition: UnifiedBackendTest.h:133
ChimeraTK::UnifiedBackendTest::test_B_6_4
void test_B_6_4()
Test application buffer unchanged after exception.
Definition: UnifiedBackendTest.h:1302
ChimeraTK::UnifiedBackendTest::switchReadOnly
void switchReadOnly(T t, bool enable)
Definition: UnifiedBackendTest.h:557
ChimeraTK::Device::isFunctional
bool isFunctional() const
Return wether a device is working as intended, usually this means it is opened and does not have any ...
Definition: Device.cc:82
ChimeraTK
Definition: DummyBackend.h:16
ChimeraTK::TestCapability
TestCapability
Used by the Capabilities descriptor.
Definition: UnifiedBackendTest.h:26
ChimeraTK::TestCapabilities::enableSwitchReadOnly
constexpr TestCapabilities< _syncRead, _forceDataLossWrite, _asyncReadInconsistency, TestCapability::enabled, _switchWriteOnly, _writeNeverLosesData, _testWriteOnly, _testReadOnly, _testRawTransfer, _testCatalogue, _setRemoteValueIncrementsVersion > enableSwitchReadOnly() const
See switchReadOnly() function in the register descriptor.
Definition: UnifiedBackendTest.h:103
ChimeraTK::TransferType::read
@ read
ChimeraTK::TestCapabilities::testReadOnly
static constexpr TestCapability testReadOnly
Definition: UnifiedBackendTest.h:222
ChimeraTK::UnifiedBackendTest::ExceptionReportingBackend::open
void open() override
Open the device.
Definition: UnifiedBackendTest.h:493
ChimeraTK::UnifiedBackendTest::test_B_8_5_2
void test_B_8_5_2()
Test initial value.
Definition: UnifiedBackendTest.h:2010
ChimeraTK::TestCapabilities::testRawTransfer
static constexpr TestCapability testRawTransfer
Definition: UnifiedBackendTest.h:223
ChimeraTK::UnifiedBackendTest::test_B_8_2
void test_B_8_2()
Test async read fills _readQueue.
Definition: UnifiedBackendTest.h:1594
ChimeraTK::UnifiedBackendTest::setForceDataLossWrite_proxy_helper< T, false >::setForceDataLossWrite_proxy_helper
setForceDataLossWrite_proxy_helper(T, bool)
Definition: UnifiedBackendTest.h:512
ChimeraTK::UnifiedBackendTest::addRegister
UnifiedBackendTest< typename boost::mpl::push_back< VECTOR_OF_REGISTERS_T, REG_T >::type > addRegister()
Add a register to be used by the test.
Definition: UnifiedBackendTest.h:352
ChimeraTK::UnifiedBackendTest::test_B_9_5
void test_B_9_5()
Test write operations throw after setException()
Definition: UnifiedBackendTest.h:2607
ChimeraTK::UnifiedBackendTest::switchReadOnly_proxy_helper
Definition: UnifiedBackendTest.h:544
ChimeraTK::logic_error
Exception thrown when a logic error has occured.
Definition: Exception.h:51
ChimeraTK::UnifiedBackendTest::test_B_9_2_2
void test_B_9_2_2()
Test repeated setException() has no effect (in particular, no additional exceptions in async transfer...
Definition: UnifiedBackendTest.h:2415
ChimeraTK::TestCapabilities::disableSetRemoteValueIncrementsVersion
constexpr TestCapabilities< _syncRead, _forceDataLossWrite, _asyncReadInconsistency, _switchReadOnly, _switchWriteOnly, _writeNeverLosesData, _testWriteOnly, _testReadOnly, _testRawTransfer, _testCatalogue, TestCapability::disabled > disableSetRemoteValueIncrementsVersion() const
Enable/disable testing of version number increment in read operations after setRemoteValue.
Definition: UnifiedBackendTest.h:205
ChimeraTK::UnifiedBackendTest::_testOnlyTransferElement
bool _testOnlyTransferElement
Flag whether to disable tests for the backend itself.
Definition: UnifiedBackendTest.h:471
ChimeraTK::TestCapabilities::disableSwitchWriteOnly
constexpr TestCapabilities< _syncRead, _forceDataLossWrite, _asyncReadInconsistency, _switchReadOnly, TestCapability::disabled, _writeNeverLosesData, _testWriteOnly, _testReadOnly, _testRawTransfer, _testCatalogue, _setRemoteValueIncrementsVersion > disableSwitchWriteOnly() const
Definition: UnifiedBackendTest.h:123
ChimeraTK::Device::setException
void setException(const std::string &message)
Set the device into an exception state.
Definition: Device.cc:97