ChimeraTK-DeviceAccess  03.18.00
testSharedDummyBackendUnified.cpp
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: Deutsches Elektronen-Synchrotron DESY, MSK, ChimeraTK Project <chimeratk-support@desy.de>
2 // SPDX-License-Identifier: LGPL-3.0-or-later
3 
4 #define BOOST_TEST_DYN_LINK
5 // Define a name for the test module.
6 #define BOOST_TEST_MODULE SharedDummyBackendUnified
7 // Only after defining the name include the unit test header.
8 #include <boost/test/unit_test.hpp>
9 using namespace boost::unit_test_framework;
10 
11 #include "BackendFactory.h"
12 #include "Device.h"
13 #include "DummyBackend.h"
14 #include "DummyRegisterAccessor.h"
15 #include "SharedDummyBackend.h"
16 #include "sharedDummyHelpers.h"
17 #include "TransferGroup.h"
18 #include "UnifiedBackendTest.h"
19 
20 using namespace ChimeraTK;
21 
22 // Create a test suite which holds all your tests.
23 BOOST_AUTO_TEST_SUITE(SharedDummyBackendUnifiedTestSuite)
24 
25 /**********************************************************************************************************************/
26 
27 // Use hardcoded information from the dmap-file
28 static std::string instanceId{"1"};
29 static std::string mapFileName{"sharedDummyUnified.map"};
30 static std::string cdd(std::string("(sharedMemoryDummy:") + instanceId + "?map=" + mapFileName + ")");
31 static boost::shared_ptr<SharedDummyBackend> sharedDummy;
33 
34 // lock preventing concurrent test execution
35 static TestLocker testLocker("sharedDummyUnified.dmap");
36 
37 // use static instance with destructor to stop background application after all tests
38 struct HelperProcess {
40  // set up sharedDummy as communication interface to background process
41  sharedDummy = boost::dynamic_pointer_cast<SharedDummyBackend>(BackendFactory::getInstance().createBackend(cdd));
42  sharedDummy->open();
43  mirrorRequest.type = sharedDummy->getRegisterAccessor<uint32_t>("MIRRORREQUEST/TYPE", 1, 0, AccessModeFlags{});
44  mirrorRequest.busy = sharedDummy->getRegisterAccessor<uint32_t>("MIRRORREQUEST/BUSY", 1, 0, AccessModeFlags{});
45  // TODO debug - it seems this register causes clean-up to fail
46  // Potentially because there is still a reference in the TransferGroup in the asyncInterruptDispatcher, causing a
47  // circular reference to the device shared pointer
48  mirrorRequest.updated = sharedDummy->getRegisterAccessor<uint32_t>(
49  "MIRRORREQUEST/UPDATED", 1, 0, AccessModeFlags{AccessMode::wait_for_new_data});
50  mirrorRequest.triggerInterrupt =
51  sharedDummy->getRegisterAccessor<uint32_t>("MIRRORREQUEST/DATA_INTERRUPT", 1, 0, {});
52  }
53 
54  struct {
55  boost::shared_ptr<NDRegisterAccessor<uint32_t>> type;
56  boost::shared_ptr<NDRegisterAccessor<uint32_t>> busy;
57  boost::shared_ptr<NDRegisterAccessor<uint32_t>> updated;
58  boost::shared_ptr<NDRegisterAccessor<uint32_t>> triggerInterrupt;
59  } mirrorRequest;
60 
61  void requestMirroring(MirrorRequestType reqType, bool triggerDataInterrupt = false) {
62  sharedDummy->open();
63  // trigger mirror operation by helper thread and wait on completion
64  mirrorRequest.triggerInterrupt->accessData(0) = triggerDataInterrupt ? 1 : 0;
65  mirrorRequest.triggerInterrupt->write();
66  mirrorRequest.type->accessData(0) = (int)reqType;
67  mirrorRequest.type->write();
68  mirrorRequest.busy->accessData(0) = 1;
69  mirrorRequest.busy->write();
70  int timeoutCnt = timeOutForWaitOnHelperProcess_ms / 50;
71  do {
72  // we use boost::sleep because it defines an interruption point for signals
73  boost::this_thread::sleep_for(boost::chrono::milliseconds(50));
74  mirrorRequest.busy->readLatest();
75  } while(mirrorRequest.busy->accessData(0) == 1 && (--timeoutCnt >= 0));
76  BOOST_CHECK(timeoutCnt >= 0);
77  }
78 
79  void start() {
80  // start second accessing application in background
81  BOOST_CHECK(!std::system("./testSharedDummyBackendUnifiedExt "
82  "--run_test=SharedDummyBackendUnifiedTestSuite/testRegisterAccessor > /dev/null"
83  " & echo $! > ./testSharedDummyBackendUnifiedExt.pid"));
84  // check that the helper application is running
85  boost::this_thread::sleep_for(boost::chrono::milliseconds(100));
86  BOOST_REQUIRE_MESSAGE(std::system("ps $(cat testSharedDummyBackendUnifiedExt.pid) > /dev/null") == 0,
87  "FATAL: background application \'testSharedDummyBackendUnifiedExt\' not running!");
88  }
89  // request helper to stop gracefully - this includes a handshake waiting on it's termination
90  void stopGracefully() { requestMirroring(MirrorRequestType::stop); }
91  void kill() {
92  auto ret = std::system("pidfile=./testSharedDummyBackendUnifiedExt.pid; if [ -f $pidfile ]; "
93  "then kill $(cat $pidfile); rm $pidfile; fi ");
94  if(ret == -1) {
95  throw std::runtime_error("Attempt to kill helper process failed.");
96  }
97  }
98  // discard all accessors. do not use after this point
99  void reset() {
100  mirrorRequest.type.reset();
101  mirrorRequest.busy.reset();
102  mirrorRequest.updated.reset();
103  mirrorRequest.triggerInterrupt.reset();
104  }
105  ~HelperProcess() { kill(); }
107 
108 /**********************************************************************************************************************/
109 
110 template<typename Derived>
112  Derived* derived{static_cast<Derived*>(this)};
114  size_t nChannels() { return 1; }
115  size_t nElementsPerChannel() { return 1; }
116  size_t writeQueueLength() { return std::numeric_limits<size_t>::max(); }
117  size_t nRuntimeErrorCases() { return 0; }
118  typedef int32_t minimumUserType;
120 
121  static constexpr auto capabilities = TestCapabilities<>()
123  .disableSwitchReadOnly()
124  .disableSwitchWriteOnly()
125  .disableTestWriteNeverLosesData()
126  .disableAsyncReadInconsistency()
127  .enableTestRawTransfer();
128 
129  boost::shared_ptr<NDRegisterAccessor<minimumUserType>> acc{
130  sharedDummy->getRegisterAccessor<minimumUserType>(derived->path(), 1, 0, AccessModeFlags{})};
131  boost::shared_ptr<NDRegisterAccessor<rawUserType>> accBackdoor{sharedDummy->getRegisterAccessor<rawUserType>(
132  std::string("MIRRORED/") + derived->path(), 1, 0, {AccessMode::raw})};
133 
134  void ensureOpen() {
135  // since the front-door and back-door access goes over the same SharedDummyBackend instance, the spec tests
136  // unintentionally also close our back-door and we need to make sure it's open again.
137  sharedDummy->open();
138  }
139 
140  // Type can be raw type or user type
141  template<typename Type>
142  std::vector<std::vector<Type>> generateValue(bool raw = false) {
143  ensureOpen();
144  accBackdoor->readLatest();
145  rawUserType rawVal00 = accBackdoor->accessData(0);
146  rawVal00 += 3;
147  Type val00 = (raw ? rawVal00 : derived->template rawToCooked<Type, rawUserType>(rawVal00));
148  return {{val00}};
149  }
150 
151  // Type can be raw type or user type
152  template<typename Type>
153  std::vector<std::vector<Type>> getRemoteValue(bool raw = false) {
154  ensureOpen();
156  accBackdoor->readLatest();
157  rawUserType rawVal00 = accBackdoor->accessData(0);
158  Type val00 = (raw ? rawVal00 : derived->template rawToCooked<Type, rawUserType>(rawVal00));
159 
160  return {{val00}};
161  }
162 
163  void setRemoteValue() {
164  ensureOpen();
165  auto x = generateValue<rawUserType>(/* raw = */ true)[0][0];
166  accBackdoor->accessData(0) = x;
167  accBackdoor->write();
169  }
170 
171  // default implementation just casting. Re-implement in derived classes if needed.
172  template<typename UserType, typename RawType>
173  RawType cookedToRaw(UserType val) {
174  return static_cast<RawType>(val);
175  }
176 
177  // default implementation just casting. Re-implement in derived classes if needed.
178  template<typename UserType, typename RawType>
179  UserType rawToCooked(RawType val) {
180  return static_cast<UserType>(val);
181  }
182 
183  // we need this because it's expected in template, but unused
184  void setForceRuntimeError(bool /*enable*/, size_t) {}
185 };
186 
187 struct Integers_signed32 : public Integers_base<Integers_signed32> {
188  std::string path() { return "INTC_RW"; }
189  bool isWriteable() { return true; }
190  bool isReadable() { return true; }
191 };
192 struct Integers_signed32_RO : public Integers_base<Integers_signed32_RO> {
193  std::string path() { return "INTA_RO"; }
194  bool isWriteable() { return false; }
195  bool isReadable() { return true; }
196 };
197 struct Integers_signed32_WO : public Integers_base<Integers_signed32_WO> {
198  std::string path() { return "INTB_WO"; }
199  bool isWriteable() { return true; }
200  bool isReadable() { return false; }
201 };
202 struct Integers_signed32_DummyWritable : public Integers_base<Integers_signed32_DummyWritable> {
203  std::string path() { return "INTA_RO/DUMMY_WRITEABLE"; }
204  bool isWriteable() { return true; }
205  bool isReadable() { return true; }
206 };
207 
208 struct Integers_signed32_async : public Integers_base<Integers_signed32_async> {
209  static int value;
210  std::string path() { return "INTD_ASYNC"; }
211  bool isWriteable() { return false; }
212  bool isReadable() { return true; }
215  }
216 
217  template<typename UserType>
218  std::vector<std::vector<UserType>> generateValue(bool = false) {
219  return {{++value}};
220  }
221 
222  void setRemoteValue() {
223  ensureOpen();
224  auto x = generateValue<minimumUserType>()[0][0];
225  accBackdoor->accessData(0) = x;
226  accBackdoor->write();
228  }
229 };
230 
232 
233 /**********************************************************************************************************************/
234 
235 BOOST_AUTO_TEST_CASE(testRegisterAccessor) {
237 
238  std::cout << "*** testRegisterAccessor *** " << std::endl;
241  .addRegister<Integers_signed32_RO>()
242  .addRegister<Integers_signed32_WO>()
243  .addRegister<Integers_signed32_DummyWritable>()
244  .addRegister<Integers_signed32_async>()
245  .runTests(cdd);
246 
248 }
249 
253 BOOST_AUTO_TEST_CASE(testVerifyMemoryDeleted) {
257 
258  // also clear our backend instance. This should also remove allocated SHM segments and semaphores
259  // - note, this only works if the global instance map uses weak pointers
260  sharedDummy.reset();
261 
262  boost::filesystem::path absPathToMapFile = boost::filesystem::absolute(mapFileName);
263  std::string shmName{createExpectedShmName(instanceId, absPathToMapFile.string(), getUserName())};
264 
265  // Check that memory is removed
266  BOOST_CHECK(!shm_exists(shmName));
267 }
268 
269 BOOST_AUTO_TEST_SUITE_END()
Integers_signed32_async::supportedFlags
ChimeraTK::AccessModeFlags supportedFlags()
Definition: testSharedDummyBackendUnified.cpp:213
TransferGroup.h
ChimeraTK::AccessMode::raw
@ raw
Raw access: disable any possible conversion from the original hardware data type into the given UserT...
Integers_signed32_DummyWritable
Definition: testSharedDummyBackendUnified.cpp:202
HelperProcess::requestMirroring
void requestMirroring(MirrorRequestType reqType, bool triggerDataInterrupt=false)
Definition: testSharedDummyBackendUnified.cpp:61
Integers_signed32_WO::isReadable
bool isReadable()
Definition: testSharedDummyBackendUnified.cpp:200
Integers_signed32_RO::path
std::string path()
Definition: testSharedDummyBackendUnified.cpp:193
HelperProcess::reset
void reset()
Definition: testSharedDummyBackendUnified.cpp:99
HelperProcess::~HelperProcess
~HelperProcess()
Definition: testSharedDummyBackendUnified.cpp:105
HelperProcess::triggerInterrupt
boost::shared_ptr< NDRegisterAccessor< uint32_t > > triggerInterrupt
Definition: testSharedDummyBackendUnified.cpp:58
Integers_base::setForceRuntimeError
void setForceRuntimeError(bool, size_t)
Definition: testSharedDummyBackendUnified.cpp:184
Integers_signed32_WO::path
std::string path()
Definition: testSharedDummyBackendUnified.cpp:198
Integers_base::generateValue
std::vector< std::vector< Type > > generateValue(bool raw=false)
Definition: testSharedDummyBackendUnified.cpp:142
Integers_base::rawUserType
minimumUserType rawUserType
Definition: testSharedDummyBackendUnified.cpp:119
HelperProcess::kill
void kill()
Definition: testSharedDummyBackendUnified.cpp:91
DummyBackend.h
Integers_signed32_RO
Definition: testSharedDummyBackendUnified.cpp:192
MirrorRequestType::stop
@ stop
Integers_signed32_WO
Definition: testSharedDummyBackendUnified.cpp:197
Integers_signed32
Definition: testNumericAddressedBackendUnified.cpp:44
HelperProcess::stopGracefully
void stopGracefully()
Definition: testSharedDummyBackendUnified.cpp:90
SharedDummyBackend.h
MirrorRequestType::from
@ from
TestLocker
Definition: testPcieBackend.cpp:51
Integers_base::getRemoteValue
std::vector< std::vector< Type > > getRemoteValue(bool raw=false)
Definition: testSharedDummyBackendUnified.cpp:153
HelperProcess::updated
boost::shared_ptr< NDRegisterAccessor< uint32_t > > updated
Definition: testSharedDummyBackendUnified.cpp:57
Integers_signed32::isWriteable
bool isWriteable()
Definition: testSharedDummyBackendUnified.cpp:189
MirrorRequestType::to
@ to
cdd
std::string cdd
Definition: testAsyncRead.cpp:25
ChimeraTK::TestCapabilities
Descriptor for the test capabilities for each register.
Definition: UnifiedBackendTest.h:54
Integers_base::ensureOpen
void ensureOpen()
Definition: testSharedDummyBackendUnified.cpp:134
BOOST_AUTO_TEST_CASE
BOOST_AUTO_TEST_CASE(testRegisterAccessor)
Definition: testSharedDummyBackendUnified.cpp:235
Integers_signed32_async::isReadable
bool isReadable()
Definition: testSharedDummyBackendUnified.cpp:212
Integers_base::writeQueueLength
size_t writeQueueLength()
Definition: testSharedDummyBackendUnified.cpp:116
Integers_base::supportedFlags
ChimeraTK::AccessModeFlags supportedFlags()
Definition: testSharedDummyBackendUnified.cpp:113
timeOutForWaitOnHelperProcess_ms
const int timeOutForWaitOnHelperProcess_ms
Definition: testSharedDummyBackendUnified.cpp:32
Integers_base::cookedToRaw
RawType cookedToRaw(UserType val)
Definition: testSharedDummyBackendUnified.cpp:173
HelperProcess::HelperProcess
HelperProcess()
Definition: testSharedDummyBackendUnified.cpp:39
Integers_signed32_DummyWritable::path
std::string path()
Definition: testSharedDummyBackendUnified.cpp:203
Integers_signed32_RO::isWriteable
bool isWriteable()
Definition: testSharedDummyBackendUnified.cpp:194
HelperProcess::start
void start()
Definition: testSharedDummyBackendUnified.cpp:79
Integers_base::nRuntimeErrorCases
size_t nRuntimeErrorCases()
Definition: testSharedDummyBackendUnified.cpp:117
Integers_base::minimumUserType
int32_t minimumUserType
Definition: testSharedDummyBackendUnified.cpp:118
ChimeraTK::AccessMode::wait_for_new_data
@ wait_for_new_data
Make any read blocking until new data has arrived since the last read.
Integers_signed32::isReadable
bool isReadable()
Definition: testSharedDummyBackendUnified.cpp:190
Device.h
Integers_signed32::path
std::string path()
Definition: testSharedDummyBackendUnified.cpp:188
createExpectedShmName
std::string createExpectedShmName(std::string instanceId_, std::string mapFileName_, std::string userName)
Definition: sharedDummyHelpers.h:16
gHelperProcess
struct HelperProcess gHelperProcess
MirrorRequestType
MirrorRequestType
Definition: sharedDummyHelpers.h:13
Integers_base
Definition: testSharedDummyBackendUnified.cpp:111
ChimeraTK::UnifiedBackendTest
Class to test any backend for correct behaviour.
Definition: UnifiedBackendTest.h:259
sharedDummyHelpers.h
shm_exists
bool shm_exists(std::string shmName)
Definition: sharedDummyHelpers.h:24
Integers_signed32_RO::isReadable
bool isReadable()
Definition: testSharedDummyBackendUnified.cpp:195
Integers_signed32_async::value
static int value
Definition: testSharedDummyBackendUnified.cpp:209
ChimeraTK::TestCapabilities::disableForceDataLossWrite
constexpr TestCapabilities< _syncRead, TestCapability::disabled, _asyncReadInconsistency, _switchReadOnly, _switchWriteOnly, _writeNeverLosesData, _testWriteOnly, _testReadOnly, _testRawTransfer, _testCatalogue, _setRemoteValueIncrementsVersion > disableForceDataLossWrite() const
Definition: UnifiedBackendTest.h:81
HelperProcess
Definition: testSharedDummyBackendUnified.cpp:38
Integers_signed32_WO::isWriteable
bool isWriteable()
Definition: testSharedDummyBackendUnified.cpp:199
Integers_signed32_async::isWriteable
bool isWriteable()
Definition: testSharedDummyBackendUnified.cpp:211
Integers_base::nChannels
size_t nChannels()
Definition: testSharedDummyBackendUnified.cpp:114
DummyRegisterAccessor.h
Integers_signed32_async
Definition: testNumericAddressedBackendUnified.cpp:85
BackendFactory.h
Integers_base::rawToCooked
UserType rawToCooked(RawType val)
Definition: testSharedDummyBackendUnified.cpp:179
Integers_signed32_async::generateValue
std::vector< std::vector< UserType > > generateValue(bool=false)
Definition: testSharedDummyBackendUnified.cpp:218
HelperProcess::busy
boost::shared_ptr< NDRegisterAccessor< uint32_t > > busy
Definition: testSharedDummyBackendUnified.cpp:56
Integers_signed32_async::setRemoteValue
void setRemoteValue()
Definition: testSharedDummyBackendUnified.cpp:222
HelperProcess::type
boost::shared_ptr< NDRegisterAccessor< uint32_t > > type
Definition: testSharedDummyBackendUnified.cpp:55
Integers_base::nElementsPerChannel
size_t nElementsPerChannel()
Definition: testSharedDummyBackendUnified.cpp:115
ChimeraTK::AccessModeFlags
Set of AccessMode flags with additional functionality for an easier handling.
Definition: AccessMode.h:48
Integers_base::setRemoteValue
void setRemoteValue()
Definition: testSharedDummyBackendUnified.cpp:163
ChimeraTK
Definition: DummyBackend.h:16
UnifiedBackendTest.h
Integers_signed32_async::path
std::string path()
Definition: testSharedDummyBackendUnified.cpp:210
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
Integers_signed32_DummyWritable::isReadable
bool isReadable()
Definition: testSharedDummyBackendUnified.cpp:205
Integers_signed32_DummyWritable::isWriteable
bool isWriteable()
Definition: testSharedDummyBackendUnified.cpp:204
getUserName
std::string getUserName()
Definition: ProcessManagement.cpp:24