ChimeraTK-DeviceAccess 03.25.00
Loading...
Searching...
No Matches
testDummyBackend.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 BOOST_TEST_MODULE testDummyBackend
6
7#include "BackendFactory.h"
8#include "Device.h"
9#include "DummyBackend.h"
10#include "Exception.h"
11#include "parserUtilities.h"
12
13#include <boost/bind/bind.hpp>
14#include <boost/function.hpp>
15#include <boost/lambda/lambda.hpp>
16
17// FIXME Remove
18#include <regex>
19
20#define BOOST_NO_EXCEPTIONS
21#include <boost/test/unit_test.hpp>
22#undef BOOST_NO_EXCEPTIONS
23
24using namespace boost::unit_test_framework;
25namespace ChimeraTK {
26 using namespace ChimeraTK;
27}
28using namespace ChimeraTK;
29
30#define TEST_MAPPING_FILE "mtcadummy_withoutModules.map"
31#define FIRMWARE_REGISTER_STRING "WORD_FIRMWARE"
32#define STATUS_REGISTER_STRING "WORD_STATUS"
33#define USER_REGISTER_STRING "WORD_USER"
34#define CLOCK_MUX_REGISTER_STRING "WORD_CLK_MUX"
35#define CLOCK_RESET_REGISTER_STRING "WORD_CLK_RST"
36#define READ_ONLY_REGISTER_STRING "WORD_READ_ONLY"
37
38#define EXISTING_DEVICE "(TestableDummy?map=" TEST_MAPPING_FILE ")"
39#define NON_EXISTING_DEVICE "DUMMY9"
40
41static BackendFactory& FactoryInstance = BackendFactory::getInstance();
42
51 public:
52 explicit TestableDummyBackend(std::string mapFileName) : DummyBackend(mapFileName) {}
53 using DummyBackend::checkSizeIsMultipleOfWordSize;
62
63 static boost::shared_ptr<DeviceBackend> createInstance(std::string, std::map<std::string, std::string> parameters) {
64 return boost::shared_ptr<DeviceBackend>(new TestableDummyBackend(parameters["map"]));
65 }
67 public:
69 std::cout << "TestableDummyBackend::BackendRegisterer: registering backend type TestableDummy" << std::endl;
71 "TestableDummy", &TestableDummyBackend::createInstance);
72 }
73 };
75};
77
78class Fixture_t {
79 public:
80 Fixture_t() : a(0), b(0), c(0), _backendInstance() {
82 std::list<std::string> parameters;
83 parameters.push_back(std::string(TEST_MAPPING_FILE));
84 _dummyBackend = boost::shared_ptr<TestableDummyBackend>(new TestableDummyBackend(TEST_MAPPING_FILE));
85 }
86
87 boost::shared_ptr<TestableDummyBackend> _dummyBackend;
90
91 // stuff for the callback function test
92 int a, b, c;
93 void increaseA() { ++a; }
94 void increaseB() { ++b; }
95 void increaseC() { ++c; }
96 boost::shared_ptr<ChimeraTK::DeviceBackend> _backendInstance;
97};
98
99static Fixture_t f;
100
101/**********************************************************************************************************************/
102
104 if(_backendInstance == nullptr) _backendInstance = FactoryInstance.createBackend(EXISTING_DEVICE);
105 _backendInstance->open();
106 DeviceBackend* rawBasePointer = _backendInstance.get();
107 return (static_cast<TestableDummyBackend*>(rawBasePointer));
108}
109
110/**********************************************************************************************************************/
111
112BOOST_AUTO_TEST_CASE(testCheckSizeIsMultipleOfWordSize) {
113 // just some arbitrary numbers to test %4 = 0, 1, 2, 3
115
117
119
121}
122
123/**********************************************************************************************************************/
124
125BOOST_AUTO_TEST_CASE(testReadWriteSingleWordRegister) {
126 // WORD_CLK_RST 0x00000001 0x00000040 0x00000004 0x00000000 32 0 0
127 TestableDummyBackend* dummyBackend = f.getBackendInstance();
128 uint64_t offset = 0x40;
129 uint64_t bar = 0;
130 int32_t dataContent = -1;
131 // BOOST_CHECK_NO_THROW(dummyBackend->readReg(bar, offset, &dataContent));
132 BOOST_CHECK_NO_THROW(dummyBackend->read(bar, offset, &dataContent, 4));
133 BOOST_CHECK(dataContent == 0);
134 dataContent = 47;
135 BOOST_CHECK_NO_THROW(dummyBackend->write(bar, offset, &dataContent, 4));
136 dataContent = -1; // make sure the value is really being read
137 // no need to test NO_THROW on the same register twice
138 // dummyBackend->readReg(offset, &dataContent, bar);
139 dummyBackend->read(bar, offset, &dataContent, 4);
140 BOOST_CHECK(dataContent == 47);
141
142 // the size as index is invalid, allowed range is 0..size-1 included.
143 BOOST_CHECK_THROW(
144 dummyBackend->read(
145 bar, static_cast<uint64_t>(dummyBackend->_barContents[bar].size() * sizeof(int32_t)), &dataContent, 4),
147 BOOST_CHECK_THROW(
148 dummyBackend->write(
149 bar, static_cast<uint64_t>(dummyBackend->_barContents[bar].size() * sizeof(int32_t)), &dataContent, 4),
151}
152
153/**********************************************************************************************************************/
154
155BOOST_AUTO_TEST_CASE(testReadWriteMultiWordRegister) {
156 // WORD_CLK_MUX 0x00000004 0x00000020 0x00000010 0x00000000 32 0 0
157 TestableDummyBackend* dummyBackend = f.getBackendInstance();
158
159 uint64_t offset = 0x20;
160 uint64_t bar = 0;
161 size_t sizeInBytes = 0x10;
162 size_t sizeInWords = 0x4;
163 std::vector<int32_t> dataContent(sizeInWords, -1);
164
165 BOOST_CHECK_NO_THROW(dummyBackend->read(bar, offset, &(dataContent[0]), sizeInBytes));
166 for(std::vector<int32_t>::iterator dataIter = dataContent.begin(); dataIter != dataContent.end(); ++dataIter) {
167 std::stringstream errorMessage;
168 errorMessage << "*dataIter = " << *dataIter;
169 BOOST_CHECK_MESSAGE(*dataIter == 0, errorMessage.str());
170 }
171
172 for(unsigned int index = 0; index < dataContent.size(); ++index) {
173 dataContent[index] = static_cast<int32_t>((index + 1) * (index + 1));
174 }
175 BOOST_CHECK_NO_THROW(dummyBackend->write(bar, offset, &(dataContent[0]), sizeInBytes));
176 // make sure the value is really being read
177 std::for_each(dataContent.begin(), dataContent.end(), boost::lambda::_1 = -1);
178
179 // no need to test NO_THROW on the same register twice
180 dummyBackend->read(bar, offset, &(dataContent[0]), sizeInBytes);
181
182 // make sure the value is really being read
183 for(unsigned int index = 0; index < dataContent.size(); ++index) {
184 BOOST_CHECK(dataContent[index] == static_cast<int32_t>((index + 1) * (index + 1)));
185 }
186
187 // tests for exceptions
188 // 1. base address too large
189 BOOST_CHECK_THROW(
190 dummyBackend->read(bar, static_cast<uint64_t>(dummyBackend->_barContents[bar].size() * sizeof(int32_t)),
191 &(dataContent[0]), sizeInBytes),
193 BOOST_CHECK_THROW(
194 dummyBackend->write(bar, static_cast<uint64_t>(dummyBackend->_barContents[bar].size() * sizeof(int32_t)),
195 &(dataContent[0]), sizeInBytes),
197 // 2. size too large (works because the target register is not at offfset 0)
198 // resize the data vector for this test
199 dataContent.resize(dummyBackend->_barContents[bar].size());
200 BOOST_CHECK_THROW(
201 dummyBackend->read(bar, offset, &(dataContent[0]), dummyBackend->_barContents[bar].size() * sizeof(int32_t)),
203 BOOST_CHECK_THROW(
204 dummyBackend->write(bar, offset, &(dataContent[0]), dummyBackend->_barContents[bar].size() * sizeof(int32_t)),
206 // 3. size not multiple of 4
207 BOOST_CHECK_THROW(dummyBackend->read(bar, offset, &(dataContent[0]), sizeInBytes - 1), ChimeraTK::logic_error);
208 BOOST_CHECK_THROW(dummyBackend->write(bar, offset, &(dataContent[0]), sizeInBytes - 1), ChimeraTK::logic_error);
209}
210
211/**********************************************************************************************************************/
212
213#if 0
214BOOST_AUTO_TEST_CASE(testReadDeviceInfo) {
215 std::string deviceInfo;
216 auto dummyBackend = FactoryInstance.createBackend("DUMMYD0");
217 deviceInfo = dummyBackend->readDeviceInfo();
218 std::cout << deviceInfo << std::endl;
219
220 // DummyDevice instances created using the factory now deals with absolute
221 // paths to the dmap file. We frame an absolute path for comaprison
222 std::string absolutePathToMapfile =
224
225 BOOST_CHECK(deviceInfo == (std::string("DummyBackend with mapping file ") + absolutePathToMapfile));
226}
227#endif
228
229/**********************************************************************************************************************/
230
231BOOST_AUTO_TEST_CASE(testReadOnly) {
232 TestableDummyBackend* dummyBackend = f.getBackendInstance();
233
234 // WORD_CLK_MUX 0x00000004 0x00000020 0x00000010 0x00000000 32 0 0
235 uint64_t offset = 0x20;
236 uint64_t bar = 0;
237 size_t sizeInBytes = 0x10;
238 size_t sizeInWords = 0x4;
239 std::stringstream errorMessage;
240 errorMessage << "This register should have 4 words. If you changed your "
241 "mapping you have to adapt the testReadOnly() test.";
242 BOOST_REQUIRE_MESSAGE(sizeInWords == 4, errorMessage.str());
243
244 std::vector<int32_t> dataContent(sizeInWords);
245 for(unsigned int index = 0; index < dataContent.size(); ++index) {
246 dataContent[index] = static_cast<int32_t>((index + 1) * (index + 1));
247 }
248 dummyBackend->write(bar, offset, &(dataContent[0]), sizeInBytes);
249 dummyBackend->setReadOnly(bar, offset, 1);
250
251 // the actual test: write 42 to all registers, register 0 must not change, all
252 // others have to
253 std::for_each(dataContent.begin(), dataContent.end(), boost::lambda::_1 = 42);
254 dummyBackend->write(bar, offset, &(dataContent[0]), sizeInBytes);
255 std::for_each(dataContent.begin(), dataContent.end(), boost::lambda::_1 = -1);
256 dummyBackend->read(bar, offset, &(dataContent[0]), sizeInBytes);
257 BOOST_CHECK(dataContent[0] == 1);
258 BOOST_CHECK(dataContent[1] == 42);
259 BOOST_CHECK(dataContent[2] == 42);
260 BOOST_CHECK(dataContent[3] == 42);
261 // also set the last two words to read only. Now only the second word has to
262 // change.
263 // We use the addressRange interface for it to also cover this.
264 TestableDummyBackend::AddressRange lastTwoMuxRegisters(bar, offset + 2 * sizeof(int32_t), 2 * sizeof(int32_t));
265 dummyBackend->setReadOnly(lastTwoMuxRegisters);
266 std::for_each(dataContent.begin(), dataContent.end(), boost::lambda::_1 = 29);
267 // also test with single write operations
268 for(size_t index = 0; index < sizeInWords; ++index) {
269 dummyBackend->write(bar, offset + index * sizeof(int32_t), &dataContent[index], 4);
270 }
271
272 std::for_each(dataContent.begin(), dataContent.end(), boost::lambda::_1 = -1);
273 dummyBackend->read(bar, offset, &(dataContent[0]), sizeInBytes);
274 BOOST_CHECK(dataContent[0] == 1);
275 BOOST_CHECK(dataContent[1] == 29);
276 BOOST_CHECK(dataContent[2] == 42);
277 BOOST_CHECK(dataContent[3] == 42);
278
279 // check that the next register is still writeable (boundary test)
280 int32_t originalNextDataWord;
281 dummyBackend->read(bar, offset + sizeInBytes, &originalNextDataWord, 4);
282 int32_t writeWord = originalNextDataWord + 1;
283 dummyBackend->write(bar, offset + sizeInBytes, &writeWord, 4);
284 int32_t readbackWord;
285 dummyBackend->read(bar, offset + sizeInBytes, &readbackWord, 4);
286 BOOST_CHECK(originalNextDataWord + 1 == readbackWord);
287}
288
289BOOST_AUTO_TEST_CASE(testWriteCallbackFunctions) {
290 // We just require the first bar to be 12 registers long.
291 // Everything else would overcomplicate this test. For a real
292 // application one would always use register names from mapping,
293 // but this is not the purpose of this test.
294
295 // from the previous test we know that adresses 32, 40 and 44 are write only
296 TestableDummyBackend* dummyBackend = f.getBackendInstance();
297 BOOST_REQUIRE(dummyBackend->_barContents[0].size() >= 13);
298 f.a = 0;
299 f.b = 0;
300 f.c = 0;
301 dummyBackend->setWriteCallbackFunction(TestableDummyBackend::AddressRange(0, 36, 4), [] { f.increaseA(); });
302 dummyBackend->setWriteCallbackFunction(TestableDummyBackend::AddressRange(0, 28, 24), [] { f.increaseB(); });
303 dummyBackend->setWriteCallbackFunction(TestableDummyBackend::AddressRange(0, 20, 12), [] { f.increaseC(); });
304
305 // test single writes
306 int32_t dataWord(42);
307 dummyBackend->write(static_cast<uint64_t>(0), 12, &dataWord, 4); // nothing
308 BOOST_CHECK(f.a == 0);
309 BOOST_CHECK(f.b == 0);
310 BOOST_CHECK(f.c == 0);
311
312 dummyBackend->write(static_cast<uint64_t>(0), 20, &dataWord, 4); // c
313 BOOST_CHECK(f.a == 0);
314 BOOST_CHECK(f.b == 0);
315 BOOST_CHECK(f.c == 1);
316 dummyBackend->write(static_cast<uint64_t>(0), 24, &dataWord, 4); // c
317 BOOST_CHECK(f.a == 0);
318 BOOST_CHECK(f.b == 0);
319 BOOST_CHECK(f.c == 2);
320 dummyBackend->write(static_cast<uint64_t>(0), 28, &dataWord, 4); // bc
321 BOOST_CHECK(f.a == 0);
322 BOOST_CHECK(f.b == 1);
323 BOOST_CHECK(f.c == 3);
324 dummyBackend->write(static_cast<uint64_t>(0), 32, &dataWord, 4); // read only
325 BOOST_CHECK(f.a == 0);
326 BOOST_CHECK(f.b == 1);
327 BOOST_CHECK(f.c == 3);
328 dummyBackend->write(static_cast<uint64_t>(0), 36, &dataWord, 4); // ab
329 BOOST_CHECK(f.a == 1);
330 BOOST_CHECK(f.b == 2);
331 BOOST_CHECK(f.c == 3);
332 dummyBackend->write(static_cast<uint64_t>(0), 40, &dataWord, 4); // read only
333 BOOST_CHECK(f.a == 1);
334 BOOST_CHECK(f.b == 2);
335 BOOST_CHECK(f.c == 3);
336 dummyBackend->write(static_cast<uint64_t>(0), 44, &dataWord, 4); // read only
337 BOOST_CHECK(f.a == 1);
338 BOOST_CHECK(f.b == 2);
339 BOOST_CHECK(f.c == 3);
340 dummyBackend->write(static_cast<uint64_t>(0), 48, &dataWord, 4); // b
341 BOOST_CHECK(f.a == 1);
342 BOOST_CHECK(f.b == 3);
343 BOOST_CHECK(f.c == 3);
344
345 std::vector<int32_t> dataContents(8, 42); // eight words, each with content 42
346 f.a = 0;
347 f.b = 0;
348 f.c = 0;
349 dummyBackend->write(static_cast<uint64_t>(0), 20, &(dataContents[0]), 32); // abc
350 BOOST_CHECK(f.a == 1);
351 BOOST_CHECK(f.b == 1);
352 BOOST_CHECK(f.c == 1);
353 dummyBackend->write(static_cast<uint64_t>(0), 20, &(dataContents[0]), 8); // c
354 BOOST_CHECK(f.a == 1);
355 BOOST_CHECK(f.b == 1);
356 BOOST_CHECK(f.c == 2);
357 dummyBackend->write(static_cast<uint64_t>(0), 20, &(dataContents[0]), 12); // bc
358 BOOST_CHECK(f.a == 1);
359 BOOST_CHECK(f.b == 2);
360 BOOST_CHECK(f.c == 3);
361 dummyBackend->write(static_cast<uint64_t>(0), 28, &(dataContents[0]), 24); // abc
362 BOOST_CHECK(f.a == 2);
363 BOOST_CHECK(f.b == 3);
364 BOOST_CHECK(f.c == 4);
365 dummyBackend->write(static_cast<uint64_t>(0), 32, &(dataContents[0]), 16); // ab
366 BOOST_CHECK(f.a == 3);
367 BOOST_CHECK(f.b == 4);
368 BOOST_CHECK(f.c == 4);
369 dummyBackend->write(static_cast<uint64_t>(0), 40, &(dataContents[0]), 8); // readOnly
370 BOOST_CHECK(f.a == 3);
371 BOOST_CHECK(f.b == 4);
372 BOOST_CHECK(f.c == 4);
373 dummyBackend->write(static_cast<uint64_t>(0), 4, &(dataContents[0]), 8); // nothing
374 BOOST_CHECK(f.a == 3);
375 BOOST_CHECK(f.b == 4);
376 BOOST_CHECK(f.c == 4);
377}
378
379/**********************************************************************************************************************/
380
381BOOST_AUTO_TEST_CASE(testWriteRegisterWithoutCallback) {
382 f.a = 0;
383 f.b = 0;
384 f.c = 0;
385 int32_t dataWord = 42;
386 TestableDummyBackend* dummyBackend = f.getBackendInstance();
387 dummyBackend->writeRegisterWithoutCallback(0, 20, dataWord); // c has callback installed on this register
388 BOOST_CHECK(f.a == 0);
389 BOOST_CHECK(f.b == 0);
390 BOOST_CHECK(f.c == 0); // c must not change
391
392 // read only is also disabled for this internal function
393 dummyBackend->read(static_cast<uint64_t>(0), 40, &dataWord, 4);
394 dummyBackend->writeRegisterWithoutCallback(0, 40, dataWord + 1);
395 int32_t readbackDataWord;
396 dummyBackend->read(static_cast<uint64_t>(0), 40, &readbackDataWord, 4);
397 BOOST_CHECK(readbackDataWord == dataWord + 1);
398}
399
400/**********************************************************************************************************************/
401
402BOOST_AUTO_TEST_CASE(testWriteToReadOnlyRegister) {
403 ChimeraTK::Device dummyDevice;
404 dummyDevice.open("DUMMYD0");
405
406 // Also get pointer to the backend in order to check the catalogue
407 TestableDummyBackend* dummyBackend = f.getBackendInstance();
408
409 const std::string DUMMY_WRITEABLE_SUFFIX{".DUMMY_WRITEABLE"};
410 auto ro_register = dummyDevice.getScalarRegisterAccessor<int>(READ_ONLY_REGISTER_STRING);
411 auto ro_register_dw = dummyDevice.getScalarRegisterAccessor<int>(READ_ONLY_REGISTER_STRING + DUMMY_WRITEABLE_SUFFIX);
412
413 // The suffixed register must not appear when iterating the the catalogue.
414 // However, the catalogue knows it when I "guess" the name.
415 auto dummyCatalogue = dummyBackend->getRegisterCatalogue();
416 bool found = false;
417 // Test 1: DUMMY_WRITABLE not in iterable catalogue
418 for(auto& info : dummyCatalogue) {
419 if(info.getRegisterName() == READ_ONLY_REGISTER_STRING + DUMMY_WRITEABLE_SUFFIX) {
420 found = true;
421 break;
422 }
423 }
424 BOOST_CHECK(found == false);
425
426 // Test 2: Register without DUMMY_WRITEABLE is in the iterable catalogue
427 for(auto& info : dummyCatalogue) {
428 if(info.getRegisterName() == READ_ONLY_REGISTER_STRING) {
429 found = true;
430 break;
431 }
432 }
433 BOOST_CHECK(found == true);
434
435 // Test 3 (should be taken over by the unified test) Whe I know the name the register info is there)
436 BOOST_CHECK(dummyCatalogue.hasRegister(READ_ONLY_REGISTER_STRING + DUMMY_WRITEABLE_SUFFIX));
437 auto info = dummyCatalogue.getRegister(READ_ONLY_REGISTER_STRING + DUMMY_WRITEABLE_SUFFIX);
438 // FIXME: This test is currently failing.
439 // BOOST_CHECK_EQUAL(info.getRegisterName(), READ_ONLY_REGISTER_STRING + DUMMY_WRITEABLE_SUFFIX);
440 BOOST_CHECK(info.isWriteable());
441
442 // Read-only register and the DUMMY_WRITEABLE companion
443 // should return appropriate read-only and writeable flags
444 BOOST_CHECK(ro_register.isReadOnly());
445 BOOST_CHECK(!ro_register.isWriteable());
446 BOOST_CHECK(!ro_register_dw.isReadOnly());
447 BOOST_CHECK(ro_register_dw.isWriteable());
448
449 // Test writing to the DUMMY_WRITEABLE register and
450 // read back through the real register
451 ro_register_dw = 42;
452 ro_register_dw.write();
453 ro_register.read();
454
455 BOOST_CHECK_EQUAL(ro_register, ro_register_dw);
456
457 // Writeing to read-only register must throw and not effect the content
458 ro_register = 84;
459 BOOST_CHECK_THROW(ro_register.write(), ChimeraTK::logic_error);
460 ro_register.read();
461 BOOST_CHECK_NE(ro_register, 84);
462 BOOST_CHECK_EQUAL(ro_register, ro_register_dw);
463
464 // Don't close the device here because the backend needs to stay open
465 // for the following test cases
466}
467
468BOOST_AUTO_TEST_CASE(testDummyInterruptCatalogue) {
469 ChimeraTK::Device dummyDevice;
470 dummyDevice.open("DUMMYD0");
471
472 // Also get pointer to the backend in order to check the catalogue
473 TestableDummyBackend* dummyBackend = f.getBackendInstance();
474
475 const std::string DUMMY_INTERRUPT{"/DUMMY_INTERRUPT_3"};
476
477 // The suffixed register must not appear in the catalogue when iterating
478 auto dummyCatalogue = dummyBackend->getRegisterCatalogue();
479 bool found = false;
480 for(auto& info : dummyCatalogue) {
481 if(info.getRegisterName() == DUMMY_INTERRUPT) {
482 found = true;
483 break;
484 }
485 }
486 BOOST_CHECK(found == false);
487
488 // If I guess the name correctly, the register info is there
489 BOOST_CHECK(dummyBackend->getRegisterCatalogue().hasRegister(DUMMY_INTERRUPT));
490 auto info = dummyCatalogue.getRegister(DUMMY_INTERRUPT);
491 BOOST_CHECK_EQUAL(info.getRegisterName(), DUMMY_INTERRUPT);
492 BOOST_CHECK(!info.isReadable());
493 BOOST_CHECK(info.isWriteable());
494 BOOST_TEST(info.getDataDescriptor().fundamentalType() == DataDescriptor::FundamentalType::nodata);
495
496 dummyDevice.close();
497}
498
499BOOST_AUTO_TEST_CASE(testDummyInterrupt) {
500 ChimeraTK::Device dummyDevice;
501 dummyDevice.open("DUMMYD0");
502 dummyDevice.activateAsyncRead();
503
504 const std::string DUMMY_INTERRUPT{"/DUMMY_INTERRUPT_3"};
505 //
506 // auto ro_register = dummyDevice.getScalarRegisterAccessor<int>(DUMMY_INTERRUPT);
507 auto dummyAsyncAccessor = dummyDevice.getVoidRegisterAccessor(DUMMY_INTERRUPT);
508 BOOST_CHECK(!dummyAsyncAccessor.isReadable());
509 BOOST_CHECK(!dummyAsyncAccessor.isReadOnly());
510 BOOST_CHECK(dummyAsyncAccessor.isWriteable());
511 BOOST_CHECK_THROW(dummyAsyncAccessor.readNonBlocking(), ChimeraTK::logic_error);
512
513 const std::string INTERRUPT{"/!3"}; // canonical path for INTERRUPT3
514 auto asyncAccessor = dummyDevice.getVoidRegisterAccessor(INTERRUPT, {AccessMode::wait_for_new_data});
515 asyncAccessor.read(); // the initial value has arrived
516
517 BOOST_CHECK(!asyncAccessor.readNonBlocking());
518 dummyAsyncAccessor.write();
519 BOOST_CHECK(asyncAccessor.readNonBlocking());
520 BOOST_CHECK(!asyncAccessor.readNonBlocking());
521 dummyAsyncAccessor.write();
522 dummyAsyncAccessor.write();
523 BOOST_CHECK(asyncAccessor.readNonBlocking());
524 BOOST_CHECK(asyncAccessor.readNonBlocking());
525 BOOST_CHECK(!asyncAccessor.readNonBlocking());
526
527 dummyDevice.close();
528}
529
530/**********************************************************************************************************************/
531
532BOOST_AUTO_TEST_CASE(testAddressRange) {
533 TestableDummyBackend::AddressRange range24_8_0(0, 24, 8);
534
535 BOOST_CHECK(range24_8_0.offset == 24);
536 BOOST_CHECK(range24_8_0.sizeInBytes == 8);
537 BOOST_CHECK(range24_8_0.bar == 0);
538
539 TestableDummyBackend::AddressRange range24_8_1(1, 24, 8); // larger bar
540 TestableDummyBackend::AddressRange range12_8_1(1, 12, 8); // larger bar, smaller offset
541 TestableDummyBackend::AddressRange range28_8_0(0, 28, 8); // larger offset
542 TestableDummyBackend::AddressRange range28_8_1(1, 28, 8); // larger bar, larger offset
543 TestableDummyBackend::AddressRange range24_12_0(0, 24, 12); // different size, compares equal with range1
544
545 // compare 24_8_0 with the other cases as left argument
546 BOOST_CHECK((range24_8_0 < range24_8_1));
547 BOOST_CHECK((range24_8_0 < range12_8_1));
548 BOOST_CHECK((range24_8_0 < range28_8_0));
549 BOOST_CHECK((range24_8_0 < range28_8_1));
550 BOOST_CHECK(!(range24_8_0 < range24_12_0));
551
552 // compare 24_8_0 with the other cases as right argument
553 BOOST_CHECK(!(range24_8_1 < range24_8_0));
554 BOOST_CHECK(!(range12_8_1 < range24_8_0));
555 BOOST_CHECK(!(range28_8_0 < range24_8_0));
556 BOOST_CHECK(!(range28_8_1 < range24_8_0));
557 BOOST_CHECK(!(range24_12_0 < range24_8_0));
558}
559
560/**********************************************************************************************************************/
561
562BOOST_AUTO_TEST_CASE(testIsWriteRangeOverlap) {
563 // the only test not covered by the writeCallbackFunction test:
564 // An overlapping range in different bars
565 TestableDummyBackend* dummyBackend = f.getBackendInstance();
566 bool overlap = dummyBackend->isWriteRangeOverlap(
568 BOOST_CHECK(overlap == false);
569}
570
571/**********************************************************************************************************************/
572
573BOOST_AUTO_TEST_CASE(testFinalClosing) {
574 // all features have to be enabled before closing
575 TestableDummyBackend* dummyBackend = f.getBackendInstance();
576 BOOST_CHECK(dummyBackend->_barContents.size() != 0);
577 BOOST_CHECK(dummyBackend->_readOnlyAddresses.size() != 0);
578 BOOST_CHECK(dummyBackend->_writeCallbackFunctions.size() != 0);
579
580 dummyBackend->close();
581}
582
583/**********************************************************************************************************************/
584
585BOOST_AUTO_TEST_CASE(testOpenClose) {
586 TestableDummyBackend* dummyBackend = f.getBackendInstance();
587 // there have to be bars 0 and 2 with sizes 0x14C and 0x1000 bytes,
588 // plus the dma bar 0xD
589 // BOOST_CHECK((*dummyBackend)._barContents.size() == 3 );
590 BOOST_CHECK(dummyBackend->_barContents.size() == 3);
591 std::map<uint64_t, std::vector<int32_t>>::const_iterator bar0Iter = dummyBackend->_barContents.find(0);
592 BOOST_REQUIRE(bar0Iter != dummyBackend->_barContents.end());
593 BOOST_CHECK(bar0Iter->second.size() == 0x53); // 0x14C bytes in 32 bit words
594 std::map<uint64_t, std::vector<int32_t>>::const_iterator bar2Iter = dummyBackend->_barContents.find(2);
595 BOOST_REQUIRE(bar2Iter != dummyBackend->_barContents.end());
596 BOOST_CHECK(bar2Iter->second.size() == 0x400); // 0x1000 bytes in 32 bit words
597
598 // the "portmapFile" has an implicit conversion to bool to check
599 // if it points to NULL
600 // BOOST_CHECK(dummyBackend->_registerMapping);
601 BOOST_CHECK(dummyBackend->isOpen());
602 // it must always be possible to re-open a backend
603 dummyBackend->open();
604 BOOST_CHECK(dummyBackend->isOpen());
605
606 dummyBackend->close();
607 BOOST_CHECK(dummyBackend->isOpen() == false);
608 // it must always be possible to re-close a backend
609 dummyBackend->close();
610 BOOST_CHECK(dummyBackend->isOpen() == false);
611}
612
613/**********************************************************************************************************************/
614
617 f._backendInstance->close();
619 BOOST_CHECK(f._backendInstance->isOpen() == false);
620}
621
622/**********************************************************************************************************************/
623
625 f._backendInstance->open();
626 BOOST_CHECK(f._backendInstance->isOpen() == true);
627}
628
629/**********************************************************************************************************************/
630
631BOOST_AUTO_TEST_CASE(testCreateBackend) {
633 std::map<std::string, std::string> pararmeters;
634 BOOST_CHECK_THROW(DummyBackend::createInstance("", pararmeters), ChimeraTK::logic_error);
636 BOOST_CHECK_THROW(FactoryInstance.createBackend(NON_EXISTING_DEVICE), ChimeraTK::logic_error);
637 std::string cdd1 = "(dummy?map=" TEST_MAPPING_FILE ")";
638 auto backendInstance = FactoryInstance.createBackend(cdd1);
639 BOOST_CHECK(backendInstance);
641 BOOST_CHECK(backendInstance->isOpen() == false);
642
644 auto instance2 = BackendFactory::getInstance().createBackend(cdd1);
645 std::string cdd3 = "(dummy:FOO?map=" TEST_MAPPING_FILE ")";
646 auto instance3 = BackendFactory::getInstance().createBackend(cdd3);
647 auto instance4 = BackendFactory::getInstance().createBackend(cdd3);
648 std::string cdd5 = "(dummy:BAR?map=" TEST_MAPPING_FILE ")";
649 auto instance5 = BackendFactory::getInstance().createBackend(cdd5);
650
651 // instance 1 and 2 are the same
652 BOOST_CHECK(backendInstance.get() == instance2.get());
653 // instance 3 and 4 are the same
654 BOOST_CHECK(instance3.get() == instance4.get());
655
656 // instances 1, 3 and 5 are all different
657 BOOST_CHECK(backendInstance.get() != instance3.get());
658 BOOST_CHECK(backendInstance.get() != instance5.get());
659 BOOST_CHECK(instance3.get() != instance5.get());
660}
661
662/**********************************************************************************************************************/
BackendFactory is a the factory class to create devices.
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.
void setDMapFilePath(std::string dMapFilePath)
This function sets the _DMapFilePath.
boost::shared_ptr< DeviceBackend > createBackend(const std::string &aliasOrUri)
Create a new backend and return the instance as a shared pointer.
The base class for backends providing IO functionality for the Device class.
bool isOpen() override
Return whether a device has been opened or not.
Class allows to read/write registers from device.
Definition Device.h:39
void close()
Close the device.
Definition Device.cc:66
VoidRegisterAccessor getVoidRegisterAccessor(const RegisterPath &registerPathName, const AccessModeFlags &flags=AccessModeFlags({})) const
Get a VoidRegisterAccessor object for the given register.
Definition Device.cc:103
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:266
void activateAsyncRead() noexcept
Activate asyncronous read for all transfer elements where AccessMode::wait_for_new_data is set.
Definition Device.cc:91
void open(std::string const &aliasName)
Open a device by the given alias name from the DMAP file.
Definition Device.cc:58
static void checkSizeIsMultipleOfWordSize(size_t sizeInBytes)
The dummy device opens a mapping file instead of a device, and implements all registers defined in th...
void write(uint64_t bar, uint64_t address, int32_t const *data, size_t sizeInBytes) override
Write function to be implemented by backends.
void read(uint64_t bar, uint64_t address, int32_t *data, size_t sizeInBytes) override
Read function to be implemented by backends.
std::set< std::pair< uint64_t, uint64_t > > _readOnlyAddresses
void setWriteCallbackFunction(AddressRange addressRange, boost::function< void(void)> const &writeCallbackFunction)
std::multimap< AddressRange, boost::function< void(void)> > _writeCallbackFunctions
void open() override
Open the device.
std::map< uint64_t, std::vector< int32_t > > _barContents
static boost::shared_ptr< DeviceBackend > createInstance(std::string address, std::map< std::string, std::string > parameters)
bool isWriteRangeOverlap(AddressRange firstRange, AddressRange secondRange)
returns true if the ranges overlap and at least one of the overlapping registers can be written
void writeRegisterWithoutCallback(uint64_t bar, uint64_t address, int32_t data)
Not write-protected function for internal use only.
void setReadOnly(uint64_t bar, uint64_t address, size_t sizeInWords)
RegisterCatalogue getRegisterCatalogue() const override
Return the register catalogue with detailed information on all registers.
void close() final
Deactivates all asynchronous accessors and calls closeImpl().
bool hasRegister(const RegisterPath &registerPathName) const
Check if register with the given path name exists.
Exception thrown when a logic error has occured.
Definition Exception.h:51
boost::shared_ptr< TestableDummyBackend > _dummyBackend
boost::shared_ptr< ChimeraTK::DeviceBackend > _backendInstance
friend class DummyBackendTestSuite
TestableDummyBackend * getBackendInstance()
The TestableDummybackend is derived from DummyBackend to get access to the protected members.
std::set< std::pair< uint64_t, uint64_t > > _readOnlyAddresses
void setWriteCallbackFunction(AddressRange addressRange, boost::function< void(void)> const &writeCallbackFunction)
static boost::shared_ptr< DeviceBackend > createInstance(std::string, std::map< std::string, std::string > parameters)
std::multimap< AddressRange, boost::function< void(void)> > _writeCallbackFunctions
static BackendRegisterer backendRegisterer
std::map< uint64_t, std::vector< int32_t > > _barContents
TestableDummyBackend(std::string mapFileName)
bool isWriteRangeOverlap(AddressRange firstRange, AddressRange secondRange)
returns true if the ranges overlap and at least one of the overlapping registers can be written
void writeRegisterWithoutCallback(uint64_t bar, uint64_t address, int32_t data)
Not write-protected function for internal use only.
void setReadOnly(uint64_t bar, uint64_t address, size_t sizeInWords)
std::string getCurrentWorkingDirectory()
Returns absolute path to current working directory. The returned path ends with a forward slash.
constexpr auto DUMMY_WRITEABLE_SUFFIX
#define NON_EXISTING_DEVICE
#define TEST_MAPPING_FILE
BOOST_AUTO_TEST_CASE(testCheckSizeIsMultipleOfWordSize)
#define READ_ONLY_REGISTER_STRING
#define EXISTING_DEVICE