ChimeraTK-DeviceAccess  03.18.00
testLMapMathPluginPushPars.cc
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 
5 #define BOOST_TEST_DYN_LINK
6 #define BOOST_TEST_MODULE LMapMathPluginTest
7 #include <boost/test/unit_test.hpp>
8 using namespace boost::unit_test_framework;
9 
10 #include "Device.h"
12 
13 using namespace ChimeraTK;
14 
15 BOOST_AUTO_TEST_SUITE(LMapMathPluginTestSuite)
16 
17 /**********************************************************************************************************************/
18 
20  using LogicalNameMappingBackend::LogicalNameMappingBackend;
21 
22  static boost::shared_ptr<DeviceBackend> createInstance(std::string, std::map<std::string, std::string>) {
23  return boost::make_shared<DummyForCleanupCheck>("mathPluginWithPushPars.xlmap");
24  }
25  ~DummyForCleanupCheck() override {
26  std::cout << "~DummyForCleanupCheck()" << std::endl;
27  cleanupCalled = true;
28  }
29 
30  struct BackendRegisterer {
33  "DummyForCleanupCheck", &DummyForCleanupCheck::createInstance, {"map"});
34  }
35  };
36  static std::atomic_bool cleanupCalled;
37 };
38 std::atomic_bool DummyForCleanupCheck::cleanupCalled{false};
39 static DummyForCleanupCheck::BackendRegisterer gDFCCRegisterer;
40 
41 BOOST_AUTO_TEST_CASE(testPushPars) {
42  setDMapFilePath("mathPluginWithPushPars.dmap");
43  {
44  // we enforce that initial values from variable definition in xlmap are never used in MathPugin
45  // - therefore, in test here, we must issue write for all push-parameters
46  // - then, version number check in MathPlugin will notice valid data has been provided after open
47  // - for poll-type accessors, version number check also succeeds since they get recent version numbers anyway
48  // Test needs to call activateAsyncRead but it must be irrelevant whether before or after writes
49 
50  ChimeraTK::Device targetDevice;
51  auto targetWriteCount = [&targetDevice]() {
52  auto exceptionDummyForTargetDev = boost::static_pointer_cast<ExceptionDummy>(targetDevice.getBackend());
53  return exceptionDummyForTargetDev->getWriteCount("MATHTEST/TARGET");
54  };
55  size_t writeCount = 0; // this counter tracks expected writes to target register
56 
57  targetDevice.open("HOLD");
58  auto accTarget = targetDevice.getScalarRegisterAccessor<uint32_t>("MATHTEST/TARGET");
59 
60  ChimeraTK::Device logicalDevice("EOD");
61  logicalDevice.open();
62  logicalDevice.activateAsyncRead();
63  auto pushPar = logicalDevice.getScalarRegisterAccessor<uint32_t>("DET/PUSHPAR");
64 
65  pushPar = 2;
66  pushPar.write();
67 
68  auto accMathWrite = logicalDevice.getScalarRegisterAccessor<double>("DET/X");
69  // we don't have main value (x in formula) yet, since it wasn't yet written.
70  // therefore, we expect to have no value yet for formula output (0 is default from dummy construction)
71  accTarget.read();
72  BOOST_TEST(int(accTarget) == 0);
73  BOOST_TEST(targetWriteCount() == writeCount); // just a sanity check
74 
75  // write to main value and check result
76  accMathWrite = 3;
77  accMathWrite.write();
78  accTarget.read();
79  BOOST_TEST(int(accTarget) == 10 * pushPar + accMathWrite);
80  // check that result was written exactly once
81  writeCount++;
82  BOOST_TEST(targetWriteCount() == writeCount);
83 
84  // write to push-parameter and check result
85  // note, it's a new feature that result is completely written when write() returns.
86  pushPar = 4;
87  pushPar.write();
88  accTarget.read();
89  BOOST_TEST(int(accTarget) == 10 * pushPar + accMathWrite);
90  writeCount++;
91  BOOST_TEST(targetWriteCount() == writeCount);
92 
93  // re-open and test again, with different write order (x,p) instead of (p,x)
94  logicalDevice.close();
95  targetDevice.open(); // open again since low-level device was closed by LNM
96  accTarget = 0; // reset result in dummy
97  accTarget.write();
98  writeCount++; // direct write from test also must be counted
99  logicalDevice.open();
100  logicalDevice.activateAsyncRead();
101 
102  accMathWrite = 5;
103  accMathWrite.write();
104  // check that MathPlugin did not yet write to the device - it must wait on push-parameter value
105  accTarget.read();
106  BOOST_CHECK_EQUAL(int(accTarget), 0);
107  BOOST_TEST(targetWriteCount() == writeCount);
108 
109  pushPar.write();
110  accTarget.read();
111  BOOST_TEST(int(accTarget) == 10 * pushPar + accMathWrite);
112  writeCount++;
113  BOOST_TEST(targetWriteCount() == writeCount);
114 
115  // write-behavior does not depend on whether or when we call activateAsyncRead
116  logicalDevice.close();
117  logicalDevice.open();
118  accTarget = 0;
119  accTarget.write();
120  writeCount++; // direct write from test also counts
121  BOOST_TEST(targetWriteCount() == writeCount); // sanity check (that we are counting correctly)
122  accMathWrite = 7;
123  accMathWrite.write(); // not all parameters written after open -> no target write
124  pushPar = 6;
125  pushPar.write();
126  writeCount++;
127  BOOST_TEST(int(accTarget) == 0);
128  BOOST_TEST(targetWriteCount() == writeCount);
129  logicalDevice.activateAsyncRead(); // does not trigger target write
130  BOOST_TEST(targetWriteCount() == writeCount);
131  accTarget.read();
132  BOOST_TEST(int(accTarget) == 10 * pushPar + accMathWrite);
133 
134  // we also need to test that write count is correct if there are two push-parameters
135  auto pushPar2 = logicalDevice.getScalarRegisterAccessor<uint32_t>("DET/PUSHPAR2");
136  auto accMathWrite2 = logicalDevice.getScalarRegisterAccessor<double>("DET/X2");
137  auto accTarget2 = targetDevice.getScalarRegisterAccessor<uint32_t>("MATHTEST/TARGET2");
138  auto targetWriteCount2 = [&targetDevice]() {
139  auto exceptionDummyForTargetDev = boost::static_pointer_cast<ExceptionDummy>(targetDevice.getBackend());
140  return exceptionDummyForTargetDev->getWriteCount("MATHTEST/TARGET2");
141  };
142  size_t writeCount2 = 0; // this counter tracks expected writes to target register TARGET2
143  pushPar = 1;
144  pushPar.write();
145  pushPar2 = 2;
146  pushPar2.write();
147  accMathWrite2 = 3;
148  accMathWrite2.write();
149  accTarget2.read();
150  BOOST_TEST(int(accTarget2) == 200 * pushPar2 + 20 * pushPar + 2 * accMathWrite2);
151  writeCount2++;
152  BOOST_TEST(targetWriteCount2() == writeCount2);
153  logicalDevice.close();
154  logicalDevice.open();
155  pushPar.write();
156  pushPar2.write();
157  accMathWrite2.write();
158  writeCount2++;
159  BOOST_TEST(targetWriteCount2() == writeCount2);
160  logicalDevice.activateAsyncRead();
161  BOOST_TEST(int(accTarget2) == 200 * pushPar2 + 20 * pushPar + 2 * accMathWrite2);
162  BOOST_TEST(targetWriteCount2() == writeCount2);
163  }
164  // regression test for bug https://redmine.msktools.desy.de/issues/11506
165  // (math plugin + push-parameter + shm has resource cleanup problem)
167 }
168 
169 BOOST_AUTO_TEST_CASE(testPushParsLateOpen) {
170  // test that push-parameter logic also works if accessor to variable is obtained first, before device is opened
171  // this is a regression test for bug https://redmine.msktools.desy.de/issues/11910
172  setDMapFilePath("mathPluginWithPushPars.dmap");
173  {
174  ChimeraTK::Device targetDevice;
175  auto targetWriteCount = [&targetDevice]() {
176  auto exceptionDummyForTargetDev = boost::static_pointer_cast<ExceptionDummy>(targetDevice.getBackend());
177  return exceptionDummyForTargetDev->getWriteCount("MATHTEST/TARGET");
178  };
179  size_t writeCount = 0; // this counter tracks expected writes to target register
180 
181  targetDevice.open("HOLD");
182  auto accTarget = targetDevice.getScalarRegisterAccessor<uint32_t>("MATHTEST/TARGET");
183 
184  ChimeraTK::Device logicalDevice("EOD");
185  auto pushPar = logicalDevice.getScalarRegisterAccessor<uint32_t>("DET/PUSHPAR");
186  auto accMathWrite = logicalDevice.getScalarRegisterAccessor<double>("DET/X");
187  logicalDevice.open();
188  logicalDevice.activateAsyncRead();
189 
190  pushPar = 2;
191  pushPar.write();
192 
193  // write to main value and check result
194  accMathWrite = 3;
195  accMathWrite.write();
196  accTarget.read();
197  BOOST_TEST(int(accTarget) == 10 * pushPar + accMathWrite);
198  // check that result was written exactly once
199  writeCount++;
200  BOOST_TEST(targetWriteCount() == writeCount);
201 
202  // write to push-parameter and check result
203  // note, it's a new feature that result is completely written when write() returns.
204  pushPar = 4;
205  pushPar.write();
206  accTarget.read();
207  BOOST_TEST(int(accTarget) == 10 * pushPar + accMathWrite);
208  writeCount++;
209  BOOST_TEST(targetWriteCount() == writeCount);
210  }
212 }
213 
214 /**********************************************************************************************************************/
215 
216 BOOST_AUTO_TEST_SUITE_END()
ExceptionDummyBackend.h
DummyForCleanupCheck
Definition: testAsyncVarAndHierarchicalInterruptsUnified.cpp:22
DummyForCleanupCheck::createInstance
static boost::shared_ptr< DeviceBackend > createInstance(std::string, std::map< std::string, std::string >)
Definition: testLMapMathPluginPushPars.cc:22
DummyForCleanupCheck::~DummyForCleanupCheck
~DummyForCleanupCheck() override
Definition: testLMapMathPluginPushPars.cc:25
ChimeraTK::Device::getBackend
boost::shared_ptr< DeviceBackend > getBackend()
Obtain the backend.
Definition: Device.cc:111
ChimeraTK::BackendFactory::getInstance
static BackendFactory & getInstance()
Static function to get an instance of factory.
Definition: BackendFactory.cc:191
BOOST_AUTO_TEST_CASE
BOOST_AUTO_TEST_CASE(testPushPars)
Definition: testLMapMathPluginPushPars.cc:41
LogicalNameMappingBackend.h
DummyForCleanupCheck::cleanupCalled
static std::atomic_bool cleanupCalled
Definition: testAsyncVarAndHierarchicalInterruptsUnified.cpp:39
DummyForCleanupCheck::createInstance
static boost::shared_ptr< DeviceBackend > createInstance(std::string, std::map< std::string, std::string > parameters)
Definition: testAsyncVarAndHierarchicalInterruptsUnified.cpp:25
Device.h
DummyForCleanupCheck::BackendRegisterer::BackendRegisterer
BackendRegisterer()
Definition: testLMapMathPluginPushPars.cc:31
DummyForCleanupCheck::BackendRegisterer
Definition: testAsyncVarAndHierarchicalInterruptsUnified.cpp:33
ChimeraTK::Device
Class allows to read/write registers from device.
Definition: Device.h:39
ChimeraTK::LogicalNameMappingBackend
Backend to map logical register names onto real hardware registers.
Definition: LogicalNameMappingBackend.h:19
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
BackendRegisterer
Definition: testGenericMuxedInterruptDistributor.cpp:70
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
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
ChimeraTK::setDMapFilePath
void setDMapFilePath(std::string dmapFilePath)
Set the location of the dmap file.
Definition: Utilities.cpp:327
ChimeraTK
Definition: DummyBackend.h:16