ChimeraTK-DeviceAccess  03.18.00
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 
24 using namespace boost::unit_test_framework;
25 namespace ChimeraTK {
26  using namespace ChimeraTK;
27 }
28 using 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 
41 static BackendFactory& FactoryInstance = BackendFactory::getInstance();
42 
51  public:
52  explicit TestableDummyBackend(std::string mapFileName) : DummyBackend(mapFileName) {}
53  using DummyBackend::checkSizeIsMultipleOfWordSize;
54  using DummyBackend::_barContents;
55  using DummyBackend::setReadOnly;
57  using DummyBackend::setWriteCallbackFunction;
58  using DummyBackend::writeRegisterWithoutCallback;
59  using DummyBackend::isWriteRangeOverlap;
60  using DummyBackend::_readOnlyAddresses;
61  using DummyBackend::_writeCallbackFunctions;
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 
78 class Fixture_t {
79  public:
80  Fixture_t() : a(0), b(0), c(0), _backendInstance() {
81  BackendFactory::getInstance().setDMapFilePath(TEST_DMAP_FILE_PATH);
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;
88  TestableDummyBackend* getBackendInstance();
89  friend class DummyBackendTestSuite;
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 
99 static 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 
112 BOOST_AUTO_TEST_CASE(testCheckSizeIsMultipleOfWordSize) {
113  // just some arbitrary numbers to test %4 = 0, 1, 2, 3
114  BOOST_CHECK_NO_THROW(TestableDummyBackend::checkSizeIsMultipleOfWordSize(24));
115 
117 
119 
121 }
122 
123 /**********************************************************************************************************************/
124 
125 BOOST_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 
155 BOOST_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
214 BOOST_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 
231 BOOST_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 
289 BOOST_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 
381 BOOST_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 
402 BOOST_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 
468 BOOST_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 
499 BOOST_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 
532 BOOST_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 
562 BOOST_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 
573 BOOST_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 
585 BOOST_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 
631 BOOST_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 /**********************************************************************************************************************/
NON_EXISTING_DEVICE
#define NON_EXISTING_DEVICE
Definition: testDummyBackend.cpp:39
ChimeraTK::BackendFactory::createBackend
boost::shared_ptr< DeviceBackend > createBackend(const std::string &aliasOrUri)
Create a new backend and return the instance as a shared pointer.
Definition: BackendFactory.cc:201
Fixture_t
Definition: testDummyBackend.cpp:78
TestableDummyBackend
The TestableDummybackend is derived from DummyBackend to get access to the protected members.
Definition: testDummyBackend.cpp:50
ChimeraTK::NumericAddressedBackend::close
void close() final
Deactivates all asynchronous accessors and calls closeImpl().
Definition: NumericAddressedBackend.cc:220
ChimeraTK::parserUtilities::getCurrentWorkingDirectory
std::string getCurrentWorkingDirectory()
Returns absolute path to current working directory. The returned path ends with a forward slash.
Definition: parserUtilities.cc:19
ChimeraTK::DummyBackend::write
void write(uint64_t bar, uint64_t address, int32_t const *data, size_t sizeInBytes) override
Write function to be implemented by backends.
Definition: DummyBackend.cc:61
ChimeraTK::Device::close
void close()
Close the device.
Definition: Device.cc:66
ChimeraTK::BackendFactory::getInstance
static BackendFactory & getInstance()
Static function to get an instance of factory.
Definition: BackendFactory.cc:191
ChimeraTK::RegisterCatalogue::hasRegister
bool hasRegister(const RegisterPath &registerPathName) const
Check if register with the given path name exists.
Definition: RegisterCatalogue.cc:49
DummyBackend.h
ChimeraTK::Device::getVoidRegisterAccessor
VoidRegisterAccessor getVoidRegisterAccessor(const RegisterPath &registerPathName, const AccessModeFlags &flags=AccessModeFlags({})) const
Get a VoidRegisterAccessor object for the given register.
Definition: Device.cc:103
Fixture_t::Fixture_t
Fixture_t()
Definition: testDummyBackend.cpp:80
Fixture_t::c
int c
Definition: testDummyBackend.cpp:92
ChimeraTK::VoidRegisterAccessor::read
void read()
Definition: VoidRegisterAccessor.h:44
ChimeraTK::DummyBackend::writeRegisterWithoutCallback
void writeRegisterWithoutCallback(uint64_t bar, uint64_t address, int32_t data)
Not write-protected function for internal use only.
Definition: DummyBackend.cc:44
ChimeraTK::DummyBackend::_readOnlyAddresses
std::set< std::pair< uint64_t, uint64_t > > _readOnlyAddresses
Definition: DummyBackend.h:104
ChimeraTK::DummyBackend::setWriteCallbackFunction
void setWriteCallbackFunction(AddressRange addressRange, boost::function< void(void)> const &writeCallbackFunction)
Definition: DummyBackend.cc:100
ChimeraTK::DummyBackend::AddressRange
Definition: DummyBackend.h:82
ChimeraTK::DummyBackend::open
void open() override
Open the device.
Definition: DummyBackend.cc:21
parserUtilities.h
ChimeraTK::DummyBackend::_writeCallbackFunctions
std::multimap< AddressRange, boost::function< void(void)> > _writeCallbackFunctions
Definition: DummyBackend.h:105
ChimeraTK::DeviceBackend
The base class for backends providing IO functionality for the Device class.
Definition: DeviceBackend.h:28
Fixture_t::a
int a
Definition: testDummyBackend.cpp:92
ChimeraTK::DummyBackend::AddressRange::bar
const uint64_t bar
Definition: DummyBackend.h:85
Fixture_t::increaseA
void increaseA()
Definition: testDummyBackend.cpp:93
Fixture_t::_backendInstance
boost::shared_ptr< ChimeraTK::DeviceBackend > _backendInstance
Definition: testDummyBackend.cpp:96
ChimeraTK::DummyBackendBase::checkSizeIsMultipleOfWordSize
static void checkSizeIsMultipleOfWordSize(size_t sizeInBytes)
Definition: DummyBackendBase.cc:35
ChimeraTK::DummyBackend::setReadOnly
void setReadOnly(uint64_t bar, uint64_t address, size_t sizeInWords)
Definition: DummyBackend.cc:86
READ_ONLY_REGISTER_STRING
#define READ_ONLY_REGISTER_STRING
Definition: testDummyBackend.cpp:36
ChimeraTK::Device::activateAsyncRead
void activateAsyncRead() noexcept
Activate asyncronous read for all transfer elements where AccessMode::wait_for_new_data is set.
Definition: Device.cc:91
EXISTING_DEVICE
#define EXISTING_DEVICE
Definition: testDummyBackend.cpp:38
ChimeraTK::BackendFactory
BackendFactory is a the factory class to create devices.
Definition: BackendFactory.h:26
Device.h
ChimeraTK::DummyBackend::AddressRange::sizeInBytes
const uint32_t sizeInBytes
Definition: DummyBackend.h:84
ChimeraTK::DummyBackend::_barContents
std::map< uint64_t, std::vector< int32_t > > _barContents
Definition: DummyBackend.h:103
ChimeraTK::DummyBackend::read
void read(uint64_t bar, uint64_t address, int32_t *data, size_t sizeInBytes) override
Read function to be implemented by backends.
Definition: DummyBackend.cc:49
ChimeraTK::NumericAddressedBackend::getRegisterCatalogue
RegisterCatalogue getRegisterCatalogue() const override
Return the register catalogue with detailed information on all registers.
Definition: NumericAddressedBackend.cc:230
TestableDummyBackend::TestableDummyBackend
TestableDummyBackend(std::string mapFileName)
Definition: testDummyBackend.cpp:52
ChimeraTK::Device
Class allows to read/write registers from device.
Definition: Device.h:39
Fixture_t::increaseC
void increaseC()
Definition: testDummyBackend.cpp:95
TEST_MAPPING_FILE
#define TEST_MAPPING_FILE
Definition: testDummyBackend.cpp:30
Fixture_t::getBackendInstance
TestableDummyBackend * getBackendInstance()
Definition: testDummyBackend.cpp:103
ChimeraTK::Device::open
void open(std::string const &aliasName)
Open a device by the given alias name from the DMAP file.
Definition: Device.cc:58
TestableDummyBackend::backendRegisterer
static BackendRegisterer backendRegisterer
Definition: testDummyBackend.cpp:74
TestableDummyBackend::BackendRegisterer::BackendRegisterer
BackendRegisterer()
Definition: testDummyBackend.cpp:68
ChimeraTK::for_each
void for_each(MAPTYPE &map, const LAMBDATYPE &lambda)
Variant of boost::fusion::for_each() to iterate a boost::fusion::map, which accepts a lambda instead ...
Definition: SupportedUserTypes.h:888
ChimeraTK::Device::getScalarRegisterAccessor
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:263
BOOST_AUTO_TEST_CASE
BOOST_AUTO_TEST_CASE(testCheckSizeIsMultipleOfWordSize)
Definition: testDummyBackend.cpp:112
BackendFactory.h
TestableDummyBackend::createInstance
static boost::shared_ptr< DeviceBackend > createInstance(std::string, std::map< std::string, std::string > parameters)
Definition: testDummyBackend.cpp:63
ChimeraTK::DUMMY_WRITEABLE_SUFFIX
constexpr auto DUMMY_WRITEABLE_SUFFIX
Definition: DummyBackendRegisterCatalogue.cc:10
ChimeraTK::DummyBackend::AddressRange::offset
const uint64_t offset
Definition: DummyBackend.h:83
Fixture_t::increaseB
void increaseB()
Definition: testDummyBackend.cpp:94
ChimeraTK::DummyBackend
The dummy device opens a mapping file instead of a device, and implements all registers defined in th...
Definition: DummyBackend.h:45
TEST_DMAP_FILE_PATH
#define TEST_DMAP_FILE_PATH
Definition: BackendFactory.h:18
ChimeraTK::BackendFactory::registerBackendType
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.
Definition: BackendFactory.cc:45
Exception.h
ChimeraTK::DeviceBackendImpl::isOpen
bool isOpen() override
Return whether a device has been opened or not.
Definition: DeviceBackendImpl.h:27
ChimeraTK::DummyBackend::isWriteRangeOverlap
bool isWriteRangeOverlap(AddressRange firstRange, AddressRange secondRange)
returns true if the ranges overlap and at least one of the overlapping registers can be written
Definition: DummyBackend.cc:138
ChimeraTK
Definition: DummyBackend.h:16
TestableDummyBackend::BackendRegisterer
Definition: testDummyBackend.cpp:66
Fixture_t::_dummyBackend
boost::shared_ptr< TestableDummyBackend > _dummyBackend
Definition: testDummyBackend.cpp:87
Fixture_t::b
int b
Definition: testDummyBackend.cpp:92
ChimeraTK::logic_error
Exception thrown when a logic error has occured.
Definition: Exception.h:51