5#define BOOST_TEST_DYN_LINK
6#define BOOST_TEST_MODULE DataConsistencyGroupTest
7#include <boost/test/unit_test.hpp>
8using namespace boost::unit_test_framework;
17BOOST_AUTO_TEST_SUITE(HistorizedDataMatchingTestSuite)
24 unsigned numDiscarded = 0;
26 while((
id = rag.readAnyNonBlocking()).isValid()) {
39 dev.
open(
"(logicalNameMap?map=historizedDataMatching.xlmap)");
49 gettimeofday(&now,
nullptr);
50 timeout.tv_sec = now.tv_sec + 1;
51 timeout.tv_nsec = now.tv_usec * 300;
52 sem_timedwait(&
sem, &timeout);
57 void updaterLoop(
unsigned nLoops,
unsigned delay,
unsigned duplicateVns = 0,
bool catchUp =
false) {
58 std::cout <<
"updaterLoop: delay=" << delay <<
", duplicateVns=" << duplicateVns << std::endl;
62 std::vector<ChimeraTK::VersionNumber> vs(nLoops);
63 for(
unsigned loopCount = 0; loopCount < nLoops; loopCount++) {
64 std::cout <<
"updaterLoop: writing value " << loopCount << std::endl;
65 accA = (int32_t)loopCount;
69 if(loopCount > 0 && duplicateVns > 0) {
70 vs[loopCount] = vs[loopCount - 1];
76 accA.write(vs[loopCount]);
77 if(loopCount >= delay) {
78 accB = (int32_t)(loopCount - delay);
79 accB.write(vs[loopCount - delay]);
81 if(loopCount == nLoops - 1 && delay > 0 && catchUp) {
83 for(
unsigned i = loopCount - delay + 1; i < nLoops; i++) {
90 std::cout <<
"updaterLoop: exception, wait and retry write.." << std::endl;
91 std::this_thread::sleep_for(std::chrono::milliseconds{500});
117 std::cout <<
"test1: no history" << std::endl;
119 const unsigned nLoops = 4;
120 for(
unsigned delay = 0; delay <= 2; delay++) {
122 unsigned nUpdates = 0;
123 unsigned nConsistentUpdates = 0;
124 std::thread updaterThread1([&]() { updaterLoop(nLoops, delay); });
128 auto id = rag.readAny();
129 auto& acc =
id == readAccA.getId() ? readAccA : readAccB;
130 std::cout <<
"readAny: seeing update for target " << acc.getName() <<
" vs " << acc.getVersionNumber()
134 if(readAccA.getVersionNumber() == readAccB.getVersionNumber()) {
135 nConsistentUpdates++;
137 if(
id == readAccA.getId()) {
142 catch(boost::thread_interrupted& e) {
143 std::cout <<
"thread interrupted" << std::endl;
145 updaterThread1.join();
147 unsigned nExpectedConsistentUpdates = (delay == 0) ? nUpdates / 2 : 0;
148 BOOST_TEST(nConsistentUpdates == nExpectedConsistentUpdates);
149 BOOST_TEST(nUpdates == nLoops + nLoops - delay);
152 std::cout <<
"test1: with history" << std::endl;
155 for(
unsigned delay = 0; delay <= 2; delay++) {
159 unsigned nUpdates = 0;
160 unsigned nConsistentUpdates = 0;
161 std::thread updaterThread1([&]() { updaterLoop(nLoops, delay); });
165 auto id = rag.readAny();
166 bool isConsistent = dg.update(
id);
168 if(readAccA.getVersionNumber() == readAccB.getVersionNumber()) {
169 nConsistentUpdates++;
172 BOOST_TEST(isConsistent);
173 BOOST_TEST(readAccA.getVersionNumber() == readAccB.getVersionNumber());
174 BOOST_TEST(readAccA == readAccB);
179 catch(boost::thread_interrupted& e) {
180 std::cout <<
"thread interrupted" << std::endl;
182 updaterThread1.join();
183 BOOST_TEST(nConsistentUpdates == nLoops - delay);
184 BOOST_TEST(nConsistentUpdates == nUpdates);
189 std::cout <<
"testDuplicateVns" << std::endl;
195 const unsigned nLoops = 4;
196 const unsigned nDuplicateVns = 1;
198 for(
unsigned delay = 0; delay <= 2; delay++) {
202 unsigned nUpdates = 0;
203 unsigned nConsistentUpdates = 0;
204 std::thread updaterThread1([&]() { updaterLoop(nLoops, delay, nDuplicateVns); });
208 auto id = rag.readAny();
210 bool isConsistent = dg.update(
id);
211 if(readAccA.getVersionNumber() == readAccB.getVersionNumber()) {
212 nConsistentUpdates++;
215 BOOST_TEST(isConsistent);
216 BOOST_TEST(readAccA.getVersionNumber() == readAccB.getVersionNumber());
221 catch(boost::thread_interrupted& e) {
222 std::cout <<
"thread interrupted" << std::endl;
224 updaterThread1.join();
225 unsigned nExpectedUpdates = nLoops - delay;
230 BOOST_TEST(nConsistentUpdates == nExpectedUpdates);
231 BOOST_TEST(nConsistentUpdates == nUpdates);
236 std::cout <<
"testExceptions" << std::endl;
241 const unsigned nLoops = 6;
242 const unsigned delay = 0;
247 unsigned nUpdates = 0;
248 unsigned nConsistentUpdates = 0;
249 std::thread updaterThread1([&]() { updaterLoop(nLoops, delay); });
254 auto id = rag.readAny();
255 auto& acc =
id == readAccA.getId() ? readAccA : readAccB;
256 std::cout <<
"readAny: seeing update for target " << acc.getName() <<
" vs " << acc.getVersionNumber()
257 <<
" values " << readAccA <<
"," << readAccB << std::endl;
259 bool isConsistent = dg.update(
id);
261 if(readAccA.getVersionNumber() == readAccB.getVersionNumber()) {
262 nConsistentUpdates++;
265 BOOST_TEST(isConsistent);
266 BOOST_TEST(readAccA.getVersionNumber() == readAccB.getVersionNumber());
267 BOOST_TEST(readAccA == readAccB);
272 dev.setException(
"exception");
274 dev.activateAsyncRead();
281 catch(boost::thread_interrupted& e) {
282 std::cout <<
"thread interrupted" << std::endl;
285 std::cout <<
"runtime error: " << e.what() << std::endl;
289 updaterThread1.join();
291 BOOST_TEST(nConsistentUpdates == nLoops - delay + 1);
292 BOOST_TEST(nConsistentUpdates == nUpdates);
297 std::cout <<
"testCatchUp" << std::endl;
302 const unsigned nLoops = 6;
303 const unsigned delay = 2;
308 unsigned nUpdates = 0;
309 unsigned nConsistentUpdates = 0;
310 std::thread updaterThread1([&]() { updaterLoop(nLoops, delay,
false,
true); });
315 auto id = rag.readAny();
316 auto& acc =
id == readAccA.getId() ? readAccA : readAccB;
317 std::cout <<
"readAny: seeing update for target " << acc.getName() <<
" vs " << acc.getVersionNumber()
318 <<
" values " << readAccA <<
"," << readAccB << std::endl;
320 bool isConsistent = dg.update(
id);
322 if(readAccA.getVersionNumber() == readAccB.getVersionNumber()) {
323 nConsistentUpdates++;
326 BOOST_TEST(isConsistent);
327 BOOST_TEST(readAccA.getVersionNumber() == readAccB.getVersionNumber());
328 BOOST_TEST(readAccA == readAccB);
334 catch(boost::thread_interrupted& e) {
335 std::cout <<
"thread interrupted" << std::endl;
338 updaterThread1.join();
339 BOOST_TEST(nConsistentUpdates == nLoops);
340 BOOST_TEST(nConsistentUpdates == nUpdates);
344 std::cout <<
"testInitialValues" << std::endl;
353 BOOST_TEST(nDiscarded == 1);
355 BOOST_TEST(readAccA.getVersionNumber() !=
VersionNumber(
nullptr));
356 BOOST_TEST(readAccB.getVersionNumber() !=
VersionNumber(
nullptr));
366 dev.
open(
"(logicalNameMap?map=historizedDataMatching.xlmap)");
369 for(
bool extraDecorators : {
false,
true}) {
375 accA.setAndWrite(100, vs1);
376 accB.setAndWrite(99, vs0);
382 if(extraDecorators) {
386 auto da = boost::make_shared<NDRegisterAccessorDecorator<int32_t>>(readAccA.getImpl());
387 readAccA.replace(da);
388 auto db = boost::make_shared<NDRegisterAccessorDecorator<int32_t>>(readAccB.getImpl());
389 readAccB.replace(db);
393 readAccA.readLatest();
394 readAccB.readLatest();
395 BOOST_TEST(readAccA == 100);
396 BOOST_TEST(readAccA.getVersionNumber() == vs1);
397 BOOST_TEST(readAccB == 99);
398 BOOST_TEST(readAccB.getVersionNumber() == vs0);
403 BOOST_TEST(readAccA.getVersionNumber() == vs1);
404 BOOST_TEST(readAccB.getVersionNumber() == vs0);
405 BOOST_TEST(dg.isConsistent() ==
false);
407 BOOST_TEST(readAccA == 100);
408 BOOST_TEST(readAccB == 99);
411 accB.setAndWrite(100, vs1);
412 auto id = rag.readAny();
413 BOOST_TEST(
id == readAccB.getId());
414 BOOST_TEST(readAccA == 100);
415 BOOST_TEST(readAccB == 100);
416 BOOST_TEST(readAccA.getVersionNumber() == vs1);
417 BOOST_TEST(readAccB.getVersionNumber() == vs1);
418 BOOST_TEST(dg.isConsistent() ==
true);
423 std::cout <<
"testIllegalUse" << std::endl;
432BOOST_AUTO_TEST_SUITE_END()
Group several registers (= TransferElement) which ensures data consistency across multiple variables ...
Class allows to read/write registers from device.
ScalarRegisterAccessor< UserType > getScalarRegisterAccessor(const RegisterPath ®isterPathName, size_t wordOffsetInRegister=0, const AccessModeFlags &flags=AccessModeFlags({})) const
Get a ScalarRegisterObject object for the given register.
void activateAsyncRead() noexcept
Activate asyncronous read for all transfer elements where AccessMode::wait_for_new_data is set.
void open(std::string const &aliasName)
Open a device by the given alias name from the DMAP file.
void replace(const NDRegisterAccessorAbstractor< UserType > &newAccessor)
Assign a new accessor to this NDRegisterAccessorAbstractor.
Group several registers (= TransferElement) to allow waiting for an update of any of the registers.
Accessor class to read and write scalar registers transparently by using the accessor object like a v...
void interrupt()
Return from a blocking read immediately and throw boost::thread_interrupted.
Simple class holding a unique ID for a TransferElement.
Class for generating and holding version numbers without exposing a numeric representation.
Exception thrown when a logic error has occured.
Exception thrown when a runtime error has occured.
ScalarRegisterAccessor< int32_t > readAccB
ScalarRegisterAccessor< int32_t > readAccA
void updaterLoop(unsigned nLoops, unsigned delay, unsigned duplicateVns=0, bool catchUp=false)
BOOST_FIXTURE_TEST_CASE(test1, Fixture)
BOOST_AUTO_TEST_CASE(testInitialValuesConsistency)
auto emptyQueues(ReadAnyGroup &rag, DataConsistencyGroup *dg=nullptr)