Belle II Software development
relations_internal.cc
1/**************************************************************************
2 * basf2 (Belle II Analysis Software Framework) *
3 * Author: The Belle II Collaboration *
4 * *
5 * See git log for contributors and copyright holders. *
6 * This file is licensed under LGPL-3.0, see LICENSE.md. *
7 **************************************************************************/
8#include <framework/datastore/RelationIndex.h>
9#include <framework/dataobjects/EventMetaData.h>
10#include <framework/dataobjects/ProfileInfo.h>
11#include <framework/datastore/RelationsObject.h>
12#include <framework/utilities/TestHelpers.h>
13
14#include <gtest/gtest.h>
15
16using namespace std;
17using namespace Belle2;
18
19namespace {
21 class RelationsInternal : public ::testing::Test {
22 protected:
24 void SetUp() override
25 {
27 evtData.registerInDataStore();
28 profileData.registerInDataStore();
29 relObjData.registerInDataStore();
31
32 for (int i = 0; i < 10; ++i) {
33 evtData.appendNew();
34 profileData.appendNew();
35 relObjData.appendNew();
36 }
37 }
38
40 void TearDown() override
41 {
43 }
44
45 void findRelationsCheckContents();
46
48 StoreArray<ProfileInfo> profileData;
50 };
51
53 TEST_F(RelationsInternal, RelationCreate)
54 {
56 evtData.registerRelationTo(profileData);
58
59 RelationArray relation(evtData, profileData);
60 EXPECT_FALSE(relation); //creation only happens on write access or explicitly
61 //since we provided arguments to constructor, these should be available even before create()
62 EXPECT_TRUE(relation.getFromAccessorParams() == evtData.getAccessorParams());
63 EXPECT_TRUE(relation.getToAccessorParams() == profileData.getAccessorParams());
64
65 relation.create();
66 EXPECT_TRUE(relation);
67 EXPECT_TRUE(relation.getFromAccessorParams() == evtData.getAccessorParams());
68 EXPECT_TRUE(relation.getToAccessorParams() == profileData.getAccessorParams());
69 }
70
72 TEST_F(RelationsInternal, RelationFind)
73 {
75 EXPECT_FALSE(evtData.optionalRelationTo(profileData));
76 EXPECT_FALSE(evtData.requireRelationTo(profileData));
77
79 evtData.registerRelationTo(profileData);
80 evtData2.registerInDataStore("OwnName");
81 evtData2.registerRelationTo(profileData);
83
84 RelationArray relation(evtData, profileData);
85 EXPECT_FALSE(RelationArray(DataStore::relationName(evtData.getName(), profileData.getName()), DataStore::c_Event));
86 relation.create();
87 EXPECT_TRUE(RelationArray(evtData, profileData, "", DataStore::c_Event));
88 string name = relation.getName();
89
90 EXPECT_EQ("OwnName", evtData2.getName());
91
92
93 RelationArray relationAttachedUsingName(name, DataStore::c_Event);
94 //trying to get the accessor params should cause the array to attach (and thus get the appropriate data)
95 EXPECT_TRUE(relationAttachedUsingName.getFromAccessorParams() == evtData.getAccessorParams());
96 EXPECT_TRUE(relationAttachedUsingName.getToAccessorParams() == profileData.getAccessorParams());
97 EXPECT_TRUE(relationAttachedUsingName);
98
99 //check for OwnNameToProfileInfos
100 EXPECT_FALSE(RelationArray(DataStore::relationName(evtData2.getName(), profileData.getName()), DataStore::c_Event));
101 EXPECT_FALSE(RelationArray("OwnNameToProfileInfos", DataStore::c_Event));
102 RelationArray relation2(evtData2, profileData);
103 relation2.create();
104 EXPECT_TRUE(relation2.getName() == "OwnNameToProfileInfos");
105 EXPECT_TRUE(RelationArray(evtData2, profileData));
106
107 //outside of initialize() this should fail
108 EXPECT_B2FATAL(evtData.requireRelationTo(profileData));
109 }
110
112 TEST_F(RelationsInternal, RelationWrongDeathTest)
113 {
115 RelationArray relation1(evtData, profileData, "test");
116 relation1.registerInDataStore();
118
119 relation1.create();
120 EXPECT_B2FATAL(RelationArray(profileData, evtData, "test").isValid());
121 EXPECT_B2FATAL(RelationArray(profileData, evtData, "test").add(0, 0, 1.0));
122 EXPECT_B2FATAL(RelationArray(profileData, evtData, "test")[0]);
123 EXPECT_B2FATAL(RelationArray(profileData, evtData, "test").getModified());
124 }
125
127 TEST_F(RelationsInternal, RegistrationWithDefaultNames)
128 {
129 //not registered yet
130 EXPECT_B2FATAL(relObjData[0]->addRelationTo(profileData[3]));
131 EXPECT_B2FATAL(DataStore::Instance().addRelationFromTo((evtData)[0], (profileData)[1], 2.0));
132
134 relObjData.registerRelationTo(profileData);
135 evtData.registerRelationTo(profileData);
137
138 //profileData has default name, so this should be ok now
139 relObjData[0]->addRelationTo(profileData[4]);
140 DataStore::Instance().addRelationFromTo((evtData)[0], (profileData)[3], 2.0);
141 }
142
147 TEST_F(RelationsInternal, RelationDefaultConstructed)
148 {
150 RelationArray array(evtData, profileData);
151 array.registerInDataStore();
153
154 auto* rel = new RelationContainer(); //default constructed object, as written to file
155 ASSERT_TRUE(DataStore::Instance().createObject(rel, false, array));
156
157 EXPECT_FALSE(array.isValid());
158
159 //shouldn't die here
160 RelationIndex<EventMetaData, ProfileInfo> index(evtData, profileData);
161 }
162
164 TEST_F(RelationsInternal, BuildIndex)
165 {
167 evtData.registerRelationTo(profileData);
169
170 RelationArray relation(evtData, profileData);
171 relation.add(0, 0, 1.0);
172 relation.add(0, 1, 2.0);
173 relation.add(0, 2, 3.0);
174 relation.consolidate();
175 EXPECT_EQ(relation.getEntries(), 1);
176
178 EXPECT_EQ(relIndex.size(), 3u);
179
180 relation.add(1, 0, 1.0);
182 EXPECT_EQ(relIndex2.size(), 4u);
183 //Rebuilding index will affect old index. Should we copy index?
184 //Copy could be expensive and this should be a corner-case anyway
185 EXPECT_EQ(relIndex.size(), 4u);
186
187 //check elements of last relation (both from objects point to to_obj)
188 const EventMetaData* first_from_obj = (evtData)[0];
189 const EventMetaData* from_obj = (evtData)[1];
190 const ProfileInfo* to_obj = (profileData)[0];
191 EXPECT_TRUE(first_from_obj == relIndex.getFirstElementTo(to_obj)->from);
192 EXPECT_TRUE(to_obj == relIndex.getFirstElementTo(to_obj)->to);
193 EXPECT_FLOAT_EQ(1.0, relIndex.getFirstElementTo(to_obj)->weight);
194 EXPECT_TRUE(first_from_obj == relIndex.getFirstElementTo(*to_obj)->from);
195 EXPECT_TRUE(to_obj == relIndex.getFirstElementFrom(from_obj)->to);
196 EXPECT_TRUE(to_obj == relIndex.getFirstElementFrom(*from_obj)->to);
197 EXPECT_TRUE(to_obj == relIndex.getFirstElementFrom(first_from_obj)->to);
198
199 //check search for non-existing relations
200 EXPECT_TRUE(relIndex.getFirstElementTo(nullptr) == nullptr);
201 EXPECT_TRUE(relIndex.getFirstElementFrom(nullptr) == nullptr);
202 EXPECT_TRUE(relIndex.getFirstElementFrom(nullptr) == nullptr);
203 EXPECT_TRUE(relIndex.getFirstElementFrom((evtData)[4]) == nullptr);
204 EXPECT_TRUE(relIndex.getFirstElementTo((profileData)[3]) == nullptr);
205
206 //check size of found element lists
207 {
208 int size(0);
209 double allweights(0);
210 for (auto& e : relIndex.getElementsFrom((evtData)[0])) {
211 ++size;
212 allweights += e.weight;
213 }
214 EXPECT_EQ(size, 3);
215 EXPECT_DOUBLE_EQ(allweights, 6.0);
216 }
217 {
218 int size(0);
219 double allweights(0);
220 for (auto& e : relIndex.getElementsTo((profileData)[0])) {
221 ++size;
222 allweights += e.weight;
223 }
224 EXPECT_EQ(size, 2);
225 EXPECT_DOUBLE_EQ(allweights, 2.0);
226 }
227 {
228 int size(0);
229 double allweights(0);
230 for (const auto& e : relIndex.getElementsTo((profileData)[4])) {
231 ++size;
232 allweights += e.weight;
233 }
234 EXPECT_EQ(size, 0);
235 EXPECT_DOUBLE_EQ(allweights, 0.0);
236 }
237 }
238
240 TEST_F(RelationsInternal, InconsistentIndexDeathTest)
241 {
243 evtData.registerRelationTo(profileData);
245
246 RelationArray relation(evtData, profileData);
247 relation.add(0, 10, 1.0);
249 EXPECT_B2FATAL(rel_t relIndex);
250 relation.clear();
251 relation.add(10, 0, 1.0);
252 EXPECT_B2FATAL(rel_t relIndex);
253 }
254
255
257 TEST_F(RelationsInternal, EmptyIndex)
258 {
260 evtData.registerRelationTo(profileData);
262
264 EXPECT_FALSE(index);
265 EXPECT_EQ(index.size(), 0u);
266 EXPECT_EQ(index.getFromAccessorParams().first, "");
267 EXPECT_EQ(index.getToAccessorParams().first, "");
268 }
269
271 TEST_F(RelationsInternal, WrongRelationIndexDeathTest)
272 {
273 StoreArray<EventMetaData> eventData("evts");
274
276 RelationArray relation(profileData, evtData, "test");
277 relation.registerInDataStore();
278 RelationArray(evtData, profileData).registerInDataStore();
279 eventData.registerInDataStore();
280 RelationArray relation2(evtData, profileData, "test2");
281 relation2.registerInDataStore();
283
284 relation.create();
286 EXPECT_B2FATAL(rel_t(evtData, profileData, "test"));
287 EXPECT_B2FATAL(rel_t("test"));
288
289 relation2.create();
290 EXPECT_B2FATAL(rel_t(eventData, profileData, "test2"));
291
292 //This relation works and points to evtData, not eventData.
293 //no check is performed, user is responsible to check
294 //using getFromAccessorParams and getToAccessorParams
295 EXPECT_TRUE(rel_t("test2"));
296 }
297
299 void RelationsInternal::findRelationsCheckContents()
300 {
301 const EventMetaData* fromObj = (evtData)[0];
302 RelationVector<ProfileInfo> toRels = DataStore::getRelationsWithObj<ProfileInfo>(fromObj);
303 EXPECT_EQ(toRels.size(), 3u);
304 //this is assuming stable order, correct?
305 EXPECT_DOUBLE_EQ(toRels.weight(0), 1.0);
306 EXPECT_DOUBLE_EQ(toRels.weight(1), 2.0);
307 EXPECT_DOUBLE_EQ(toRels.weight(2), -3.0);
308
309 EXPECT_TRUE(toRels.object(0) == (profileData)[0]);
310 EXPECT_TRUE(toRels.object(1) == (profileData)[1]);
311 EXPECT_TRUE(toRels.object(2) == (profileData)[2]);
312
313 //test range-based for over RelationVector
314 int i = 0;
315 for (const ProfileInfo& prof : toRels) {
316 (void)prof; //variable unused
317 i++;
318 }
319 EXPECT_EQ(i, 3);
320
321 const ProfileInfo* toObj = (profileData)[2];
322 RelationVector<EventMetaData> fromRels = DataStore::getRelationsWithObj<EventMetaData>(toObj);
323 EXPECT_EQ(fromRels.size(), 1u);
324 EXPECT_DOUBLE_EQ(fromRels.weight(0), -3.0);
325 EXPECT_TRUE(fromRels.object(0) == fromObj);
326 EXPECT_TRUE(fromRels[0] == fromObj);
327
328 //some things that shouldn't return anything
329 EXPECT_EQ(DataStore::getRelationsWithObj<EventMetaData>(fromObj).size(), 0u);
330 }
331
333 TEST_F(RelationsInternal, FindRelations)
334 {
336 evtData.registerRelationTo(profileData);
338
339 //check non-existing relations (registered)
340 const EventMetaData* fromObj = (evtData)[0];
341 RelationVector<ProfileInfo> toRels = DataStore::getRelationsWithObj<ProfileInfo>(fromObj);
342 EXPECT_EQ(toRels.size(), 0u);
343 const ProfileInfo* toObj = (profileData)[2];
344 RelationVector<EventMetaData> fromRels = DataStore::getRelationsWithObj<EventMetaData>(toObj);
345 EXPECT_EQ(fromRels.size(), 0u);
346
347 //check non-existing relations (unregistered)
348 RelationVector<EventMetaData> toRels2 = DataStore::getRelationsWithObj<EventMetaData>(fromObj);
349 EXPECT_EQ(toRels2.size(), 0u);
350 RelationVector<ProfileInfo> fromRels2 = DataStore::getRelationsWithObj<ProfileInfo>(toObj);
351 EXPECT_EQ(fromRels2.size(), 0u);
352
353 RelationArray relation(evtData, profileData);
354 relation.add(0, 0, 1.0);
355 relation.add(0, 1, 2.0);
356 relation.add(0, 2, -3.0);
357
358 findRelationsCheckContents();
359
360
361 //check that results don't change after consolidation
362 relation.consolidate();
363 findRelationsCheckContents();
364 }
365
367 TEST_F(RelationsInternal, AddRelations)
368 {
370 evtData.registerRelationTo(profileData);
372
373 DataStore::Instance().addRelationFromTo((evtData)[0], (profileData)[0], 1.0);
374 DataStore::Instance().addRelationFromTo((evtData)[0], (profileData)[1], 2.0);
375 DataStore::Instance().addRelationFromTo((evtData)[0], (profileData)[2], -3.0);
376
377 findRelationsCheckContents();
378 }
379
381 TEST_F(RelationsInternal, GetRelationsWith)
382 {
384 evtData.registerRelationTo(profileData);
386
387 DataStore::Instance().addRelationFromTo((evtData)[0], (profileData)[0], 1.0);
388 DataStore::Instance().addRelationFromTo((evtData)[0], (profileData)[1], 2.0);
389 DataStore::Instance().addRelationFromTo((evtData)[0], (profileData)[2], 3.0);
390
391 //some objects with no relations to given type
392 EXPECT_EQ(DataStore::getRelationsWithObj<EventMetaData>((evtData)[0]).size(), 0u);
393 EXPECT_EQ(DataStore::getRelationsWithObj<EventMetaData>((profileData)[3]).size(), 0u);
394
395 RelationVector<ProfileInfo> profRels = DataStore::getRelationsWithObj<ProfileInfo>((evtData)[0]);
396 EXPECT_EQ(profRels.size(), 3u);
397 EXPECT_EQ(profRels.weight(0), 1.0); //should be positive
398
399 RelationVector<EventMetaData> eventRels = DataStore::getRelationsWithObj<EventMetaData>((profileData)[0]);
400 EXPECT_EQ(eventRels.size(), 1u);
401 EXPECT_EQ(eventRels.weight(0), 1.0); //points to given object, same weight
402 }
403
405 TEST_F(RelationsInternal, SearchAll)
406 {
407 //2nd array of this type
408 StoreArray<ProfileInfo> profileData2("ProfileInfos2");
409
411 profileData2.registerInDataStore();
412 evtData.registerRelationTo(profileData);
413 evtData.registerRelationTo(profileData2);
415
416 DataStore::Instance().addRelationFromTo((evtData)[0], (profileData)[0], 1.0);
417 DataStore::Instance().addRelationFromTo((evtData)[0], (profileData)[1], 2.0);
418 DataStore::Instance().addRelationFromTo((evtData)[0], (profileData)[2], -3.0);
419
420 //add one object (plus relation) to the other array
421 profileData2.appendNew();
422 DataStore::Instance().addRelationFromTo((evtData)[0], profileData2[0], 42.0);
423
424 //profileData2 shouldn't be searched by default when searching or EventMetaData objects
425 findRelationsCheckContents();
426
427 //actually test "ALL" option
428 const EventMetaData* fromObj = (evtData)[0];
429 RelationVector<ProfileInfo> toRels = DataStore::getRelationsWithObj<ProfileInfo>(fromObj, "ALL");
430 EXPECT_EQ(toRels.size(), 4u);
431 //order might be anything, check sum of weights
432 double sum = 0.0;
433 for (int i = 0; i < (int)toRels.size(); i++) {
434 sum += toRels.weight(i);
435 }
436 EXPECT_DOUBLE_EQ(sum, 42.0 + 1 + 2 - 3);
437
438 //finding with default TO name
439 EXPECT_EQ(DataStore::getRelationsWithObj<ProfileInfo>(fromObj, profileData.getName()).size(), 3u);
440 //finding with TO name of 2nd array
441 EXPECT_EQ(DataStore::getRelationsWithObj<ProfileInfo>(fromObj, profileData2.getName()).size(), 1u);
442 //and something that doesn't exist
443 EXPECT_EQ(DataStore::getRelationsWithObj<ProfileInfo>(fromObj, "DoesntExist").size(), 0u);
444
445 const ProfileInfo* toObj = profileData2[0];
446 //object should also be found without specifying the name
447 EXPECT_EQ(DataStore::getRelationsWithObj<EventMetaData>(toObj).size(), 1u);
448 //or with 'ALL"
449 EXPECT_EQ(DataStore::getRelationsWithObj<EventMetaData>(toObj, "ALL").size(), 1u);
450 //and using a base class
451 EXPECT_EQ(DataStore::getRelationsWithObj<TObject>(toObj, "ALL").size(), 1u);
452
453 //no relations to this type
454 EXPECT_EQ(DataStore::getRelationsWithObj<EventMetaData>(fromObj, "ALL").size(), 0u);
455 //should work again
456 EXPECT_EQ(DataStore::getRelationsWithObj<TObject>(fromObj, "ALL").size(), 4u);
457
458 }
459
460
461 TEST_F(RelationsInternal, ListOfRelatedArrays)
462 {
463 //2nd array of this type
464 StoreArray<ProfileInfo> profileData2("ProfileInfos2");
465
466 //nothing
467 EXPECT_EQ(0u, DataStore::Instance().getListOfRelatedArrays(profileData2).size());
468 EXPECT_EQ(0u, DataStore::Instance().getListOfRelatedArrays(profileData).size());
469 EXPECT_EQ(0u, DataStore::Instance().getListOfRelatedArrays(evtData).size());
470
472 profileData2.registerInDataStore();
473 evtData.registerRelationTo(profileData);
474 evtData.registerRelationTo(profileData2);
476
477 EXPECT_EQ(1u, DataStore::Instance().getListOfRelatedArrays(profileData2).size());
478 EXPECT_EQ(1u, DataStore::Instance().getListOfRelatedArrays(profileData).size());
479 EXPECT_EQ(2u, DataStore::Instance().getListOfRelatedArrays(evtData).size());
480
481 EXPECT_EQ(evtData.getName(), DataStore::Instance().getListOfRelatedArrays(profileData2).at(0));
482 EXPECT_EQ(evtData.getName(), DataStore::Instance().getListOfRelatedArrays(profileData).at(0));
483 }
484
485 TEST_F(RelationsInternal, StoreArray_clear_cleans_relations)
486 {
488 evtData.registerRelationTo(relObjData);
489 relObjData.registerRelationTo(profileData);
490 evtData.registerRelationTo(profileData);
492
493 DataStore::Instance().addRelationFromTo((evtData)[0], (relObjData)[0], 1.0);
494 DataStore::Instance().addRelationFromTo((relObjData)[1], (profileData)[9], 1.0);
495 DataStore::Instance().addRelationFromTo((evtData)[1], (profileData)[1], 2.0);
496
497 evtData.clear();
498 profileData.clear();
499
500 EXPECT_EQ(DataStore::getRelationsWithObj<ProfileInfo>(relObjData[1]).size(), 0u);
501 EXPECT_EQ(DataStore::getRelationsWithObj<EventMetaData>(relObjData[0]).size(), 0u);
502 }
503} // namespace
static void addRelationFromTo(const TObject *fromObject, const TObject *toObject, float weight=1.0, const std::string &namedRelation="")
Add a relation from an object in a store array to another object in a store array.
Definition: DataStore.h:387
std::vector< std::string > getListOfRelatedArrays(const StoreAccessorBase &array) const
Returns a list of names of arrays which have registered relations that point to or from 'array'.
Definition: DataStore.cc:640
@ c_Event
Different object in each event, all objects/arrays are invalidated after event() function has been ca...
Definition: DataStore.h:59
static DataStore & Instance()
Instance of singleton Store.
Definition: DataStore.cc:54
void setInitializeActive(bool active)
Setter for m_initializeActive.
Definition: DataStore.cc:94
static std::string relationName(const std::string &fromName, const std::string &toName, std::string const &namedRelation="")
Return storage name for a relation between two arrays of the given names.
Definition: DataStore.h:180
void reset(EDurability durability)
Frees memory occupied by data store items and removes all objects from the map.
Definition: DataStore.cc:86
Store event, run, and experiment numbers.
Definition: EventMetaData.h:33
Store execution time and memory usage.
Definition: ProfileInfo.h:22
Low-level class to create/modify relations between StoreArrays.
Definition: RelationArray.h:62
Class to store relations between StoreArrays in the DataStore.
Provides access to fast ( O(log n) ) bi-directional lookups on a specified relation.
Definition: RelationIndex.h:76
const Element * getFirstElementFrom(const FROM &from) const
Return a pointer to the first relation Element of the given object.
size_t size() const
Get the size of the index.
range_from getElementsFrom(const FROM *from) const
Return a range of all elements pointing from the given object.
const Element * getFirstElementTo(const TO &to) const
Return a pointer to the first relation Element of the given object.
range_to getElementsTo(const TO *to) const
Return a range of all elements pointing to the given object.
Class for type safe access to objects that are referred to in relations.
T * object(int index) const
Get object with index.
size_t size() const
Get number of relations.
float weight(int index) const
Get weight with index.
const std::string & getName() const
Return name under which the object is saved in the DataStore.
bool registerInDataStore(DataStore::EStoreFlags storeFlags=DataStore::c_WriteOut)
Register the object/array in the DataStore.
Accessor to arrays stored in the data store.
Definition: StoreArray.h:113
bool registerRelationTo(const StoreArray< TO > &toArray, DataStore::EDurability durability=DataStore::c_Event, DataStore::EStoreFlags storeFlags=DataStore::c_WriteOut, const std::string &namedRelation="") const
Register a relation to the given StoreArray.
Definition: StoreArray.h:140
bool isValid(EForwardBackward eForwardBackward)
Check whether the given enum instance is one of the valid values.
Abstract base class for different kinds of events.
STL namespace.
const FROM * from
pointer of the element from which the relation points.
const TO * to
pointer of the element to which the relation points.
RelationElement::weight_type weight
weight of the relation.