ChimeraTK-DeviceAccess 03.25.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 "SharedDummyBackend.h"
14#include "sharedDummyHelpers.h"
15#include "UnifiedBackendTest.h"
16
17using namespace ChimeraTK;
18
19// Create a test suite which holds all your tests.
20BOOST_AUTO_TEST_SUITE(SharedDummyBackendUnifiedTestSuite)
21
22/**********************************************************************************************************************/
23
24// Use hardcoded information from the dmap-file
25static std::string instanceId{"1"};
26static std::string mapFileName{"sharedDummyUnified.map"};
27static std::size_t instanceIdHash = Utilities::shmDummyInstanceIdHash(instanceId, {{"map", mapFileName}});
28static std::string shmName{Utilities::createShmName(instanceIdHash, mapFileName, getUserName())};
29
30static std::string cdd(std::string("(sharedMemoryDummy:") + instanceId + "?map=" + mapFileName + ")");
31static boost::shared_ptr<SharedDummyBackend> sharedDummy;
33
34// lock preventing concurrent test execution
35static TestLocker testLocker("sharedDummyUnified.dmap");
36
37// use static instance with destructor to stop background application after all tests
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;
60
61 void requestMirroring(MirrorRequestType reqType, bool triggerDataInterrupt = false) const {
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 static 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
91 static 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 }
106 try {
107 kill();
108 }
109 catch(std::exception& e) {
110 // Try-catch to make the linter happy. We in the process of killing a global instance, so at the end of the test, anyway.
111 std::cerr << "Caught exception when killing helper process: " << e.what() << std::endl;
112 }
113 }
115
116/**********************************************************************************************************************/
117
118template<typename Derived>
120 Derived* derived{static_cast<Derived*>(this)};
122 size_t nChannels() { return 1; }
123 size_t nElementsPerChannel() { return 1; }
124 size_t writeQueueLength() { return std::numeric_limits<size_t>::max(); }
125 size_t nRuntimeErrorCases() { return 0; }
126 using minimumUserType = int32_t;
128
129 static constexpr auto capabilities = TestCapabilities<>()
131 .disableSwitchReadOnly()
132 .disableSwitchWriteOnly()
133 .disableTestWriteNeverLosesData()
134 .disableAsyncReadInconsistency()
135 .enableTestRawTransfer();
136
137 boost::shared_ptr<NDRegisterAccessor<minimumUserType>> acc{
138 sharedDummy->getRegisterAccessor<minimumUserType>(derived->path(), 1, 0, AccessModeFlags{})};
139 boost::shared_ptr<NDRegisterAccessor<rawUserType>> accBackdoor{sharedDummy->getRegisterAccessor<rawUserType>(
140 std::string("MIRRORED/") + derived->path(true), 1, 0, {AccessMode::raw})};
141
142 void ensureOpen() {
143 // since the front-door and back-door access goes over the same SharedDummyBackend instance, the spec tests
144 // unintentionally also close our back-door and we need to make sure it's open again.
145 sharedDummy->open();
146 }
147
148 // Type can be raw type or user type
149 template<typename Type>
150 std::vector<std::vector<Type>> generateValue(bool raw = false) {
151 ensureOpen();
152 accBackdoor->readLatest();
153 rawUserType rawVal00 = accBackdoor->accessData(0);
154 rawVal00 += 3;
155 Type val00 = (raw ? rawVal00 : derived->template rawToCooked<Type, rawUserType>(rawVal00));
156 return {{val00}};
157 }
158
159 // Type can be raw type or user type
160 template<typename Type>
161 std::vector<std::vector<Type>> getRemoteValue(bool raw = false) {
162 ensureOpen();
164 accBackdoor->readLatest();
165 rawUserType rawVal00 = accBackdoor->accessData(0);
166 Type val00 = (raw ? rawVal00 : derived->template rawToCooked<Type, rawUserType>(rawVal00));
167
168 return {{val00}};
169 }
170
172 ensureOpen();
173 auto x = generateValue<rawUserType>(/* raw = */ true)[0][0];
174 accBackdoor->accessData(0) = x;
175 accBackdoor->write();
177 }
178
179 // default implementation just casting. Re-implement in derived classes if needed.
180 template<typename UserType, typename RawType>
181 RawType cookedToRaw(UserType val) {
182 return static_cast<RawType>(val);
183 }
184
185 // default implementation just casting. Re-implement in derived classes if needed.
186 template<typename UserType, typename RawType>
187 UserType rawToCooked(RawType val) {
188 return static_cast<UserType>(val);
189 }
190
191 // we need this because it's expected in template, but unused
192 void setForceRuntimeError(bool /*enable*/, size_t) {}
193};
194
195struct IntegersSigned32 : public IntegersBase<IntegersSigned32> {
196 static std::string path([[maybe_unused]] bool mirrored = false) { return "INTC_RW"; }
197 static bool isWriteable() { return true; }
198 static bool isReadable() { return true; }
199};
200struct IntegersSigned32RO : public IntegersBase<IntegersSigned32RO> {
201 static std::string path([[maybe_unused]] bool mirrored = false) { return "INTA_RO"; }
202 static bool isWriteable() { return false; }
203 static bool isReadable() { return true; }
204};
205struct IntegersSigned32WO : public IntegersBase<IntegersSigned32WO> {
206 static std::string path([[maybe_unused]] bool mirrored = false) { return "INTB_WO"; }
207 static bool isWriteable() { return true; }
208 static bool isReadable() { return false; }
209};
210struct IntegersSigned32DummyWritable : public IntegersBase<IntegersSigned32DummyWritable> {
211 static std::string path(bool mirrored = false) { return !mirrored ? "INTA_RO/DUMMY_WRITEABLE" : "INTA_RO"; }
212 static bool isWriteable() { return true; }
213 static bool isReadable() { return true; }
214};
215
216struct IntegersSigned32Async : public IntegersBase<IntegersSigned32Async> {
217 static int value;
218 static std::string path([[maybe_unused]] bool mirrored = false) { return "INTD_ASYNC"; }
219 static bool isWriteable() { return false; }
220 static bool isReadable() { return true; }
224
225 template<typename UserType>
226 std::vector<std::vector<UserType>> generateValue(bool = false) {
227 return {{++value}};
228 }
229
231 ensureOpen();
232 auto x = generateValue<minimumUserType>()[0][0];
233 accBackdoor->accessData(0) = x;
234 accBackdoor->write();
236 }
237};
238
240
241/**********************************************************************************************************************/
242
243BOOST_AUTO_TEST_CASE(TestRegisterAccessor) {
244 SharedDummyBackendUnifiedTestSuite::HelperProcess::start();
245
246 std::cout << "*** testRegisterAccessor *** " << std::endl;
249 .addRegister<IntegersSigned32RO>()
250 .addRegister<IntegersSigned32WO>()
251 .addRegister<IntegersSigned32DummyWritable>()
252 .addRegister<IntegersSigned32Async>()
253 .runTests(cdd);
254
255 SharedDummyBackendUnifiedTestSuite::HelperProcess::kill();
256}
257
261BOOST_AUTO_TEST_CASE(TestVerifyMemoryDeleted) {
262 SharedDummyBackendUnifiedTestSuite::HelperProcess::start();
265
266 // also clear our backend instance. This should also remove allocated SHM segments and semaphores
267 // - note, this only works if the global instance map uses weak pointers
268 sharedDummy.reset();
269
270 // Check that memory is removed
271 bool shm_removed{false};
272 for(size_t n = 0; n < 30; ++n) {
273 shm_removed = !shm_exists(shmName);
274 if(shm_removed) {
275 break;
276 }
277 sleep(1);
278 }
279 BOOST_CHECK(shm_removed);
280}
281
282BOOST_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)