GCC Code Coverage Report


Directory: ./
File: src/dp/pipeline.test.cc
Date: 2025-09-01 06:19:01
Exec Total Coverage
Lines: 46 46 100.0%
Functions: 7 7 100.0%
Branches: 34 42 81.0%

Line Branch Exec Source
1 #include "na64dp/pipeline.hh"
2 #include "na64dp/abstractHandler.hh"
3 #include "na64dp/abstractEventSource.hh"
4 #include "na64event/data/event.hh"
5 //#include "na64dp/abstractHitHandler.hh"
6 //#include "na64dp/abstractEventSource.hh"
7 //#include "na64detID/TBName.hh"
8 //#include "na64dp/pipeline.hh"
9
10 #include <gtest/gtest.h>
11
12 /**\file tests/PipelineBasic.cc
13 *
14 * Testing unit processing basic pipeline's discrimination logic as well as
15 * construction of basic structures.
16 * Creates two handlers within pipeline over a mocking source. The source
17 * sets only the event identifier and yields fixed number of events. The
18 * handlers discriminates each 2nd and 3rd event and records event ids passed
19 * by.
20 * Then the unit verifies that all the events supposed to be passed were passed
21 * indeed.
22 *
23 * Other suite tests full stack of pipeline API with mock classes of event source, event
24 * handler and calibration handle.
25 * */
26
27 namespace na64dp {
28 namespace test {
29
30 // Mock handler used for testing purposes; discriminates events by their IDs
31 class MockHandler : public AbstractHandler {
32 public:
33 std::vector<EventID> idsPassed;
34 private:
35 int _evIDDiv;
36 public:
37 2 MockHandler( int evIDDiv )
38 2 : AbstractHandler(log4cpp::Category::getInstance("testing"))
39
3/3
✓ Branch 1 taken 2 times.
✓ Branch 4 taken 2 times.
✓ Branch 7 taken 2 times.
6 , _evIDDiv(evIDDiv) {}
40 27 virtual ProcRes process_event(event::Event & event) override {
41
2/2
✓ Branch 1 taken 15 times.
✓ Branch 2 taken 12 times.
27 if( event.id % _evIDDiv ) {
42 15 idsPassed.push_back( event.id );
43 15 return kOk;
44 }
45 12 return kDiscriminateEvent;
46 }
47 };
48
49 // Mock events source
50 class MockSource : public AbstractEventSource {
51 private:
52 size_t _evCounter
53 , _evMaxCount;
54 public:
55 1 MockSource( size_t max )
56 1 : AbstractEventSource()
57 1 , _evCounter(0)
58 1 , _evMaxCount(max) {}
59 19 virtual bool read(event::Event & e, event::LocalMemory &) override {
60 19 ++_evCounter;
61 19 e.id = EventID(_evCounter);
62 19 return _evCounter < _evMaxCount;
63 }
64 };
65
66 // Builds pipeline of two mock handlers over mock source, checks the basic
67 // discrimination logic
68 8 TEST(Pipeline, EventDiscriminationLogic) {
69
2/3
✓ Branch 1 taken 1 times.
✓ Branch 4 taken 1 times.
✗ Branch 7 not taken.
2 MockHandler * h1 = new MockHandler(2) // discriminate each 2nd event
70
2/3
✓ Branch 1 taken 1 times.
✓ Branch 4 taken 1 times.
✗ Branch 7 not taken.
2 , * h2 = new MockHandler(3) // discriminate each 3rd event
71 ;
72
1/1
✓ Branch 1 taken 1 times.
2 Pipeline p;
73
1/1
✓ Branch 1 taken 1 times.
2 p.push_back(h1);
74
1/1
✓ Branch 1 taken 1 times.
2 p.push_back(h2);
75
1/1
✓ Branch 1 taken 1 times.
2 MockSource src(19);
76
77 // TODO: since some important use cases of allocators lifecycle are not
78 // clear yet, the block below will most probably change at some point.
79 #if 0
80 auto lmem = LocalMemory::instantiate();
81 bool hasRead = false;
82 do {
83 auto evRef = lmem.new_event();
84 if( hasRead = src.read(*evRef) ) {
85 p.process(evRef);
86 }
87 } while( hasRead );
88 #else
89 {
90 // Physical memory block holding the event data created
91 char buffer[1024];
92 // ^^^ TODO: this must be enlarged sometimes, when we are changing
93 // event size... We have to foresee some adaptive mechanism here.
94
95 // Memory management strategy
96 // TODO: handle cases other than `PlainBlock`
97 // (will vary depending on the NA64SW_EVMALLOC_STRATEGY)
98 2 mem::PlainBlock block(buffer, sizeof(buffer));
99 // Reentrant memory access API
100
1/1
✓ Branch 1 taken 1 times.
2 LocalMemory lmem(block);
101
102 for(;;) {
103 // Create event for current iteration
104
1/1
✓ Branch 1 taken 19 times.
38 auto evRef = lmem.create<event::Event>(lmem);
105
106
1/1
✓ Branch 2 taken 19 times.
38 util::reset(*evRef);
107
2/2
✓ Branch 2 taken 18 times.
✓ Branch 3 taken 1 times.
38 if( src.read(*evRef, lmem) )
108
1/1
✓ Branch 2 taken 18 times.
36 p.process(*evRef, lmem);
109 else
110 2 break;
111
1/1
✓ Branch 1 taken 18 times.
36 lmem.reset();
112 36 block.reset();
113 74 }
114 2 }
115 #endif
116 // h1 has contain increasing odd numbers: 1, 3, 5 ... 17
117 2 int nC = -1;
118
2/3
✓ Branch 2 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
2 EXPECT_EQ( h1->idsPassed.size(), 9 );
119
2/2
✓ Branch 5 taken 9 times.
✓ Branch 6 taken 1 times.
20 for( auto n : h1->idsPassed ) {
120
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 9 times.
18 EXPECT_TRUE( n%2 );
121
2/3
✓ Branch 1 taken 9 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 9 times.
18 EXPECT_EQ( nC += 2, n );
122 }
123 // h1 has contain 1, 5, 7, 11, 13, 17
124 2 const int checkArr[] = { 1, 5, 7, 11, 13, 17 };
125 2 const int * nCPtr = checkArr;
126
2/3
✓ Branch 2 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
2 EXPECT_EQ( h2->idsPassed.size(), 6 );
127
2/2
✓ Branch 5 taken 6 times.
✓ Branch 6 taken 1 times.
14 for(auto n : h2->idsPassed) {
128
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 6 times.
12 EXPECT_TRUE( n%3 );
129
2/3
✓ Branch 1 taken 6 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 6 times.
12 EXPECT_EQ( *(nCPtr++), n );
130 }
131 2 }
132
133 #if 0
134
135 /**\brief A mock detector naming
136 *
137 * Represents a mock calibration data source: applies distinct naming schemata for
138 * run #1 and run #2.
139 **/
140 class MockCalibHandle : public iCalibHandle {
141 private:
142 int _cRunNo;
143 utils::TBNameMappings _nameMappings;
144 protected:
145 // This mock implementation considers only run #1 and #2 and changes name
146 // mappings between them.
147 virtual bool _new_run_no( na64ee::runNo_t rNo ) override {
148 bool doRecache = (rNo != _cRunNo);
149 if(doRecache) {
150 utils::TBNameMappings & m = _nameMappings;
151 m.clear_mappings();
152 if( 1 == rNo ) {
153 m.add_chip( "SADCT", 0x1
154 , "A mock chip for SADC hits, v#1."
155 , "{kin}{statNum2}/{hist}" );
156 m.add_chip( "APVT", 0x2
157 , "A mock chip for APV hits, v#1."
158 , "{kin}{statNum2}/{hist}" );
159
160 m.define_kin( "NCAL", 0x1, "Non-existing SADC detector #1"
161 , "SADCT", "{kin}{statNum}" );
162 m.define_kin( "NCBL", 0x2, "Non-existing SADC detector #2"
163 , "SADCT", "{kin}{statNum}" );
164 m.define_kin( "TRCK", 0x1, "Non-existing APV detector #1"
165 , "APVT", "{kin}{statNum2}__" );
166 } else if( 2 == rNo ) {
167 m.add_chip( "SADCT", 0x3, "A mock chip for SADC hits, v#2.", "{kin}{statNum2}/{hist}" );
168 m.add_chip( "APVT", 0x1, "A mock chip for APV hits, v#2.", "{kin}{statNum2}/{hist}" );
169
170 m.define_kin( "NCAL", 0x1, "Non-existing SADC detector #1"
171 , "SADCT", "{kin}{statNum2}" );
172 m.define_kin( "TRCK", 0x4, "Non-existing APV detector #1"
173 , "APVT", "{kin}{statNum2}__" );
174 m.define_kin( "TRDK", 0x3, "Non-existing APV detector #2"
175 , "APVT", "{kin}{statNum2}__" );
176 } else {
177 throw std::runtime_error("Invalid run number provided.");
178 }
179 }
180 _cRunNo = rNo;
181 return doRecache;
182 }
183 virtual bool _has_calib_info( const std::string &, DetID_t ) const override { return false; }
184 virtual const CalibBuffer & _get_calib_info( const std::string &, DetID_t ) override { throw std::runtime_error("forbidden call"); }
185 virtual void _set_calib_info( const std::string &, DetID_t, const CalibBuffer & ) override {}
186 public:
187 MockCalibHandle() {}
188 virtual const utils::TBNameMappings & naming() override {
189 return _nameMappings;
190 }
191 };
192
193 class MockIndex : public iCalibRunIndex {
194 public:
195 const int n;
196 MockIndex(int n_) : n(n_) {}
197 virtual void updates( EventID oldEventID
198 , EventID newEventID
199 , UpdatesList & ul ) {
200 uint8_t ctrlOctet = newEventID.run_no();
201 if( 1 == n ) {
202 ctrlOctet &= 0xf;
203 } else {
204 ctrlOctet >>= 4;
205 }
206 if( ! (ctrlOctet & 0x1) ) return;
207 oldEventID.event_no(n - 1);
208 std::string loaderName = (0x2 & ctrlOctet) ? "loader1" : "loader2"
209 , calibTypeName = (0x4 & ctrlOctet) ? "foo" : "bar"
210 ;
211 if( 0x8 & ctrlOctet ) {
212 ul.enqueue_update_of_type<int>( calibTypeName
213 , loaderName
214 , oldEventID );
215 } else {
216 ul.enqueue_update_of_type<float>( calibTypeName
217 , loaderName
218 , oldEventID );
219 }
220 }
221 };
222
223 class MockLoader : public iCalibDataLoader {
224 public:
225 const int n;
226 uint8_t & ctrlByte;
227 MockLoader( int n_, uint8_t & ctrlByte_ ) : n(n_), ctrlByte(ctrlByte_) {}
228 /// Loads calibration data into given dispatcher instance
229 virtual void load_data( EventID eventID
230 , Dispatcher::CIDataID cidID
231 , Dispatcher & ) override {
232 std::string calibName = cidID.second;
233 std::type_index calibTypeIndex = cidID.first;
234 uint8_t ctrlSeq = 0x0;
235 if( calibName == "foo" ) {
236 ctrlSeq |= 0x4;
237 } else {
238 ASSERT_STREQ( calibName.c_str(), "bar" );
239 }
240 if( std::type_index(typeid(int)) == calibTypeIndex ) {
241 ctrlSeq |= 0x8;
242 } else {
243 ASSERT_EQ(std::type_index(typeid(float)), calibTypeIndex);
244 }
245 if( 1 == n ) {
246 ctrlSeq |= 0x2;
247 }
248 ctrlSeq |= 0x1; // unconditionally set, indicating that index has emitted update
249 ctrlByte |= (ctrlSeq << (eventID.event_no() ? 4 : 0) );
250 }
251 /// Shall return whether the specified calibration is available
252 virtual bool has( EventID
253 , Dispatcher::CIDataID ) {
254 return true;
255 }
256 };
257
258 //template<typename HitT>
259 //struct MockSourceTraits;
260
261 /// Mock event source, producing distinct types of events dor two runs
262 class MockEventSource : public AbstractEventSource {
263 public:
264 std::map<std::string, size_t> nHitsByName;
265 private:
266 int _eventCounter;
267 //MockCalibHandle & _ch; //?
268
269 template<typename HitT>
270 void _fill_hits( event::Event & e, int count, const char * tbn ) {
271 if( count < 1 ) return; // no fill for false counts
272 char * postfix = NULL;
273 const utils::TBNameMappings & m = _ch.naming();
274 DetID did = m.detector_id_by_ddd_name( tbn, postfix );
275 _log.debug( "Name mappings for event %d yielded detector ID %#x for"
276 " name \"%s\" (filling with %d hits).", e.id, did.id, tbn, count );
277 //EXPECT_EQ('\0', *postfix);
278 if( nHitsByName.end() != nHitsByName.find(tbn) ) {
279 nHitsByName[tbn] += count;
280 } else {
281 nHitsByName[tbn] = count;
282 }
283 for( int i = 0; i < count; ++i ) {
284 DetID thisDid(did); // we copy did (chip + kin + station number) to
285 thisDid.payload(i+1); // modify the payload
286 try {
287 HitT & newHit = *EvFieldTraits<HitT>::inserter(*this)( e, thisDid, _ch.naming() );
288 // ...
289 } catch( std::exception & e ) {
290 _log.error( "Error occured while imposing new hit entry for"
291 " detector id %#x (chip = %#x \"%s\", kin = %#x,"
292 " stat.num.=%d, payload=%#x, name=\"%s\"/\"%s\")"
293 , thisDid.id
294 , thisDid.chip(), m.chip_name( thisDid.chip() )
295 , thisDid.kin()
296 , thisDid.number()
297 , thisDid.payload()
298 , tbn
299 , m.detector_ddd_name_by_id( thisDid ).c_str() );
300 throw;
301 }
302 _log.debug( "Put hit in map; banks sizes: SADC - %zu, APV - %zu,"
303 " maps sizes: SADC - %zu, APV - %zu."
304 , _banks.bankSADC.size()
305 , _banks.bankAPV.size()
306 , e.sadcHits.size()
307 , e.apvHits.size() );
308 }
309 }
310 public:
311 MockEventSource( calib::Manager & mgr ) : AbstractEventSource(banks)
312 , _eventCounter(0)
313 {}
314
315 virtual bool read( event::Event & e ) override {
316 na64ee::UEventID euID = { .numeric = na64ee::assemble_event_id( _eventCounter > 70 ? 1 : 2
317 , _eventCounter/10
318 , _eventCounter%10 ) };
319 e.id = euID.chunklessLayout;
320 // For series below the following summation formulae takes place:
321 // * number of hits per event (negative must be zeroed):
322 // n := c % D - B
323 // * number of events where hits of these type are present:
324 // E := floor(N/D)*(D - B - 1)
325 // May be understood as numer of passed events (D - B - 1) of on
326 // interval of length D multiplied by numer of intervals.
327 // * overall number of hits "generated" for certain detector type:
328 // k = D - B - 1
329 // S := floor(N/D)*(n*(n-1)/2)
330 // May be derived from previous takin into account that whithin each
331 // interval, n-th triangular number of hits is generated -- a
332 // binomial coefficient (n-2, 2) which is (n^2 - n)/2.
333 if( _eventCounter < 50 ) {
334 // run #1
335 _ch.set_run_no(1); // has to cause re-caching of all subscribers
336 _fill_hits<event::SADCHit>( e, _eventCounter%3 , "NCAL1" );
337 _fill_hits<event::SADCHit>( e, _eventCounter%5 - 2 , "NCAL2" );
338 _fill_hits<event::SADCHit>( e, _eventCounter%3 - 1 , "NCBL1" );
339 _fill_hits<event::APVHit>( e, _eventCounter%5 - 1 , "TRCK01" );
340 } else {
341 // run #2
342 _ch.set_run_no(2); // has to cause re-caching of all subscribers
343 _fill_hits<event::SADCHit>( e, _eventCounter%5 , "NCAL01" );
344 _fill_hits<event::APVHit>( e, _eventCounter%3 - 1 , "TRCK01__" );
345 _fill_hits<event::APVHit>( e, _eventCounter%10 , "TRDK01__" );
346 _fill_hits<event::APVHit>( e, _eventCounter%10- 5 , "TRDK02__" );
347 }
348 // Expected counts:
349 // NCAL1 : 100 + 49 = 149
350 // NCAL2 : 30
351 // NCBL1 : 16
352 // TRCK01 : 60 + 17 = 77
353 // TRDK01 : 225
354 // TRDK02 : 50
355 return (++_eventCounter) <= 100;
356 }
357 };
358
359 struct MockHandlerStats {
360 size_t nHits
361 , nHitsInEvent;
362 std::map<DetID_t, size_t> nHitsByDet;
363 std::set<na64ee::EventNumericID> eventIDs;
364
365 MockHandlerStats() : nHits(0) {}
366 };
367
368 // A mock hits processing handler template
369 // Gathers statistics of hits passed by.
370 template<typename HitT>
371 class MockHandler : public AbstractHitHandler<HitT>
372 , public MockHandlerStats {
373 protected:
374 bool _removeOdd;
375 public:
376 MockHandler( iCalibHandle * ch
377 , const std::string & only=""
378 , bool removeOdd=false )
379 : AbstractHitHandler<HitT>(ch, only)
380 , _removeOdd(removeOdd) {}
381
382 virtual AbstractHandler::ProcRes process_event( Event * e ) override {
383 nHitsInEvent = 0;
384 na64ee::UEventID ueID = { .chunklessLayout = e->id };
385 auto it = eventIDs.find(ueID.numeric);
386 EXPECT_EQ( it, eventIDs.end() ); // assure events are unique
387 eventIDs.insert(ueID.numeric);
388 return AbstractHitHandler<HitT>::process_event(e);
389 }
390
391 virtual bool process_hit( na64ee::AbsEventID eventID
392 , DetID_t detID
393 , HitT & hit ) override {
394 // increase general hits counters
395 ++nHits;
396 ++nHitsInEvent;
397 // increase by-detector hit counter
398 auto ir = nHitsByDet.emplace(detID, 1);
399 if( ! ir.second ) { // insertion failed because of existing element
400 ++ (ir.first->second);
401 }
402 if( _removeOdd && nHits%2 ) {
403 this->_set_hit_erase_flag();
404 }
405 return true;
406 }
407 };
408
409 TEST(Pipeline, HitProcessorRecachingLogic) {
410 // This string keys will be used to identify the mock SADC detectors
411 const char sadcDetNames[][8] = { "NCAL1", "NCAL01"
412 , "NCAL2"
413 , "NCBL01"
414 };
415 // This string keys will be used to identify mock APV detectors
416 const char apvDetNames[][8] = { "TRCK01"
417 , "TRDK01"
418 , "TRDK02"
419 };
420 // This array will be filled with ptrs to handlers for checks
421 MockHandlerStats * stats[7], ** cStat = stats;
422 // Hit banks is an important object for data source and handlers. It
423 // provides pre-allocated memory for fast event modifications -- imposing
424 // hits in event structure by source, and derived data by handlers.
425 HitBanks banks;
426 // Calibration handle is another important object providing calibration
427 // data, detector name mappings and so on.
428 MockCalibHandle ch;
429 // Create the pipeline
430 Pipeline p;
431
432 MockHandler<SADCHit> * h1, * h3;
433 MockHandler<APVHit> * h2, * h4;
434 // Insert some handlers in pipeline
435 { // - This one will accept all the SADC hits
436 auto hPtr = h1 = new MockHandler<SADCHit>( &ch );
437 p.push_back( hPtr );
438 *(cStat++) = hPtr;
439 }
440 { // - This one will accept all the APV hits
441 auto hPtr = h2 = new MockHandler<APVHit>( &ch );
442 p.push_back( hPtr );
443 *(cStat++) = hPtr;
444 }
445 { // - This one shall trait only NCAL1 (NCAL01 for run #2) detector
446 auto hPtr = h3 = new MockHandler<SADCHit>( &ch
447 , "NCAL == kin && 1 == number"
448 , true );
449 p.push_back( hPtr );
450 *(cStat++) = hPtr;
451 }
452 { // - This one shall trait only TRCK01 detector
453 auto hPtr = h4 = new MockHandler<APVHit>( &ch
454 , "TRCK == kin && 1 == number"
455 , true );
456 p.push_back( hPtr );
457 *(cStat++) = hPtr;
458 }
459 { // - This one will accept all the SADC hits (after removal)
460 auto hPtr = h1 = new MockHandler<SADCHit>( &ch );
461 p.push_back( hPtr );
462 *(cStat++) = hPtr;
463 }
464 #if 0
465 { // - This one will accept all the APV hits (after removal)
466 auto hPtr = h2 = new MockHandler<APVHit>( &ch );
467 p.push_back( hPtr );
468 *(cStat++) = hPtr;
469 }
470 #endif
471 *(cStat++) = nullptr;
472 // Create the source
473 MockEventSource src( banks, ch );
474 // Create reentrant event instance (owned by thread)
475 Event e(banks);
476 EXPECT_EQ( &banks.bankSADC, &e.sadcHits.pool() );
477 EXPECT_EQ( &banks.bankAPV, &e.apvHits.pool() );
478 // Process events from source with pipeline
479 while(src.read(e)) {
480 EXPECT_EQ( &banks.bankSADC, &e.sadcHits.pool() );
481 EXPECT_EQ( &banks.bankAPV, &e.apvHits.pool() );
482 p.process(e);
483 e.clear();
484 banks.clear();
485 }
486 // Check the results
487 // - at least one event is present on handler #1
488 ASSERT_FALSE( stats[0]->eventIDs.empty() );
489 // - 100 events were passed through all the handlers, and check total
490 // number of hits accepted by each of the handlers
491 const size_t expectedHitCounts[] = {
492 100 + 49 + 30 + 16, /* SADC overall */
493 60 + 17 + 225 + 50, /* APV overall */
494 100 + 49, /* NCAL1/NCAL01 */
495 60 + 17, /* TRCK01 */
496 (100+49)/2+30+16, /* SADC overall, after discriminating odd NCAL1/NCAL01 */
497 (60+17)/2+225+50, /* APV overall, after discriminating odd TRCK01 */
498 };
499 const size_t * expectedCountPtr = expectedHitCounts;
500 for( MockHandlerStats ** cStat = stats
501 ; *cStat
502 ; ++cStat, ++expectedCountPtr ) {
503 EXPECT_EQ( 100, (*cStat)->eventIDs.size() );
504 if( *expectedCountPtr ) {
505 EXPECT_EQ( *expectedCountPtr, (*cStat)->nHits )
506 << " at handler #"
507 << cStat - stats;
508 }
509 }
510 }
511
512 #endif
513
514 } // namespace na64dp::test
515 } // namespace na64dp
516