PODIO v00-16-03
An Event-Data-Model Toolkit for High Energy Physics Experiments
Loading...
Searching...
No Matches
unittest.cpp
Go to the documentation of this file.
1// STL
2#include <cstdint>
3#include <map>
4#include <stdexcept>
5#include <thread>
6#include <type_traits>
7#include <vector>
8
9#include "catch2/catch_test_macros.hpp"
10
11// podio specific includes
12#include "podio/EventStore.h"
16#include "podio/ROOTReader.h"
17#include "podio/podioVersion.h"
18#ifdef PODIO_WITH_SIO
19 #include "podio/SIOFrameReader.h"
21 #include "podio/SIOReader.h"
22#endif
23
24// Test data types
25#include "datamodel/EventInfoCollection.h"
26#include "datamodel/ExampleClusterCollection.h"
27#include "datamodel/ExampleForCyclicDependency1Collection.h"
28#include "datamodel/ExampleForCyclicDependency2Collection.h"
29#include "datamodel/ExampleHitCollection.h"
30#include "datamodel/ExampleWithOneRelationCollection.h"
31#include "datamodel/ExampleWithUserInitCollection.h"
32#include "datamodel/ExampleWithVectorMemberCollection.h"
33#include "datamodel/MutableExampleWithComponent.h"
35
36TEST_CASE("AutoDelete", "[basics][memory-management]") {
37 auto store = podio::EventStore();
38 auto hit1 = MutableEventInfo();
39 auto hit2 = MutableEventInfo();
40 auto hit3 = MutableEventInfo();
41 auto& coll = store.create<EventInfoCollection>("info");
42 coll.push_back(hit1);
43 hit3 = hit2;
44}
45
46TEST_CASE("Basics", "[basics][memory-management]") {
47 auto store = podio::EventStore();
48 // Adding
49 auto& collection = store.create<ExampleHitCollection>("name");
50 auto hit1 = collection.create(0xcaffeeULL, 0., 0., 0., 0.); // initialize w/ value
51 auto hit2 = collection.create(); // default initialize
52 hit2.energy(12.5);
53 // Retrieving
54 const ExampleHitCollection* coll2(nullptr);
55 REQUIRE(store.get("name", coll2));
56 const ExampleHitCollection* coll3(nullptr);
57 REQUIRE_FALSE(store.get("wrongName", coll3));
58}
59
60TEST_CASE("Assignment-operator ref count", "[basics][memory-management]") {
61 // Make sure that the assignment operator handles the reference count
62 // correctly. (Will trigger in an ASan build if it is not the case)
63 // See https://github.com/AIDASoft/podio/issues/200
64 std::map<int, ExampleHit> hitMap;
65 for (int i = 0; i < 10; ++i) {
66 auto hit = ExampleHit(0x42ULL, i, i, i, i);
67 hitMap[i] = hit;
68 }
69
70 // hits should still be valid here
71 for (int i = 0; i < 10; ++i) {
72 const auto hit = hitMap[i];
73 REQUIRE(hit.energy() == i);
74 }
75}
76
77TEST_CASE("Clearing", "[ASAN-FAIL][THREAD-FAIL][basics][memory-management]") {
78 bool success = true;
79 auto store = podio::EventStore();
80 auto& hits = store.create<ExampleHitCollection>("hits");
81 auto& clusters = store.create<ExampleClusterCollection>("clusters");
82 auto& oneRels = store.create<ExampleWithOneRelationCollection>("OneRelation");
83 auto nevents = unsigned(1000);
84 for (unsigned i = 0; i < nevents; ++i) {
85 hits.clear();
86 clusters.clear();
87 auto hit1 = hits.create();
88 auto hit2 = ExampleHit();
89 hit1.energy(double(i));
90 auto cluster = clusters.create();
91 cluster.addHits(hit1);
92 cluster.addHits(hit2);
93 hits.push_back(hit2);
94 auto oneRel = MutableExampleWithOneRelation();
95 oneRel.cluster(cluster);
96 oneRel.cluster(cluster);
97 oneRels.push_back(oneRel);
98 }
99 hits.clear();
100 if (hits.size() != 0) {
101 success = false;
102 }
103 REQUIRE(success);
104}
105
106TEST_CASE("Cloning", "[basics][memory-management]") {
107 bool success = true;
108 auto hit = MutableExampleHit();
109 hit.energy(30);
110 auto hit2 = hit.clone();
111 hit2.energy(20);
112 if (hit.energy() == hit2.energy()) {
113 success = false;
114 }
115 auto cluster = MutableExampleCluster();
116 cluster.addHits(hit);
117 auto cluster2 = cluster.clone();
118 cluster.addHits(hit2);
119 // check that the clone of a const object is mutable
120 auto const_hit = ExampleHit();
121 auto const_hit_clone = const_hit.clone();
122 const_hit_clone.energy(30);
123 REQUIRE(success);
124}
125
126TEST_CASE("Component", "[basics]") {
127 auto info = MutableExampleWithComponent();
128 info.component().data.x = 3;
129 REQUIRE(3 == info.component().data.x);
130}
131
132TEST_CASE("Cyclic", "[LEAK-FAIL][basics][relations][memory-management]") {
133 auto start = MutableExampleForCyclicDependency1();
134 auto isAvailable = start.ref().isAvailable();
135 REQUIRE_FALSE(isAvailable);
136 auto end = MutableExampleForCyclicDependency2();
137 start.ref(end);
138 isAvailable = start.ref().isAvailable();
139 REQUIRE(isAvailable);
140 end.ref(start);
141 REQUIRE(start == end.ref());
142 auto end_eq = start.ref();
143 auto start_eq = end_eq.ref();
144 REQUIRE(start == start_eq);
145 REQUIRE(start == start.ref().ref());
146}
147
148TEST_CASE("Invalid_refs", "[LEAK-FAIL][basics][relations]") {
149 auto store = podio::EventStore();
150 auto& hits = store.create<ExampleHitCollection>("hits");
151 auto hit1 = hits.create(0xcaffeeULL, 0., 0., 0., 0.);
152 auto hit2 = ExampleHit();
153 auto& clusters = store.create<ExampleClusterCollection>("clusters");
154 auto cluster = clusters.create();
155 cluster.addHits(hit1);
156 cluster.addHits(hit2);
157 REQUIRE_THROWS_AS(clusters.prepareForWrite(), std::runtime_error);
158}
159
160TEST_CASE("Looping", "[basics]") {
161 auto store = podio::EventStore();
162 auto& coll = store.create<ExampleHitCollection>("name");
163 auto hit1 = coll.create(0xbadULL, 0., 0., 0., 0.);
164 auto hit2 = coll.create(0xcaffeeULL, 1., 1., 1., 1.);
165 for (auto&& i : coll) {
166 i.energy(42); // make sure that we can indeed change the energy here for
167 // non-const collections
168 }
169 REQUIRE(hit1.energy() == 42);
170 REQUIRE(hit2.energy() == 42);
171
172 for (int i = 0, end = coll.size(); i != end; ++i) {
173 coll[i].energy(i); // reset it back to the original value
174 }
175
176 REQUIRE(coll[0].energy() == 0);
177 REQUIRE(coll[1].energy() == 1);
178
179 auto& constColl = store.get<ExampleHitCollection>("name");
180 int index = 0;
181 for (auto hit : constColl) {
182 auto energy = hit.energy();
183 REQUIRE(energy == index++);
184 }
185}
186
187TEST_CASE("Notebook", "[basics]") {
188 auto store = podio::EventStore();
189 auto& hits = store.create<ExampleHitCollection>("hits");
190 for (unsigned i = 0; i < 12; ++i) {
191 auto hit = hits.create(0xcaffeeULL, 0., 0., 0., double(i));
192 }
193
194 // Request only subset
195 auto energies = hits.energy(10);
196 REQUIRE(energies.size() == 10);
197 int index = 0;
198 for (auto energy : energies) {
199 REQUIRE(double(index) == energy);
200 ++index;
201 }
202
203 // Make sure there are no "invented" values
204 REQUIRE(hits.energy(24).size() == hits.size());
205 REQUIRE(hits.energy().size() == hits.size());
206}
207
208TEST_CASE("OneToOneRelations", "[basics][relations]") {
209 bool success = true;
210 auto cluster = ExampleCluster();
211 auto rel = MutableExampleWithOneRelation();
212 rel.cluster(cluster);
213 REQUIRE(success);
214}
215
216TEST_CASE("Podness", "[basics][code-gen]") {
217 // fail this already at compile time
218 STATIC_REQUIRE(std::is_standard_layout_v<ExampleClusterData>); // Generated data classes do not have standard layout
219 STATIC_REQUIRE(std::is_trivially_copyable_v<ExampleClusterData>); // Generated data classes are not trivially copyable
220 STATIC_REQUIRE(std::is_standard_layout_v<ExampleHitData>); // Generated data classes do not have standard layout
221 STATIC_REQUIRE(std::is_trivially_copyable_v<ExampleHitData>); // Generated data classes are not trivially copyable
222 STATIC_REQUIRE(std::is_standard_layout_v<ExampleWithOneRelationData>); // Generated data classes do not have standard
223 // layout
224 STATIC_REQUIRE(std::is_trivially_copyable_v<ExampleWithOneRelationData>); // Generated data classes are not trivially
225 // copyable
226}
227
228TEST_CASE("Referencing", "[basics][relations]") {
229 auto store = podio::EventStore();
230 auto& hits = store.create<ExampleHitCollection>("hits");
231 auto hit1 = hits.create(0x42ULL, 0., 0., 0., 0.);
232 auto hit2 = hits.create(0x42ULL, 1., 1., 1., 1.);
233 auto& clusters = store.create<ExampleClusterCollection>("clusters");
234 auto cluster = clusters.create();
235 cluster.addHits(hit1);
236 cluster.addHits(hit2);
237 int index = 0;
238 for (auto i = cluster.Hits_begin(), end = cluster.Hits_end(); i != end; ++i) {
239 REQUIRE(i->energy() == index);
240 ++index;
241 }
242}
243
244TEST_CASE("VariadicCreate", "[basics]") {
245 // Test that objects created via the variadic create template function handle relations correctly
246 auto store = podio::EventStore();
247 auto& clusters = store.create<ExampleClusterCollection>("clusters");
248
249 auto variadic_cluster = clusters.create(3.14f);
250 auto normal_cluster = clusters.create();
251 normal_cluster.energy(42);
252
253 variadic_cluster.addClusters(normal_cluster);
254 REQUIRE(variadic_cluster.Clusters_size() == 1);
255 REQUIRE(variadic_cluster.Clusters(0) == normal_cluster);
256}
257
258TEST_CASE("write_buffer", "[basics][io]") {
259 auto store = podio::EventStore();
260 auto& coll = store.create<ExampleHitCollection>("data");
261 auto hit1 = coll.create(0x42ULL, 0., 0., 0., 0.);
262 auto hit2 = coll.create(0x42ULL, 1., 1., 1., 1.);
263 auto& clusters = store.create<ExampleClusterCollection>("clusters");
264 auto cluster = clusters.create();
265 // add a few related objects to also exercise relation writing
266 cluster.addHits(hit1);
267 cluster.addHits(hit2);
268
269 REQUIRE_NOTHROW(clusters.prepareForWrite());
270 auto buffers = clusters.getBuffers();
271 REQUIRE(buffers.dataAsVector<ExampleClusterData>()->size() == clusters.size());
272
273 // a second call should not crash the whole thing and leave everything untouched
274 REQUIRE_NOTHROW(clusters.prepareForWrite());
275 REQUIRE(clusters.getBuffers().data == buffers.data);
276
277 auto& ref_coll = store.create<ExampleWithOneRelationCollection>("onerel");
278 auto withRef = ref_coll.create();
279 REQUIRE_NOTHROW(ref_coll.prepareForWrite());
280}
281
282TEST_CASE("thread-safe prepareForWrite", "[basics][multithread]") {
283 // setup a collection that we can then prepareForWrite from multiple threads
284 constexpr auto nElements = 100u;
285 ExampleHitCollection hits{};
287 for (size_t i = 0; i < nElements; ++i) {
288 hits.create(i, i * 0.5, i * 1.5, i * 2.5, 3.14);
289 userInts.push_back(i);
290 }
291
292 constexpr int nThreads = 10;
293 std::vector<std::thread> threads{};
294 threads.reserve(nThreads);
295
296 for (int i = 0; i < nThreads; ++i) {
297 threads.emplace_back([&hits, &userInts]() {
298 hits.prepareForWrite();
299 userInts.prepareForWrite();
300 });
301 }
302
303 for (auto& t : threads) {
304 t.join();
305 }
306
307 // NOTE: doing this on a single thread, because getBuffers is not threadsafe
308 auto buffers = hits.getBuffers();
309 auto* dataVec = buffers.dataAsVector<ExampleHitData>();
310 REQUIRE(dataVec->size() == nElements);
311
312 auto intBuffers = userInts.getBuffers();
313 auto* intVec = intBuffers.dataAsVector<std::uint64_t>();
314 REQUIRE(intVec->size() == nElements);
315
316 size_t i = 0;
317 for (const auto& h : *dataVec) {
318 REQUIRE(h.energy == 3.14);
319 REQUIRE(h.cellID == i);
320 REQUIRE(h.x == i * 0.5);
321 REQUIRE(h.y == i * 1.5);
322 REQUIRE(h.z == i * 2.5);
323
324 REQUIRE((*intVec)[i] == i);
325
326 i++;
327 }
328}
329
330/*
331TEST_CASE("Arrays") {
332 auto obj = ExampleWithArray();
333 obj.array({1,2,3});
334 REQUIRE( obj.array()[0] == 1);
335}
336*/
337
338TEST_CASE("Extracode", "[basics][code-gen]") {
339 auto ev = MutableEventInfo();
340 ev.setNumber(42);
341 REQUIRE(ev.getNumber() == 42);
342
343 int ia[3] = {1, 2, 3};
344 auto simple = SimpleStruct(ia);
345 REQUIRE(simple.x == 1);
346 REQUIRE(simple.y == 2);
347 REQUIRE(simple.z == 3);
348}
349
350TEST_CASE("AssociativeContainer", "[basics]") {
351 auto clu1 = ExampleCluster();
352 auto clu2 = ExampleCluster();
353 auto clu3 = ExampleCluster();
354 auto clu4 = ExampleCluster();
355 auto clu5 = ExampleCluster();
356
357 std::set<ExampleCluster> cSet;
358 cSet.insert(clu1);
359 cSet.insert(clu2);
360 cSet.insert(clu3);
361 cSet.insert(clu4);
362 cSet.insert(clu5);
363 cSet.insert(clu1);
364 cSet.insert(clu2);
365 cSet.insert(clu3);
366 cSet.insert(clu4);
367 cSet.insert(clu5);
368
369 REQUIRE(cSet.size() == 5);
370
371 std::map<ExampleCluster, int> cMap;
372 cMap[clu1] = 1;
373 cMap[clu2] = 2;
374 cMap[clu3] = 3;
375 cMap[clu4] = 4;
376 cMap[clu5] = 5;
377
378 REQUIRE(cMap[clu3] == 3);
379
380 cMap[clu3] = 42;
381
382 REQUIRE(cMap[clu3] == 42);
383}
384
385TEST_CASE("Equality", "[basics]") {
386 auto cluster = ExampleCluster();
387 auto rel = MutableExampleWithOneRelation();
388 rel.cluster(cluster);
389 auto returned_cluster = rel.cluster();
390 REQUIRE(cluster == returned_cluster);
391 REQUIRE(returned_cluster == cluster);
392}
393
394TEST_CASE("UserInitialization", "[basics][code-gen]") {
395 ExampleWithUserInitCollection coll;
396 // Default initialization values should work even through the create factory
397 auto elem = coll.create();
398 REQUIRE(elem.i16Val() == 42);
399 REQUIRE(elem.floats()[0] == 3.14f);
400 REQUIRE(elem.floats()[1] == 1.23f);
401 REQUIRE(elem.s().x == 10);
402 REQUIRE(elem.s().y == 11);
403 REQUIRE(elem.d() == 9.876e5);
404 REQUIRE(elem.comp().i == 42);
405 REQUIRE(elem.comp().arr[0] == 1.2);
406 REQUIRE(elem.comp().arr[1] == 3.4);
407
408 // And obviously when initialized directly
409 auto ex = ExampleWithUserInit{};
410 REQUIRE(ex.i16Val() == 42);
411 REQUIRE(ex.floats()[0] == 3.14f);
412 REQUIRE(ex.floats()[1] == 1.23f);
413 REQUIRE(ex.s().x == 10);
414 REQUIRE(ex.s().y == 11);
415 REQUIRE(ex.d() == 9.876e5);
416 REQUIRE(ex.comp().i == 42);
417 REQUIRE(ex.comp().arr[0] == 1.2);
418 REQUIRE(ex.comp().arr[1] == 3.4);
419}
420
421TEST_CASE("NonPresentCollection", "[basics][event-store]") {
422 auto store = podio::EventStore();
423 REQUIRE_THROWS_AS(store.get<ExampleHitCollection>("NonPresentCollection"), std::runtime_error);
424}
425
426TEST_CASE("const correct indexed access to const collections", "[const-correctness]") {
427 STATIC_REQUIRE(std::is_same_v<decltype(std::declval<const ExampleClusterCollection>()[0]),
428 ExampleCluster>); // const collections should only have indexed access to mutable
429 // objects
430 STATIC_REQUIRE(std::is_same_v<decltype(std::declval<const ExampleClusterCollection>().at(0)),
431 ExampleCluster>); // const collections should only have indexed access to mutable
432 // objects
433}
434
435TEST_CASE("const correct indexed access to collections", "[const-correctness]") {
436 auto store = podio::EventStore();
437 auto& collection = store.create<ExampleHitCollection>("irrelevant name");
438
439 STATIC_REQUIRE(std::is_same_v<decltype(collection), ExampleHitCollection&>); // collection created by store should not
440 // be const
441
442 STATIC_REQUIRE(std::is_same_v<decltype(collection[0]), MutableExampleHit>); // non-const collections should have
443 // indexed access to mutable objects
444
445 STATIC_REQUIRE(std::is_same_v<decltype(std::declval<ExampleClusterCollection>()[0]),
446 MutableExampleCluster>); // collections should have indexed access to mutable objects
447
448 STATIC_REQUIRE(std::is_same_v<decltype(std::declval<ExampleClusterCollection>().at(0)),
449 MutableExampleCluster>); // collections should have indexed access to mutable objects
450}
451
452TEST_CASE("const correct iterators on const collections", "[const-correctness]") {
453 const auto collection = ExampleHitCollection();
454 // this essentially checks the whole "chain" from begin() / end() through
455 // iterator operators
456 for (auto hit : collection) {
457 STATIC_REQUIRE(std::is_same_v<decltype(hit), ExampleHit>); // const collection iterators should only return
458 // immutable objects
459 }
460
461 // but we can exercise it in a detailed fashion as well to make it easier to
462 // spot where things fail, should they fail
463 STATIC_REQUIRE(std::is_same_v<decltype(std::declval<const ExampleHitCollection>().begin()),
464 ExampleHitCollectionIterator>); // const collection begin() should return a
465 // CollectionIterator
466
467 STATIC_REQUIRE(std::is_same_v<decltype(std::declval<const ExampleHitCollection>().end()),
468 ExampleHitCollectionIterator>); // const collection end() should return a
469 // CollectionIterator
470
471 STATIC_REQUIRE(std::is_same_v<decltype(*std::declval<const ExampleHitCollection>().begin()),
472 ExampleHit>); // CollectionIterator should only give access to immutable objects
473
474 STATIC_REQUIRE(std::is_same_v<decltype(std::declval<ExampleHitCollectionIterator>().operator->()),
475 ExampleHit*>); // CollectionIterator should only give access to immutable objects
476}
477
478TEST_CASE("const correct iterators on collections", "[const-correctness]") {
479 auto collection = ExampleClusterCollection();
480 for (auto cluster : collection) {
481 STATIC_REQUIRE(std::is_same_v<decltype(cluster), MutableExampleCluster>); // collection iterators should return
482 // mutable objects
483 cluster.energy(42); // this will necessarily also compile
484 }
485
486 // check the individual steps again from above, to see where things fail if they fail
487 STATIC_REQUIRE(std::is_same_v<decltype(std::declval<ExampleClusterCollection>().end()),
488 ExampleClusterMutableCollectionIterator>); // non const collection end() should return a
489 // MutableCollectionIterator
490
491 STATIC_REQUIRE(std::is_same_v<decltype(std::declval<ExampleClusterCollection>().end()),
492 ExampleClusterMutableCollectionIterator>); // non const collection end() should return a
493 // MutableCollectionIterator
494
495 STATIC_REQUIRE(std::is_same_v<decltype(std::declval<ExampleClusterCollection>().end()),
496 ExampleClusterMutableCollectionIterator>); // collection end() should return a
497 // MutableCollectionIterator
498
499 STATIC_REQUIRE(std::is_same_v<decltype(*std::declval<ExampleClusterCollection>().begin()),
500 MutableExampleCluster>); // MutableCollectionIterator should give access to mutable
501 // objects
502
503 STATIC_REQUIRE(std::is_same_v<decltype(std::declval<ExampleClusterMutableCollectionIterator>().operator->()),
504 MutableExampleCluster*>); // CollectionIterator should only give access to mutable
505 // objects
506}
507
508TEST_CASE("Subset collection basics", "[subset-colls]") {
509 auto clusterRefs = ExampleClusterCollection();
510 clusterRefs.setSubsetCollection();
511
512 // The following will always be true
513 REQUIRE(clusterRefs.isSubsetCollection());
514 const auto refCollBuffers = clusterRefs.getBuffers();
515 REQUIRE(refCollBuffers.data == nullptr);
516 REQUIRE(refCollBuffers.vectorMembers->empty());
517 REQUIRE(refCollBuffers.references->size() == 1u);
518}
519
520TEST_CASE("Subset collection can handle subsets", "[subset-colls]") {
521 // Can only collect things that already live in a different colection
522 auto clusters = ExampleClusterCollection();
523 auto cluster = clusters.create();
524
525 auto clusterRefs = ExampleClusterCollection();
526 clusterRefs.setSubsetCollection();
527 clusterRefs.push_back(cluster);
528
529 auto clusterRef = clusterRefs[0];
530 STATIC_REQUIRE(std::is_same_v<decltype(clusterRef), decltype(cluster)>); // Elements that can be obtained from a
531 // collection and a subset collection should
532 // have the same type
533
534 REQUIRE(clusterRef == cluster);
535
536 // These are "true" subsets, so changes should propagate
537 cluster.energy(42);
538 REQUIRE(clusterRef.energy() == 42);
539 // Also in the other directon
540 clusterRef.energy(-42);
541 REQUIRE(cluster.energy() == -42);
542
543 clusters.setID(42);
544 for (auto c : clusters) {
545 REQUIRE(c.getObjectID().collectionID == 42);
546 }
547
548 // Setting the ID on a subset collection should not change the IDs of the
549 // reference objects as otherwise we cannot use them in I/O
550 clusterRefs.setID(314);
551 REQUIRE(clusterRefs.getID() == 314);
552 for (auto c : clusterRefs) {
553 REQUIRE(c.getObjectID().collectionID == 42);
554 }
555}
556
557TEST_CASE("Collection iterators work with subset collections", "[LEAK-FAIL][subset-colls]") {
558 auto hits = ExampleHitCollection();
559 auto hit1 = hits.create(0x42ULL, 0., 0., 0., 0.);
560 auto hit2 = hits.create(0x42ULL, 1., 1., 1., 1.);
561
562 auto hitRefs = ExampleHitCollection();
563 hitRefs.setSubsetCollection();
564 for (const auto h : hits) {
565 hitRefs.push_back(h);
566 }
567
568 // index-based looping / access
569 for (size_t i = 0; i < hitRefs.size(); ++i) {
570 REQUIRE(hitRefs[i].energy() == i);
571 }
572
573 // range-based for loop
574 int index = 0;
575 for (const auto h : hitRefs) {
576 REQUIRE(h.energy() == index++);
577 }
578}
579
580TEST_CASE("Canont convert a normal collection into a subset collection", "[subset-colls]") {
581 auto clusterRefs = ExampleClusterCollection();
582 auto cluster = clusterRefs.create();
583
584 REQUIRE_THROWS_AS(clusterRefs.setSubsetCollection(), std::logic_error);
585}
586
587TEST_CASE("Cannot convert a subset collection into a normal collection", "[subset-colls]") {
588 auto clusterRefs = ExampleClusterCollection();
589 clusterRefs.setSubsetCollection();
590
591 auto clusters = ExampleClusterCollection();
592 auto cluster = clusters.create();
593 clusterRefs.push_back(cluster);
594
595 REQUIRE_THROWS_AS(clusterRefs.setSubsetCollection(false), std::logic_error);
596}
597
598TEST_CASE("Subset collection only handles tracked objects", "[subset-colls]") {
599 auto clusterRefs = ExampleClusterCollection();
600 clusterRefs.setSubsetCollection();
601 auto cluster = ExampleCluster();
602
603 REQUIRE_THROWS_AS(clusterRefs.push_back(cluster), std::invalid_argument);
604 REQUIRE_THROWS_AS(clusterRefs.create(), std::logic_error);
605}
606
607// Helper functionality to keep the tests below with a common setup a bit shorter
608auto createCollections(const size_t nElements = 3u) {
609 auto colls = std::make_tuple(ExampleHitCollection(), ExampleClusterCollection(), ExampleWithVectorMemberCollection(),
611 auto& [hitColl, clusterColl, vecMemColl, userDataColl] = colls;
612
613 for (auto i = 0u; i < nElements; ++i) {
614 auto hit = hitColl.create();
615 hit.cellID(i);
616 hit.energy(100.f * i);
617 auto cluster = clusterColl.create();
618 // create a few relations as well
619 cluster.addHits(hit);
620
621 auto vecMem = vecMemColl.create();
622 vecMem.addcount(i);
623 vecMem.addcount(42 + i);
624
625 userDataColl.push_back(3.14f * i);
626 }
627
628 vecMemColl.prepareForWrite();
629 auto buffers = vecMemColl.getBuffers();
630 auto vecBuffers = buffers.vectorMembers;
631 auto thisVec = (*vecBuffers)[0].second;
632
633 // const auto floatVec = podio::CollectionWriteBuffers::asVector<float>(thisVec);
634 const auto floatVec2 = podio::CollectionReadBuffers::asVector<float>(thisVec);
635
636 // std::cout << floatVec->size() << '\n';
637 std::cout << "** " << floatVec2->size() << " vs " << vecMemColl.size() << '\n';
638
639 return colls;
640}
641
642// Helper functionality to keep the tests below with a common setup a bit shorter
643void checkCollections(/*const*/ ExampleHitCollection& hits, /*const*/ ExampleClusterCollection& clusters,
644 /*const*/ ExampleWithVectorMemberCollection& vectors,
645 /*const*/ podio::UserDataCollection<float>& userData, const size_t nElements = 3u) {
646 // Basics
647 REQUIRE(hits.size() == nElements);
648 REQUIRE(clusters.size() == nElements);
649 REQUIRE(vectors.size() == nElements);
650 REQUIRE(userData.size() == nElements);
651
652 int i = 0;
653 for (auto cluster : clusters) {
654 REQUIRE(cluster.Hits(0) == hits[i++]);
655 }
656
657 i = 0;
658 for (const auto vec : vectors) {
659 const auto counts = vec.count();
660 REQUIRE(counts.size() == 2);
661 REQUIRE(counts[0] == i);
662 REQUIRE(counts[1] == i + 42);
663 i++;
664 }
665
666 i = 0;
667 for (const auto v : userData) {
668 REQUIRE(v == 3.14f * i++);
669 }
670
671 // Also check that the buffers we get are valid and have the expected contents
672 hits.prepareForWrite();
673 auto hitBuffers = hits.getBuffers();
674 if (!hits.isSubsetCollection()) {
675 auto* hitPODBuffers = hitBuffers.dataAsVector<ExampleHitData>();
676 REQUIRE(hitPODBuffers->size() == nElements);
677 for (size_t iHit = 0; iHit < nElements; ++iHit) {
678 const auto& hitPOD = (*hitPODBuffers)[iHit];
679 REQUIRE(hitPOD.cellID == static_cast<unsigned long long>(iHit));
680 REQUIRE(hitPOD.energy == 100.f * iHit);
681 }
682 }
683
684 clusters.prepareForWrite();
685 auto clusterBuffers = clusters.getBuffers();
686 auto* clusterRelationBuffers = clusterBuffers.references;
687
688 if (!clusters.isSubsetCollection()) {
689 REQUIRE(clusterRelationBuffers->size() == 2); // We have two relations, to hits and to clusters
690 const auto& hitRelationBuffer = (*clusterRelationBuffers)[0]; // Index is an implementation detail
691 REQUIRE(hitRelationBuffer->size() == nElements * 1); // Each cluster has one hit in these tests
692 for (size_t iHit = 0; iHit < nElements * 1; ++iHit) {
693 const auto& hitID = (*hitRelationBuffer)[iHit];
694 REQUIRE(hitID.index == (int)iHit);
695 REQUIRE(static_cast<unsigned>(hitID.collectionID) == hits.getID());
696 }
697 }
698
699 vectors.prepareForWrite();
700 auto vecMemBuffers = vectors.getBuffers();
701 if (!vectors.isSubsetCollection()) {
702 auto* vecMemVecBuffers = vecMemBuffers.vectorMembers;
703 REQUIRE(vecMemVecBuffers->size() == 1); // Only one vector member here
704 // Now we are really descending into implementation details
705 const auto* countBuffer = *static_cast<std::vector<int>**>((*vecMemVecBuffers)[0].second);
706 REQUIRE(countBuffer->size() == nElements * 2);
707 for (int iC = 0; iC < (int)nElements; ++iC) {
708 REQUIRE((*countBuffer)[iC * 2] == iC);
709 REQUIRE((*countBuffer)[iC * 2 + 1] == 42 + iC);
710 }
711 }
712}
713
714template <typename>
715struct TD;
716
717TEST_CASE("Move-only collections", "[collections][move-semantics]") {
718 // Setup a few collections that will be used throughout below
719 auto [hitColl, clusterColl, vecMemColl, userDataColl] = createCollections();
720
721 // Hopefully redundant check for setup
722 checkCollections(hitColl, clusterColl, vecMemColl, userDataColl);
723
724 SECTION("Move constructor") {
725 // Move-construct collections and make sure the size is as expected
726 auto newHits = std::move(hitColl);
727 auto newClusters = std::move(clusterColl);
728 auto newVecMems = std::move(vecMemColl);
729 auto newUserData = std::move(userDataColl);
730
731 checkCollections(newHits, newClusters, newVecMems, newUserData);
732 }
733
734 SECTION("Move assignment") {
735 // Move assign collections and make sure everything is as expected
736 auto newHits = ExampleHitCollection();
737 newHits = std::move(hitColl);
738
739 auto newClusters = ExampleClusterCollection();
740 newClusters = std::move(clusterColl);
741
742 auto newVecMems = ExampleWithVectorMemberCollection();
743 newVecMems = std::move(vecMemColl);
744
745 auto newUserData = podio::UserDataCollection<float>();
746 newUserData = std::move(userDataColl);
747
748 checkCollections(newHits, newClusters, newVecMems, newUserData);
749 }
750
751 SECTION("Prepared collections can be move constructed") {
752 hitColl.prepareForWrite();
753 auto newHits = std::move(hitColl);
754
755 clusterColl.prepareForWrite();
756 auto newClusters = std::move(clusterColl);
757
758 vecMemColl.prepareForWrite();
759 auto buffers = vecMemColl.getBuffers();
760 auto vecBuffers = buffers.vectorMembers;
761 auto thisVec = (*vecBuffers)[0].second;
762
763 const auto floatVec = podio::CollectionWriteBuffers::asVector<float>(thisVec);
764 const auto floatVec2 = podio::CollectionReadBuffers::asVector<float>(thisVec);
765
766 std::cout << floatVec->size() << '\n';
767 std::cout << floatVec2->size() << '\n';
768
769 // auto vecBuffers = buffers.vectorMembers;
770 // const auto vecBuffer = podio::CollectionWriteBuffers::asVector<float>((*vecBuffers)[0].second);
771 // TD<decltype(vecBuffer)> td;
772 // REQUIRE(vecBuffer->size() == 2);
773 auto newVecMems = std::move(vecMemColl);
774
775 userDataColl.prepareForWrite();
776 auto newUserData = std::move(userDataColl);
777
778 checkCollections(newHits, newClusters, newVecMems, newUserData);
779 }
780
781 SECTION("Moved collections can be prepared") {
782 }
783
784 SECTION("Prepared collections can be move assigned") {
785 hitColl.prepareForWrite();
786 clusterColl.prepareForWrite();
787 vecMemColl.prepareForWrite();
788
789 auto newHits = ExampleHitCollection();
790 newHits = std::move(hitColl);
791
792 auto newClusters = ExampleClusterCollection();
793 newClusters = std::move(clusterColl);
794
795 auto newVecMems = ExampleWithVectorMemberCollection();
796 newVecMems = std::move(vecMemColl);
797
798 auto newUserData = podio::UserDataCollection<float>();
799 newUserData = std::move(userDataColl);
800
801 checkCollections(newHits, newClusters, newVecMems, newUserData);
802 }
803
804 SECTION("Subset collections can be moved") {
805 // NOTE: Does not apply to UserDataCollections!
806 auto subsetHits = ExampleHitCollection();
807 subsetHits.setSubsetCollection();
808 for (auto hit : hitColl) {
809 subsetHits.push_back(hit);
810 }
811 checkCollections(subsetHits, clusterColl, vecMemColl, userDataColl);
812
813 auto newSubsetHits = std::move(subsetHits);
814 REQUIRE(newSubsetHits.isSubsetCollection());
815 checkCollections(newSubsetHits, clusterColl, vecMemColl, userDataColl);
816
817 auto subsetClusters = ExampleClusterCollection();
818 subsetClusters.setSubsetCollection();
819 for (auto cluster : clusterColl) {
820 subsetClusters.push_back(cluster);
821 }
822 checkCollections(newSubsetHits, subsetClusters, vecMemColl, userDataColl);
823
824 // Test move-assignment here as well
825 auto newSubsetClusters = ExampleClusterCollection();
826 newSubsetClusters = std::move(subsetClusters);
827 REQUIRE(newSubsetClusters.isSubsetCollection());
828 checkCollections(newSubsetHits, newSubsetClusters, vecMemColl, userDataColl);
829
830 auto subsetVecs = ExampleWithVectorMemberCollection();
831 subsetVecs.setSubsetCollection();
832 for (auto vec : vecMemColl) {
833 subsetVecs.push_back(vec);
834 }
835 checkCollections(newSubsetHits, newSubsetClusters, subsetVecs, userDataColl);
836
837 auto newSubsetVecs = std::move(subsetVecs);
838 REQUIRE(newSubsetVecs.isSubsetCollection());
839 checkCollections(hitColl, clusterColl, newSubsetVecs, userDataColl);
840 }
841}
842
843TEST_CASE("Version tests", "[versioning]") {
844 using namespace podio::version;
845 // all of these comparisons should be possible at compile time -> STATIC_REQUIRE
846
847 // major version checks
848 constexpr Version ver_1{1};
849 constexpr Version ver_2{2};
850 constexpr Version ver_1_1{1, 1};
851 constexpr Version ver_2_1{2, 1};
852 constexpr Version ver_1_1_1{1, 1, 1};
853 constexpr Version ver_1_0_2{1, 0, 2};
854 constexpr Version ver_2_0_2{2, 0, 2};
855
856 SECTION("Equality") {
857 STATIC_REQUIRE(ver_1 == Version{1, 0, 0});
858 STATIC_REQUIRE(ver_1 != ver_2);
859 STATIC_REQUIRE(ver_1_1_1 == Version{1, 1, 1});
860 STATIC_REQUIRE(ver_2_1 != ver_1_1);
861 STATIC_REQUIRE(ver_1_0_2 != ver_2_0_2);
862 }
863
864 SECTION("Major version") {
865 STATIC_REQUIRE(ver_1 < ver_2);
866 STATIC_REQUIRE(Version{3} > ver_2);
867 }
868
869 SECTION("Minor version") {
870 STATIC_REQUIRE(ver_1 < ver_1_1);
871 STATIC_REQUIRE(ver_2_1 > ver_2);
872 STATIC_REQUIRE(ver_1_1 < ver_2);
873 }
874
875 SECTION("Patch version") {
876 STATIC_REQUIRE(ver_1 < ver_1_0_2);
877 STATIC_REQUIRE(ver_1 < ver_1_1_1);
878 STATIC_REQUIRE(ver_1_1_1 > ver_1_1);
879 STATIC_REQUIRE(ver_2_0_2 < ver_2_1);
880 }
881}
882
883TEST_CASE("Preprocessor version tests", "[versioning]") {
884 SECTION("Basic functionality") {
885 using namespace podio::version;
886 // Check that preprocessor comparisons work by actually invoking the
887 // preprocessor
888#if PODIO_BUILD_VERSION == PODIO_VERSION(podio_VERSION_MAJOR, podio_VERSION_MINOR, podio_VERSION_PATCH)
889 STATIC_REQUIRE(true);
890#else
891 STATIC_REQUIRE(false);
892#endif
893
894 // Make sure that we can actually decode 64 bit versions
895 STATIC_REQUIRE(decode_version(PODIO_BUILD_VERSION) == build_version);
896
897 STATIC_REQUIRE(PODIO_MAJOR_VERSION(PODIO_BUILD_VERSION) == build_version.major);
898 STATIC_REQUIRE(PODIO_MINOR_VERSION(PODIO_BUILD_VERSION) == build_version.minor);
899 STATIC_REQUIRE(PODIO_PATCH_VERSION(PODIO_BUILD_VERSION) == build_version.patch);
900
901 // Make a few checks where other versions are "maxed out"
902 STATIC_REQUIRE(PODIO_MAJOR_VERSION(PODIO_VERSION(10000, 65535, 65535)) == 10000);
903 STATIC_REQUIRE(PODIO_MINOR_VERSION(PODIO_VERSION(65535, 20000, 65535)) == 20000);
904 STATIC_REQUIRE(PODIO_PATCH_VERSION(PODIO_VERSION(65535, 65535, 30000)) == 30000);
905 }
906
907 SECTION("Comparing") {
908 // Using some large numbers here to check what happens if we start to
909 // actually use the 16 available bits
910 // patch version
911 STATIC_REQUIRE(PODIO_VERSION(10000, 20000, 39999) < PODIO_VERSION(10000, 20000, 40000));
912
913 // minor version
914 STATIC_REQUIRE(PODIO_VERSION(10000, 30000, 33333) > PODIO_VERSION(10000, 29999, 33333));
915 STATIC_REQUIRE(PODIO_VERSION(10000, 30000, 33333) < PODIO_VERSION(10000, 30001, 44444));
916
917 // major version
918 STATIC_REQUIRE(PODIO_VERSION(20000, 40000, 0) < PODIO_VERSION(20001, 40000, 0));
919 STATIC_REQUIRE(PODIO_VERSION(20000, 40000, 10000) < PODIO_VERSION(20001, 30000, 0));
920 STATIC_REQUIRE(PODIO_VERSION(20001, 40000, 10000) > PODIO_VERSION(20000, 40000, 20000));
921 STATIC_REQUIRE(PODIO_VERSION(20000, 40000, 10000) > PODIO_VERSION(19999, 50000, 30000));
922 }
923}
924
925TEST_CASE("GenericParameters", "[generic-parameters]") {
926 // Check that GenericParameters work as intended
927 auto gp = podio::GenericParameters{};
928
929 gp.setValue("anInt", 42);
930 REQUIRE(gp.getValue<int>("anInt") == 42);
931 // Make sure that resetting a value with the same key works
932 gp.setValue("anInt", -42);
933 REQUIRE(gp.getValue<int>("anInt") == -42);
934
935 // Make sure that passing a string literal is converted to a string on the fly
936 gp.setValue("aString", "const char initialized");
937 REQUIRE(gp.getValue<std::string>("aString") == "const char initialized");
938
939 gp.setValue("aStringVec", {"init", "from", "const", "chars"});
940 const auto& stringVec = gp.getValue<std::vector<std::string>>("aStringVec");
941 REQUIRE(stringVec.size() == 4);
942 REQUIRE(stringVec[0] == "init");
943 REQUIRE(stringVec[3] == "chars");
944
945 // Check that storing double values works
946 gp.setValue("double", 1.234);
947 gp.setValue("manyDoubles", {1.23, 4.56, 7.89});
948 REQUIRE(gp.getValue<double>("double") == 1.234);
949 const auto& storedDoubles = gp.getValue<std::vector<double>>("manyDoubles");
950 REQUIRE(storedDoubles.size() == 3);
951 REQUIRE(storedDoubles[0] == 1.23);
952 REQUIRE(storedDoubles[1] == 4.56);
953 REQUIRE(storedDoubles[2] == 7.89);
954
955 // Check that passing an initializer_list creates the vector on the fly
956 gp.setValue("manyInts", {1, 2, 3, 4});
957 const auto& ints = gp.getValue<std::vector<int>>("manyInts");
958 REQUIRE(ints.size() == 4);
959 for (int i = 0; i < 4; ++i) {
960 REQUIRE(ints[i] == i + 1);
961 }
962
963 auto floats = std::vector<float>{3.14f, 2.718f};
964 // This stores a copy of the current value
965 gp.setValue("someFloats", floats);
966 // Hence, modifying the original vector will not be reflected
967 floats.push_back(42.f);
968 REQUIRE(floats.size() == 3);
969
970 const auto& storedFloats = gp.getValue<std::vector<float>>("someFloats");
971 REQUIRE(storedFloats.size() == 2);
972 REQUIRE(storedFloats[0] == 3.14f);
973 REQUIRE(storedFloats[1] == 2.718f);
974
975 // We can at this point reset this to a single value with the same key even if
976 // it has been a vector before
977 gp.setValue("someFloats", 12.34f);
978 REQUIRE(gp.getValue<float>("someFloats") == 12.34f);
979
980 // Missing values return the default initialized ones
981 REQUIRE(gp.getValue<int>("MissingValue") == int{});
982 REQUIRE(gp.getValue<float>("MissingValue") == float{});
983 REQUIRE(gp.getValue<std::string>("MissingValue") == std::string{}); // NOLINT(readability-container-size-empty): We
984 // want the explicit comparison here
985
986 // Same for vectors
987 REQUIRE(gp.getValue<std::vector<int>>("MissingValue").empty());
988 REQUIRE(gp.getValue<std::vector<float>>("MissingValue").empty());
989 REQUIRE(gp.getValue<std::vector<std::string>>("MissingValue").empty());
990}
991
992TEST_CASE("GenericParameters constructors", "[generic-parameters]") {
993 // Tests for making sure that generic parameters can be moved / copied correctly
994 auto originalParams = podio::GenericParameters{};
995 originalParams.setValue("int", 42);
996 originalParams.setValue("ints", {1, 2});
997 originalParams.setValue("float", 3.14f);
998 originalParams.setValue("double", 2 * 3.14);
999 originalParams.setValue("strings", {"one", "two", "three"});
1000
1001 SECTION("Copy constructor") {
1002 auto copiedParams = originalParams;
1003 REQUIRE(copiedParams.getValue<int>("int") == 42);
1004 REQUIRE(copiedParams.getValue<std::vector<int>>("ints")[1] == 2);
1005 REQUIRE(copiedParams.getValue<float>("float") == 3.14f);
1006 REQUIRE(copiedParams.getValue<double>("double") == 2 * 3.14);
1007 REQUIRE(copiedParams.getValue<std::vector<std::string>>("strings")[0] == "one");
1008
1009 // Make sure these are truly independent copies now
1010 copiedParams.setValue("anotherDouble", 1.2345);
1011 REQUIRE(originalParams.getValue<double>("anotherDouble") == double{});
1012 }
1013
1014 SECTION("Move constructor") {
1015 auto copiedParams = std::move(originalParams);
1016 REQUIRE(copiedParams.getValue<int>("int") == 42);
1017 REQUIRE(copiedParams.getValue<std::vector<int>>("ints")[1] == 2);
1018 REQUIRE(copiedParams.getValue<float>("float") == 3.14f);
1019 REQUIRE(copiedParams.getValue<double>("double") == 2 * 3.14);
1020 REQUIRE(copiedParams.getValue<std::vector<std::string>>("strings")[0] == "one");
1021 }
1022
1023 SECTION("Move assignment") {
1024 auto copiedParams = podio::GenericParameters{};
1025 copiedParams = std::move(originalParams);
1026 REQUIRE(copiedParams.getValue<int>("int") == 42);
1027 REQUIRE(copiedParams.getValue<std::vector<int>>("ints")[1] == 2);
1028 REQUIRE(copiedParams.getValue<float>("float") == 3.14f);
1029 REQUIRE(copiedParams.getValue<double>("double") == 2 * 3.14);
1030 REQUIRE(copiedParams.getValue<std::vector<std::string>>("strings")[0] == "one");
1031 }
1032}
1033
1034// Helper alias template "macro" to get the return type of calling
1035// GenericParameters::getValue with the desired template type
1036template <typename T>
1037using GPGetValue = decltype(std::declval<podio::GenericParameters>().getValue<T>(std::declval<std::string>()));
1038
1039TEST_CASE("GenericParameters return types", "[generic-parameters][static-checks]") {
1040 // Tests for checking that the getValue returns return by value resp. by const
1041 // reference as expected
1042 STATIC_REQUIRE(std::is_same_v<GPGetValue<int>, int>); // int and float are returned by value
1043 STATIC_REQUIRE(std::is_same_v<GPGetValue<std::vector<int>>, const std::vector<int>&>); // vectors are const
1044 // references
1045
1046 STATIC_REQUIRE(std::is_same_v<GPGetValue<std::string>, const std::string&>); // std::strings are returned by const
1047 // reference as well
1048 STATIC_REQUIRE(std::is_same_v<GPGetValue<std::vector<std::string>>, const std::vector<std::string>&>); // as are
1049 // vectors of
1050 // strings
1051}
1052
1053TEST_CASE("Missing files (ROOT readers)", "[basics]") {
1054 auto root_reader = podio::ROOTReader();
1055 REQUIRE_THROWS_AS(root_reader.openFile("NonExistentFile.root"), std::runtime_error);
1056
1057 auto root_legacy_reader = podio::ROOTLegacyReader();
1058 REQUIRE_THROWS_AS(root_legacy_reader.openFile("NonExistentFile.root"), std::runtime_error);
1059
1060 auto root_frame_reader = podio::ROOTFrameReader();
1061 REQUIRE_THROWS_AS(root_frame_reader.openFile("NonExistentFile.root"), std::runtime_error);
1062}
1063
1064#ifdef PODIO_WITH_SIO
1065TEST_CASE("Missing files (SIO readers)", "[basics]") {
1066 auto sio_reader = podio::SIOReader();
1067 REQUIRE_THROWS_AS(sio_reader.openFile("NonExistentFile.sio"), std::runtime_error);
1068
1069 auto sio_legacy_reader = podio::SIOLegacyReader();
1070 REQUIRE_THROWS_AS(sio_legacy_reader.openFile("NonExistentFile.sio"), std::runtime_error);
1071
1072 auto sio_frame_reader = podio::SIOFrameReader();
1073 REQUIRE_THROWS_AS(sio_frame_reader.openFile("NonExistentFile.root"), std::runtime_error);
1074}
1075#endif
1076
1077#ifdef PODIO_JSON_OUTPUT
1078 #include "nlohmann/json.hpp"
1079
1080TEST_CASE("JSON", "[json]") {
1081 const auto& [hitColl, clusterColl, vecMemColl, userDataColl] = createCollections();
1082 const nlohmann::json json{
1083 {"clusters", clusterColl}, {"hits", hitColl}, {"vectors", vecMemColl}, {"userData", userDataColl}};
1084
1085 REQUIRE(json["clusters"].size() == 3);
1086
1087 int i = 0;
1088 for (const auto& clu : json["clusters"]) {
1089 REQUIRE(clu["Hits"][0]["index"] == i++);
1090 }
1091
1092 i = 0;
1093 REQUIRE(json["hits"].size() == 3);
1094 for (const auto& hit : json["hits"]) {
1095 REQUIRE(hit["cellID"] == i);
1096 REQUIRE(hit["energy"] == 100.f * i);
1097 i++;
1098 }
1099
1100 i = 0;
1101 REQUIRE(json["vectors"].size() == 3);
1102 for (const auto& vec : json["vectors"]) {
1103 REQUIRE(vec["count"].size() == 2);
1104 REQUIRE(vec["count"][0] == i);
1105 REQUIRE(vec["count"][1] == i + 42);
1106 i++;
1107 }
1108
1109 REQUIRE(json["userData"].size() == 3);
1110 for (size_t j = 0; j < 3; ++j) {
1111 REQUIRE(json["userData"][j] == 3.14f * j);
1112 }
1113}
1114#endif
void setValue(const std::string &key, T value)
Store (a copy of) the passed value under the given key.
void push_back(const BasicType &value)
size_t size() const override
number of elements in the collection
#define PODIO_MAJOR_VERSION(v)
Get the major version from a preprocessor defined version.
#define PODIO_VERSION(major, minor, patch)
Define a version to be used in podio.
#define PODIO_PATCH_VERSION(v)
Get the patch version from a preprocessor defined version.
#define PODIO_MINOR_VERSION(v)
Get the minor version from a preprocessor defined version.
#define PODIO_BUILD_VERSION
The encoded version with which podio has been built.
Definition: unittest.cpp:715
TEST_CASE("AutoDelete", "[basics][memory-management]")
Definition: unittest.cpp:36
auto createCollections(const size_t nElements=3u)
Definition: unittest.cpp:608
decltype(std::declval< podio::GenericParameters >().getValue< T >(std::declval< std::string >())) GPGetValue
Definition: unittest.cpp:1037
void checkCollections(ExampleHitCollection &hits, ExampleClusterCollection &clusters, ExampleWithVectorMemberCollection &vectors, podio::UserDataCollection< float > &userData, const size_t nElements=3u)
Definition: unittest.cpp:643