ChimeraTK-DeviceAccess 03.25.00
Loading...
Searching...
No Matches
testAsyncVarAndHierarchicalInterruptsUnified.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 AsyncVarAndHierarchicalInterruptsUnified
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 "Device.h"
13#include "DummyBackend.h"
16// #include "TransferGroup.h"
17#include "UnifiedBackendTest.h"
18
19using namespace ChimeraTK;
20
21/**********************************************************************************************************************/
24
25 static boost::shared_ptr<DeviceBackend> createInstance(std::string, std::map<std::string, std::string> parameters) {
26 return boost::make_shared<DummyForCleanupCheck>(parameters["map"]);
27 }
29 std::cout << "~DummyForCleanupCheck()" << std::endl;
30 cleanupCalled = true;
31 }
32
39 static std::atomic_bool cleanupCalled;
40};
42static DummyForCleanupCheck::BackendRegisterer gDFCCRegisterer;
43
44/* ===============================================================================================
45 * This test is checking async variables and the map-file related part of interrupts for
46 * consistency with the specification (implemented in the unified test).
47 * - AsyncNDRegisterAccessor
48 * - AsyncVariable (multiple listeners to one logical async variable)
49 * - Hierarchical interrupts (multiplexed interrupts via DummyMuxedInterruptDistributor)
50 * - TriggeredPollDistributor
51 * - Instantiation from the map file
52 *
53 * FIXME: Unified test does not support void variables yet.
54 * ==============================================================================================*/
55
56// Create a test suite which holds all your tests.
57BOOST_AUTO_TEST_SUITE(AsyncVarAndHierarchicalInterruptsUnifiedTestSuite)
58
59/**********************************************************************************************************************/
60
61static std::string cdd("(DummyForCleanupCheck:1?map=testHierarchicalInterrupts.map)");
62static auto exceptionDummy =
63 boost::dynamic_pointer_cast<ExceptionDummy>(BackendFactory::getInstance().createBackend(cdd));
64
65template<class WITHPATH, uint32_t INTERRUPT>
67 bool isWriteable() { return false; }
68 bool isReadable() { return true; }
72 size_t nChannels() { return 1; }
73 size_t nElementsPerChannel() { return 1; }
74 size_t writeQueueLength() { return std::numeric_limits<size_t>::max(); }
75 size_t nRuntimeErrorCases() { return 1; }
76 typedef int32_t minimumUserType;
78
79 static constexpr auto capabilities = TestCapabilities<>()
81 .disableSwitchReadOnly()
82 .disableSwitchWriteOnly()
83 .disableTestWriteNeverLosesData()
84 .enableTestRawTransfer();
85
86 DummyRegisterAccessor<int32_t> acc{exceptionDummy.get(), "", WITHPATH::path()};
87
88 template<typename Type>
89 std::vector<std::vector<Type>> generateValue([[maybe_unused]] bool raw = false) {
90 return {{acc + static_cast<int32_t>(INTERRUPT)}}; // just re-use the interrupt here. Any number does the job.
91 }
92
93 template<typename UserType>
94 std::vector<std::vector<UserType>> getRemoteValue([[maybe_unused]] bool raw = false) {
95 return {{acc}};
96 }
97
99 acc = generateValue<minimumUserType>()[0][0];
100 if(!WITHPATH::activeInterruptsPath().empty()) {
101 DummyRegisterAccessor<uint32_t> activeInterrupts{exceptionDummy.get(), "", WITHPATH::activeInterruptsPath()};
102 activeInterrupts = WITHPATH::activeInterruptsValue();
103 }
104 if(exceptionDummy->isOpen()) {
105 exceptionDummy->triggerInterrupt(INTERRUPT);
106 }
107 }
108
109 void forceAsyncReadInconsistency() { acc = generateValue<minimumUserType>()[0][0]; }
110
111 void setForceRuntimeError(bool enable, size_t) {
112 exceptionDummy->throwExceptionRead = enable;
113 exceptionDummy->throwExceptionWrite = enable;
114 exceptionDummy->throwExceptionOpen = enable;
115 if(exceptionDummy->isOpen()) {
116 exceptionDummy->triggerInterrupt(INTERRUPT);
117 }
118 }
119};
120
121/**********************************************************************************************************************/
122
123struct datafrom6 : public TriggeredInt<datafrom6, 6> {
124 static std::string path() { return "/datafrom6"; }
125 static std::string activeInterruptsPath() { return ""; } // empty
126 static uint32_t activeInterruptsValue() { return 0; }
127};
128
129struct datafrom5_9 : public TriggeredInt<datafrom5_9, 5> {
130 static std::string path() { return "/datafrom5_9"; }
131 static std::string activeInterruptsPath() { return "/int_ctrls/controller5/active_ints"; }
132 static uint32_t activeInterruptsValue() { return 1U << 9U; }
133};
134
135struct datafrom4_8_2 : public TriggeredInt<datafrom4_8_2, 4> {
136 static std::string path() { return "/datafrom4_8_2"; }
137 static std::string activeInterruptsPath() { return "/int_ctrls/controller4_8/active_ints"; }
138 static uint32_t activeInterruptsValue() { return 1U << 2U; }
140 DummyRegisterAccessor<uint32_t> activeParentInterrupts{
141 exceptionDummy.get(), "", "/int_ctrls/controller4/active_ints"};
142 activeParentInterrupts = 1U << 8U;
143 }
144};
145
146struct datafrom4_8_3 : public TriggeredInt<datafrom4_8_3, 4> {
147 static std::string path() { return "/datafrom4_8_3"; }
148 static std::string activeInterruptsPath() { return "/int_ctrls/controller4_8/active_ints"; }
149 static uint32_t activeInterruptsValue() { return 1U << 3U; }
151 DummyRegisterAccessor<uint32_t> activeParentInterrupts{
152 exceptionDummy.get(), "", "/int_ctrls/controller4/active_ints"};
153 activeParentInterrupts = 1U << 8U;
154 }
155};
156
157/**********************************************************************************************************************/
158
159// Use bool accessors instead of void
160template<class WITHPATH, uint32_t INTERRUPT>
162 bool isWriteable() { return false; }
163 bool isReadable() { return true; }
165 size_t nChannels() { return 1; }
166 size_t nElementsPerChannel() { return 1; }
167 size_t writeQueueLength() { return std::numeric_limits<size_t>::max(); }
168 size_t nRuntimeErrorCases() { return 1; }
171
172 static constexpr auto capabilities = TestCapabilities<>()
174 .disableSwitchReadOnly()
175 .disableSwitchWriteOnly()
176 .disableTestWriteNeverLosesData()
177 .disableTestRawTransfer();
178
179 template<typename Type>
180 std::vector<std::vector<Type>> generateValue([[maybe_unused]] bool raw = false) {
181 return {{{}}};
182 }
183
184 template<typename UserType>
185 std::vector<std::vector<UserType>> getRemoteValue([[maybe_unused]] bool raw = false) {
186 return {{{}}};
187 }
188
190 if(!WITHPATH::activeInterruptsPath().empty()) {
191 DummyRegisterAccessor<uint32_t> activeInterrupts{exceptionDummy.get(), "", WITHPATH::activeInterruptsPath()};
192 activeInterrupts = WITHPATH::activeInterruptsValue();
193 }
194 if(exceptionDummy->isOpen()) {
195 exceptionDummy->triggerInterrupt(INTERRUPT);
196 }
197 }
198
200
201 void setForceRuntimeError(bool enable, size_t) {
202 exceptionDummy->throwExceptionRead = enable;
203 exceptionDummy->throwExceptionWrite = enable;
204 exceptionDummy->throwExceptionOpen = enable;
205 if(exceptionDummy->isOpen()) {
206 exceptionDummy->triggerInterrupt(INTERRUPT);
207 }
208 }
209};
210
211/**********************************************************************************************************************/
212
213struct interrupt6 : public BoolAsVoid<interrupt6, 6> {
214 static std::string path() { return "/interrupt6"; }
215 static std::string activeInterruptsPath() { return ""; } // empty
216 static uint32_t activeInterruptsValue() { return 0; }
217
218 // The accessor itself cannot trigger a runtime error, as it is indirectly fed by a thread that does not
219 // know about the individual accessors.
220 // Exceptions only are written to the queue when setException() is called.
221 size_t nRuntimeErrorCases() { return 0; }
222};
223
224struct canonicalInterrupt6 : public BoolAsVoid<canonicalInterrupt6, 6> {
225 static std::string path() { return "/!6"; }
226 static std::string activeInterruptsPath() { return ""; } // empty
227 static uint32_t activeInterruptsValue() { return 0; }
228 size_t nRuntimeErrorCases() { return 0; }
229};
230
231struct interrupt5_9 : public BoolAsVoid<interrupt5_9, 5> {
232 static std::string path() { return "/interrupt5_9"; }
233 static std::string activeInterruptsPath() { return "/int_ctrls/controller5/active_ints"; }
234 static uint32_t activeInterruptsValue() { return 1U << 9U; }
235};
236
237struct canonicalInterrupt5 : public BoolAsVoid<canonicalInterrupt5, 5> {
238 static std::string path() { return "/!5"; }
239 static std::string activeInterruptsPath() { return "/int_ctrls/controller5/active_ints"; }
240 static uint32_t activeInterruptsValue() { return 1U << 9U; }
241 size_t nRuntimeErrorCases() { return 0; }
242};
243
244struct canonicalInterrupt5_9 : public BoolAsVoid<canonicalInterrupt5_9, 5> {
245 static std::string path() { return "/!5:9"; }
246 static std::string activeInterruptsPath() { return "/int_ctrls/controller5/active_ints"; }
247 static uint32_t activeInterruptsValue() { return 1U << 9U; }
248};
249
250struct interrupt4_8_2 : public BoolAsVoid<interrupt4_8_2, 4> {
251 static std::string path() { return "/interrupt4_8_2"; }
252 static std::string activeInterruptsPath() { return "/int_ctrls/controller4_8/active_ints"; }
253 static uint32_t activeInterruptsValue() { return 1U << 2U; }
254};
255
256struct canonicalInterrupt4a : public BoolAsVoid<canonicalInterrupt4a, 4> {
257 static std::string path() { return "/!4"; }
258 static std::string activeInterruptsPath() { return "/int_ctrls/controller4_8/active_ints"; }
259 static uint32_t activeInterruptsValue() { return 1U << 2U; }
260 size_t nRuntimeErrorCases() { return 0; }
261};
262
263struct canonicalInterrupt4_8a : public BoolAsVoid<canonicalInterrupt4_8a, 4> {
264 static std::string path() { return "/!4:8"; }
265 static std::string activeInterruptsPath() { return "/int_ctrls/controller4_8/active_ints"; }
266 static uint32_t activeInterruptsValue() { return 1U << 2U; }
267};
268
269struct canonicalInterrupt4_8_2 : public BoolAsVoid<canonicalInterrupt4_8_2, 4> {
270 static std::string path() { return "/!4:8:2"; }
271 static std::string activeInterruptsPath() { return "/int_ctrls/controller4_8/active_ints"; }
272 static uint32_t activeInterruptsValue() { return 1U << 2U; }
273};
274
275struct interrupt4_8_3 : public BoolAsVoid<interrupt4_8_3, 4> {
276 static std::string path() { return "/interrupt4_8_3"; }
277 static std::string activeInterruptsPath() { return "/int_ctrls/controller4_8/active_ints"; }
278 static uint32_t activeInterruptsValue() { return 1U << 3U; }
279};
280
281struct canonicalInterrupt4b : public BoolAsVoid<canonicalInterrupt4b, 4> {
282 static std::string path() { return "/!4"; }
283 static std::string activeInterruptsPath() { return "/int_ctrls/controller4_8/active_ints"; }
284 static uint32_t activeInterruptsValue() { return 1U << 3U; }
285 size_t nRuntimeErrorCases() { return 0; }
286};
287
288struct canonicalInterrupt4_8b : public BoolAsVoid<canonicalInterrupt4_8b, 4> {
289 static std::string path() { return "/!4:8"; }
290 static std::string activeInterruptsPath() { return "/int_ctrls/controller4_8/active_ints"; }
291 static uint32_t activeInterruptsValue() { return 1U << 3U; }
292};
293
294struct canonicalInterrupt4_8_3 : public BoolAsVoid<canonicalInterrupt4_8_3, 4> {
295 static std::string path() { return "/!4:8:3"; }
296 static std::string activeInterruptsPath() { return "/int_ctrls/controller4_8/active_ints"; }
297 static uint32_t activeInterruptsValue() { return 1U << 3U; }
298};
299
300/**********************************************************************************************************************/
301
302BOOST_AUTO_TEST_CASE(testRegisterAccessor) {
303 struct canonicalInterrupt4_8b : public BoolAsVoid<canonicalInterrupt4_8b, 4> {
304 static std::string path() { return "/!4:8"; }
305 static std::string activeInterruptsPath() { return "/int_ctrls/controller4_8/active_ints"; }
306 static uint32_t activeInterruptsValue() { return 1U << 3U; }
307 };
308
309 std::cout << "*** testRegisterAccessor *** " << std::endl;
310 {
313 .addRegister<datafrom5_9>()
314 .addRegister<datafrom4_8_2>()
315 .addRegister<datafrom4_8_3>()
316 .addRegister<interrupt6>()
317 .addRegister<canonicalInterrupt6>()
318 .addRegister<interrupt5_9>()
319 .addRegister<canonicalInterrupt5>()
320 .addRegister<canonicalInterrupt5_9>()
321 .addRegister<interrupt4_8_2>()
322 .addRegister<canonicalInterrupt4a>()
323 .addRegister<canonicalInterrupt4_8a>()
324 .addRegister<canonicalInterrupt4_8_2>()
325 .addRegister<interrupt4_8_3>()
326 .addRegister<canonicalInterrupt4b>()
327 .addRegister<canonicalInterrupt4_8b>()
328 .addRegister<canonicalInterrupt4_8_3>()
329 .runTests(cdd);
330 }
331 exceptionDummy = nullptr;
332 // This is checking that there is no internal shared pointer loop which prevents the destructor from ever being called.
334}
335
336/**********************************************************************************************************************/
337
338BOOST_AUTO_TEST_SUITE_END()
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.
void registerBackendType(const std::string &backendType, boost::shared_ptr< DeviceBackend >(*creatorFunction)(std::string address, std::map< std::string, std::string > parameters), const std::vector< std::string > &sdmParameterNames={}, const std::string &deviceAccessVersion=CHIMERATK_DEVICEACCESS_VERSION)
Register a backend by the name backendType with the given creatorFunction.
Wrapper Class to avoid vector<bool> problems.
Register accessor for accessing single word or 1D array registers internally of a DummyBackend implem...
ExceptionDummy(std::string const &mapFileName, const std::string &dataConsistencyKeyDescriptor="")
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.
Wrapper Class for void.
@ 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...
std::vector< std::vector< Type > > generateValue(bool raw=false)
void setForceRuntimeError(bool enable, size_t)
ChimeraTK::AccessModeFlags supportedFlags()
std::vector< std::vector< UserType > > getRemoteValue(bool raw=false)
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
static boost::shared_ptr< DeviceBackend > createInstance(std::string, std::map< std::string, std::string > parameters)
std::vector< std::vector< UserType > > getRemoteValue(bool raw=false)
ChimeraTK::AccessModeFlags supportedFlags()
DummyRegisterAccessor< int32_t > acc
std::vector< std::vector< Type > > generateValue(bool raw=false)
std::string cdd
BOOST_AUTO_TEST_CASE(testRegisterAccessor)