ChimeraTK-DeviceAccess 03.27.00
Loading...
Searching...
No Matches
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>
9using namespace boost::unit_test_framework;
10
11#include "BackendFactory.h"
12#include "DummyBackend.h"
13#include "ProcessManagement.h"
14#include "SharedDummyBackend.h"
15#include "sharedDummyHelpers.h"
16#include "UnifiedBackendTest.h"
17
18using namespace ChimeraTK;
19
20// Create a test suite which holds all your tests.
21BOOST_AUTO_TEST_SUITE(SharedDummyBackendUnifiedTestSuite)
22
23/**********************************************************************************************************************/
24
25// Use hardcoded information from the dmap-file
26static std::string instanceId{"1"};
27static std::string mapFileName{"sharedDummyUnified.map"};
28static std::size_t instanceIdHash = Utilities::shmDummyInstanceIdHash(instanceId, {{"map", mapFileName}});
29static std::string shmName{Utilities::createShmName(instanceIdHash, mapFileName, getUserName())};
30
31static std::string cdd(std::string("(sharedMemoryDummy:") + instanceId + "?map=" + mapFileName + ")");
32static boost::shared_ptr<SharedDummyBackend> sharedDummy;
34
35// lock preventing concurrent test execution
36static TestLocker testLocker("sharedDummyUnified.dmap");
37
38// use static instance with destructor to stop background application after all tests
41 // set up sharedDummy as communication interface to background process
42 sharedDummy = boost::dynamic_pointer_cast<SharedDummyBackend>(BackendFactory::getInstance().createBackend(cdd));
43 sharedDummy->open();
44 mirrorRequest.type = sharedDummy->getRegisterAccessor<uint32_t>("MIRRORREQUEST/TYPE", 1, 0, AccessModeFlags{});
45 mirrorRequest.busy = sharedDummy->getRegisterAccessor<uint32_t>("MIRRORREQUEST/BUSY", 1, 0, AccessModeFlags{});
46 // TODO debug - it seems this register causes clean-up to fail
47 // Potentially because there is still a reference in the TransferGroup in the asyncInterruptDispatcher, causing a
48 // circular reference to the device shared pointer
49 mirrorRequest.updated = sharedDummy->getRegisterAccessor<uint32_t>(
50 "MIRRORREQUEST/UPDATED", 1, 0, AccessModeFlags{AccessMode::wait_for_new_data});
51 mirrorRequest.triggerInterrupt =
52 sharedDummy->getRegisterAccessor<uint32_t>("MIRRORREQUEST/DATA_INTERRUPT", 1, 0, {});
53 }
54
55 struct {
56 boost::shared_ptr<NDRegisterAccessor<uint32_t>> type;
57 boost::shared_ptr<NDRegisterAccessor<uint32_t>> busy;
58 boost::shared_ptr<NDRegisterAccessor<uint32_t>> updated;
59 boost::shared_ptr<NDRegisterAccessor<uint32_t>> triggerInterrupt;
61
62 void requestMirroring(MirrorRequestType reqType, bool triggerDataInterrupt = false) const {
63 sharedDummy->open();
64 // trigger mirror operation by helper thread and wait on completion
65 mirrorRequest.triggerInterrupt->accessData(0) = triggerDataInterrupt ? 1 : 0;
66 mirrorRequest.triggerInterrupt->write();
67 mirrorRequest.type->accessData(0) = (int)reqType;
68 mirrorRequest.type->write();
69 mirrorRequest.busy->accessData(0) = 1;
70 mirrorRequest.busy->write();
71 int timeoutCnt = timeOutForWaitOnHelperProcess_ms / 50;
72 do {
73 // we use boost::sleep because it defines an interruption point for signals
74 boost::this_thread::sleep_for(boost::chrono::milliseconds(50));
75 mirrorRequest.busy->readLatest();
76 } while(mirrorRequest.busy->accessData(0) == 1 && (--timeoutCnt >= 0));
77 BOOST_CHECK(timeoutCnt >= 0);
78 }
79
80 static void start() {
81 // start second accessing application in background
82 BOOST_CHECK(!std::system("./testSharedDummyBackendUnifiedExt "
83 "--run_test=SharedDummyBackendUnifiedTestSuite/testRegisterAccessor > /dev/null"
84 " & echo $! > ./testSharedDummyBackendUnifiedExt.pid"));
85 // check that the helper application is running
86 boost::this_thread::sleep_for(boost::chrono::milliseconds(100));
87 BOOST_REQUIRE_MESSAGE(std::system("ps $(cat testSharedDummyBackendUnifiedExt.pid) > /dev/null") == 0,
88 "FATAL: background application \'testSharedDummyBackendUnifiedExt\' not running!");
89 }
90 // request helper to stop gracefully - this includes a handshake waiting on it's termination
92 static void kill() {
93 auto ret = std::system("pidfile=./testSharedDummyBackendUnifiedExt.pid; if [ -f $pidfile ]; "
94 "then kill $(cat $pidfile); rm $pidfile; fi ");
95 if(ret == -1) {
96 throw std::runtime_error("Attempt to kill helper process failed.");
97 }
98 }
99 // discard all accessors. do not use after this point
100 void reset() {
101 mirrorRequest.type.reset();
102 mirrorRequest.busy.reset();
103 mirrorRequest.updated.reset();
104 mirrorRequest.triggerInterrupt.reset();
105 }
107 try {
108 kill();
109 }
110 catch(std::exception& e) {
111 // Try-catch to make the linter happy. We in the process of killing a global instance, so at the end of the test, anyway.
112 std::cerr << "Caught exception when killing helper process: " << e.what() << std::endl;
113 }
114 }
116
117/**********************************************************************************************************************/
118
119template<typename Derived>
121 Derived* derived{static_cast<Derived*>(this)};
123 size_t nChannels() { return 1; }
124 size_t nElementsPerChannel() { return 1; }
125 size_t writeQueueLength() { return std::numeric_limits<size_t>::max(); }
126 size_t nRuntimeErrorCases() { return 0; }
127 using minimumUserType = int32_t;
129
130 static constexpr auto capabilities = TestCapabilities<>()
132 .disableSwitchReadOnly()
133 .disableSwitchWriteOnly()
134 .disableTestWriteNeverLosesData()
135 .disableAsyncReadInconsistency()
136 .enableTestRawTransfer();
137
138 boost::shared_ptr<NDRegisterAccessor<minimumUserType>> acc{
139 sharedDummy->getRegisterAccessor<minimumUserType>(derived->path(), 1, 0, AccessModeFlags{})};
140 boost::shared_ptr<NDRegisterAccessor<rawUserType>> accBackdoor{sharedDummy->getRegisterAccessor<rawUserType>(
141 std::string("MIRRORED/") + derived->path(true), 1, 0, {AccessMode::raw})};
142
143 void ensureOpen() {
144 // since the front-door and back-door access goes over the same SharedDummyBackend instance, the spec tests
145 // unintentionally also close our back-door and we need to make sure it's open again.
146 sharedDummy->open();
147 }
148
149 // Type can be raw type or user type
150 template<typename Type>
151 std::vector<std::vector<Type>> generateValue(bool raw = false) {
152 ensureOpen();
153 accBackdoor->readLatest();
154 rawUserType rawVal00 = accBackdoor->accessData(0);
155 rawVal00 += 3;
156 Type val00 = (raw ? rawVal00 : derived->template rawToCooked<Type, rawUserType>(rawVal00));
157 return {{val00}};
158 }
159
160 // Type can be raw type or user type
161 template<typename Type>
162 std::vector<std::vector<Type>> getRemoteValue(bool raw = false) {
163 ensureOpen();
165 accBackdoor->readLatest();
166 rawUserType rawVal00 = accBackdoor->accessData(0);
167 Type val00 = (raw ? rawVal00 : derived->template rawToCooked<Type, rawUserType>(rawVal00));
168
169 return {{val00}};
170 }
171
173 ensureOpen();
174 auto x = generateValue<rawUserType>(/* raw = */ true)[0][0];
175 accBackdoor->accessData(0) = x;
176 accBackdoor->write();
178 }
179
180 // default implementation just casting. Re-implement in derived classes if needed.
181 template<typename UserType, typename RawType>
182 RawType cookedToRaw(UserType val) {
183 return static_cast<RawType>(val);
184 }
185
186 // default implementation just casting. Re-implement in derived classes if needed.
187 template<typename UserType, typename RawType>
188 UserType rawToCooked(RawType val) {
189 return static_cast<UserType>(val);
190 }
191
192 // we need this because it's expected in template, but unused
193 void setForceRuntimeError(bool /*enable*/, size_t) {}
194};
195
196struct IntegersSigned32 : public IntegersBase<IntegersSigned32> {
197 static std::string path([[maybe_unused]] bool mirrored = false) { return "INTC_RW"; }
198 static bool isWriteable() { return true; }
199 static bool isReadable() { return true; }
200};
201struct IntegersSigned32RO : public IntegersBase<IntegersSigned32RO> {
202 static std::string path([[maybe_unused]] bool mirrored = false) { return "INTA_RO"; }
203 static bool isWriteable() { return false; }
204 static bool isReadable() { return true; }
205};
206struct IntegersSigned32WO : public IntegersBase<IntegersSigned32WO> {
207 static std::string path([[maybe_unused]] bool mirrored = false) { return "INTB_WO"; }
208 static bool isWriteable() { return true; }
209 static bool isReadable() { return false; }
210};
211struct IntegersSigned32DummyWritable : public IntegersBase<IntegersSigned32DummyWritable> {
212 static std::string path(bool mirrored = false) { return !mirrored ? "INTA_RO/DUMMY_WRITEABLE" : "INTA_RO"; }
213 static bool isWriteable() { return true; }
214 static bool isReadable() { return true; }
215};
216
217struct IntegersSigned32Async : public IntegersBase<IntegersSigned32Async> {
218 static int value;
219 static std::string path([[maybe_unused]] bool mirrored = false) { return "INTD_ASYNC"; }
220 static bool isWriteable() { return false; }
221 static bool isReadable() { return true; }
225
226 template<typename UserType>
227 std::vector<std::vector<UserType>> generateValue(bool = false) {
228 return {{++value}};
229 }
230
232 ensureOpen();
233 auto x = generateValue<minimumUserType>()[0][0];
234 accBackdoor->accessData(0) = x;
235 accBackdoor->write();
237 }
238};
239
241
242/**********************************************************************************************************************/
243
244BOOST_AUTO_TEST_CASE(TestRegisterAccessor) {
245 SharedDummyBackendUnifiedTestSuite::HelperProcess::start();
246
247 std::cout << "*** testRegisterAccessor *** " << std::endl;
250 .addRegister<IntegersSigned32RO>()
251 .addRegister<IntegersSigned32WO>()
252 .addRegister<IntegersSigned32DummyWritable>()
253 .addRegister<IntegersSigned32Async>()
254 .runTests(cdd);
255
256 SharedDummyBackendUnifiedTestSuite::HelperProcess::kill();
257}
258
262BOOST_AUTO_TEST_CASE(TestVerifyMemoryDeleted) {
263 SharedDummyBackendUnifiedTestSuite::HelperProcess::start();
266
267 // also clear our backend instance. This should also remove allocated SHM segments and semaphores
268 // - note, this only works if the global instance map uses weak pointers
269 sharedDummy.reset();
270
271 // Check that memory is removed
272 bool shm_removed{false};
273 for(size_t n = 0; n < 30; ++n) {
274 shm_removed = !shm_exists(shmName);
275 if(shm_removed) {
276 break;
277 }
278 sleep(1);
279 }
280 BOOST_CHECK(shm_removed);
281}
282
283BOOST_AUTO_TEST_SUITE_END()
std::string getUserName()
Set of AccessMode flags with additional functionality for an easier handling.
Definition AccessMode.h:48
static BackendFactory & getInstance()
Static function to get an instance of factory.
Class to test any backend for correct behaviour.
UnifiedBackendTest< typename boost::mpl::push_back< VECTOR_OF_REGISTERS_T, REG_T >::type > addRegister()
Add a register to be used by the test.
std::size_t shmDummyInstanceIdHash(const std::string &address, const std::map< std::string, std::string > &parameters)
Generates shm dummy instanceId hash from address and parameter map, Intended for use with parseDevice...
std::string createShmName(std::size_t instanceIdHash, const std::string &mapFileName, const std::string &userName)
Generates shm dummy name from parameter hashes.
@ wait_for_new_data
Make any read blocking until new data has arrived since the last read.
@ raw
Raw access: disable any possible conversion from the original hardware data type into the given UserT...
MirrorRequestType
bool shm_exists(std::string shmName)
Descriptor for the test capabilities for each register.
constexpr TestCapabilities< _syncRead, TestCapability::disabled, _asyncReadInconsistency, _switchReadOnly, _switchWriteOnly, _writeNeverLosesData, _testWriteOnly, _testReadOnly, _testRawTransfer, _testCatalogue, _setRemoteValueIncrementsVersion, _testPartialAccessor > disableForceDataLossWrite() const
void requestMirroring(MirrorRequestType reqType, bool triggerDataInterrupt=false) const
boost::shared_ptr< NDRegisterAccessor< uint32_t > > updated
struct HelperProcess::@1 mirrorRequest
boost::shared_ptr< NDRegisterAccessor< uint32_t > > triggerInterrupt
boost::shared_ptr< NDRegisterAccessor< uint32_t > > type
boost::shared_ptr< NDRegisterAccessor< uint32_t > > busy
std::vector< std::vector< Type > > generateValue(bool raw=false)
boost::shared_ptr< NDRegisterAccessor< minimumUserType > > acc
ChimeraTK::AccessModeFlags supportedFlags()
static constexpr auto capabilities
boost::shared_ptr< NDRegisterAccessor< rawUserType > > accBackdoor
UserType rawToCooked(RawType val)
std::vector< std::vector< Type > > getRemoteValue(bool raw=false)
RawType cookedToRaw(UserType val)
void setForceRuntimeError(bool, size_t)
std::vector< std::vector< UserType > > generateValue(bool=false)
static std::string path(bool mirrored=false)
static ChimeraTK::AccessModeFlags supportedFlags()
static std::string path(bool mirrored=false)
static std::string path(bool mirrored=false)
static std::string path(bool mirrored=false)
static std::string path(bool mirrored=false)
std::string cdd
const int timeOutForWaitOnHelperProcess_ms
struct HelperProcess gHelperProcess
BOOST_AUTO_TEST_CASE(TestRegisterAccessor)