Line data Source code
1 : /**
2 : * @file test_DataHandlers.cpp
3 : *
4 : * @brief Common tests for DataHandlers class
5 : *
6 : * @author David Frank - initial code
7 : * @author Tobias Lasser - rewrite and code coverage
8 : * @author Jens Petit - refactoring to general DataHandler test
9 : */
10 :
11 : #include "doctest/doctest.h"
12 : #include "DataHandlerCPU.h"
13 : #include "DataHandlerMapCPU.h"
14 : #include "testHelpers.h"
15 :
16 : #ifdef ELSA_CUDA_VECTOR
17 : #include "DataHandlerGPU.h"
18 : #include "DataHandlerMapGPU.h"
19 : #endif
20 :
21 : template <typename data_t>
22 181 : long elsa::useCount(const DataHandlerCPU<data_t>& dh)
23 : {
24 181 : return dh._data.use_count();
25 : }
26 :
27 : #ifdef ELSA_CUDA_VECTOR
28 : template <typename data_t>
29 : long elsa::useCount(const DataHandlerGPU<data_t>& dh)
30 : {
31 : return dh._data.use_count();
32 : }
33 : #endif
34 :
35 : using namespace elsa;
36 : using namespace doctest;
37 :
38 : using CPUTypeTuple =
39 : std::tuple<DataHandlerCPU<float>, DataHandlerCPU<double>, DataHandlerCPU<complex<float>>,
40 : DataHandlerCPU<complex<double>>, DataHandlerCPU<index_t>>;
41 :
42 288 : TYPE_TO_STRING(DataHandlerCPU<float>);
43 252 : TYPE_TO_STRING(DataHandlerCPU<double>);
44 252 : TYPE_TO_STRING(DataHandlerCPU<index_t>);
45 252 : TYPE_TO_STRING(DataHandlerCPU<complex<float>>);
46 252 : TYPE_TO_STRING(DataHandlerCPU<complex<double>>);
47 :
48 : #ifdef ELSA_CUDA_VECTOR
49 : using GPUTypeTuple =
50 : std::tuple<DataHandlerGPU<float>, DataHandlerGPU<double>, DataHandlerGPU<complex<float>>,
51 : DataHandlerGPU<complex<double>>, DataHandlerGPU<index_t>>;
52 :
53 : TYPE_TO_STRING(DataHandlerGPU<float>);
54 : TYPE_TO_STRING(DataHandlerGPU<double>);
55 : TYPE_TO_STRING(DataHandlerGPU<index_t>);
56 : TYPE_TO_STRING(DataHandlerGPU<complex<float>>);
57 : TYPE_TO_STRING(DataHandlerGPU<complex<double>>);
58 : #endif
59 :
60 : TEST_SUITE_BEGIN("core");
61 :
62 236 : TEST_CASE_TEMPLATE_DEFINE("DataHandlers: Testing Construction", TestType, datahandler_construction)
63 : {
64 : using data_t = typename TestType::value_type;
65 :
66 40 : GIVEN("a certain size")
67 : {
68 20 : index_t size = 314;
69 :
70 25 : WHEN("constructing")
71 : {
72 10 : const TestType dh{size};
73 :
74 5 : THEN("it has the correct size") { REQUIRE_EQ(size, dh.getSize()); }
75 : }
76 :
77 25 : WHEN("constructing with a given vector")
78 : {
79 10 : auto randVec = generateRandomMatrix<data_t>(size);
80 10 : const TestType dh{randVec};
81 :
82 1575 : for (index_t i = 0; i < size; ++i)
83 1570 : REQUIRE_UNARY(checkApproxEq(dh[i], randVec(i)));
84 : }
85 :
86 25 : WHEN("copy constructing")
87 : {
88 10 : auto randVec = generateRandomMatrix<data_t>(size);
89 10 : const TestType dh{randVec};
90 10 : const auto dhView = dh.getBlock(0, size);
91 :
92 10 : TestType dh2 = dh;
93 :
94 10 : THEN("a shallow copy is created")
95 : {
96 5 : REQUIRE_EQ(dh2, dh);
97 5 : REQUIRE_EQ(useCount(dh), 2);
98 :
99 10 : const auto dh2View = dh2.getBlock(0, size);
100 10 : AND_THEN("associated maps are not transferred")
101 : {
102 5 : dh2[0] = data_t(1);
103 5 : REQUIRE_UNARY(checkApproxEq(dh2[0], 1));
104 5 : REQUIRE_UNARY(checkApproxEq((*dhView)[0], randVec[0]));
105 5 : REQUIRE_UNARY(checkApproxEq((*dh2View)[0], 1));
106 : }
107 : }
108 : }
109 :
110 25 : WHEN("move constructing")
111 : {
112 10 : auto randVec = generateRandomMatrix<data_t>(size);
113 10 : TestType dh{randVec};
114 10 : const auto dhView = dh.getBlock(0, size);
115 10 : TestType testDh{randVec};
116 :
117 10 : const TestType dh2 = std::move(dh);
118 :
119 10 : THEN("data and associated maps are moved to the new handler")
120 : {
121 5 : REQUIRE_EQ(useCount(dh2), 1);
122 5 : REQUIRE_EQ(dh2, testDh);
123 5 : REQUIRE_EQ(&(*dhView)[0], &dh2[0]);
124 : }
125 : }
126 : }
127 20 : }
128 :
129 236 : TEST_CASE_TEMPLATE_DEFINE("DataHandlers: Testing equality operator", TestType, datahandler_equality)
130 : {
131 : using data_t = typename TestType::value_type;
132 :
133 40 : GIVEN("some DataHandler")
134 : {
135 20 : const index_t size = 314;
136 40 : auto randVec = generateRandomMatrix<data_t>(size);
137 40 : const TestType dh{randVec};
138 :
139 25 : WHEN("comparing to a handler with a different size")
140 : {
141 10 : const TestType dh2{size + 1};
142 10 : THEN("the result is false")
143 : {
144 5 : REQUIRE_NE(dh, dh2);
145 5 : REQUIRE_NE(dh, *dh2.getBlock(0, size + 1));
146 : }
147 : }
148 :
149 25 : WHEN("comparing to a shallow copy or view of the handler")
150 : {
151 10 : const auto dh2 = dh;
152 10 : THEN("the result is true")
153 : {
154 5 : REQUIRE_EQ(dh, dh2);
155 5 : REQUIRE_EQ(dh, *dh.getBlock(0, size));
156 : }
157 : }
158 :
159 25 : WHEN("comparing to a deep copy or a view of the deep copy")
160 : {
161 10 : const TestType dh2{randVec};
162 10 : THEN("the result is true")
163 : {
164 5 : REQUIRE_EQ(dh, dh2);
165 5 : REQUIRE_EQ(dh, *dh2.getBlock(0, size));
166 : }
167 : }
168 :
169 25 : WHEN("comparing to a handler or map with different data")
170 : {
171 5 : randVec[0] += 1;
172 10 : const TestType dh2{randVec};
173 10 : THEN("the result is false")
174 : {
175 5 : REQUIRE_NE(dh, dh2);
176 5 : REQUIRE_NE(dh, *dh2.getBlock(0, size));
177 : }
178 : }
179 : }
180 20 : }
181 :
182 296 : TEST_CASE_TEMPLATE_DEFINE("DataHandlers: Assigning to DataHandlerCPU", TestType,
183 : datahandler_assigncpu)
184 : {
185 : using data_t = typename TestType::value_type;
186 :
187 160 : GIVEN("a DataHandlerCPU with an associated map")
188 : {
189 80 : const index_t size = 314;
190 160 : DataHandlerCPU<data_t> dh{size};
191 160 : auto dhMap = dh.getBlock(size / 2, size / 3);
192 :
193 90 : WHEN("copy assigning")
194 : {
195 :
196 20 : auto randVec = generateRandomMatrix<data_t>(size);
197 20 : const DataHandlerCPU dh2{randVec};
198 20 : const auto dh2Map = dh2.getBlock(size / 2, size / 3);
199 :
200 15 : THEN("sizes must match")
201 : {
202 10 : const DataHandlerCPU<data_t> bigDh{2 * size};
203 10 : REQUIRE_THROWS(dh = bigDh);
204 : }
205 :
206 10 : dh = dh2;
207 15 : THEN("a shallow copy is performed and associated Maps are updated")
208 : {
209 5 : REQUIRE_EQ(useCount(dh), 2);
210 5 : REQUIRE_EQ(dh, dh2);
211 5 : REQUIRE_EQ(*dhMap, *dh2Map);
212 : }
213 : }
214 :
215 90 : WHEN("move assigning")
216 : {
217 20 : auto randVec = generateRandomMatrix<data_t>(size);
218 20 : DataHandlerCPU dh2{randVec};
219 20 : const auto dh2View = dh2.getBlock(0, size);
220 20 : DataHandlerCPU testDh{randVec};
221 :
222 15 : THEN("sizes must match")
223 : {
224 10 : DataHandlerCPU<data_t> bigDh{2 * size};
225 10 : REQUIRE_THROWS(dh = std::move(bigDh));
226 : }
227 :
228 10 : dh = std::move(dh2);
229 15 : THEN("data is moved, associated maps are merged")
230 : {
231 5 : REQUIRE_EQ(useCount(dh), 1);
232 5 : REQUIRE_EQ(dh, testDh);
233 5 : REQUIRE_EQ(&(*dhMap)[0], &dh[size / 2]);
234 5 : REQUIRE_EQ(dhMap->getSize(), size / 3);
235 5 : REQUIRE_EQ(&(*dh2View)[0], &dh[0]);
236 5 : REQUIRE_EQ(dh2View->getSize(), size);
237 : }
238 : }
239 :
240 90 : WHEN("copy assigning a DataHandlerCPU through base pointers")
241 : {
242 10 : DataHandler<data_t>* dhPtr = &dh;
243 :
244 20 : auto randVec = generateRandomMatrix<data_t>(size);
245 20 : const auto dh2Ptr = std::make_unique<const DataHandlerCPU<data_t>>(randVec);
246 20 : const auto dh2Map = dh2Ptr->getBlock(size / 2, size / 3);
247 :
248 15 : THEN("sizes must match")
249 : {
250 10 : std::unique_ptr<DataHandler<data_t>> bigDh =
251 5 : std::make_unique<DataHandlerCPU<data_t>>(2 * size);
252 10 : REQUIRE_THROWS(*dhPtr = *bigDh);
253 : }
254 :
255 10 : *dhPtr = *dh2Ptr;
256 15 : THEN("a shallow copy is performed and associated Maps are updated")
257 : {
258 5 : REQUIRE_EQ(useCount(dh), 2);
259 5 : REQUIRE_EQ(dh, *dh2Ptr);
260 5 : REQUIRE_EQ(*dhMap, *dh2Map);
261 5 : dh[0] = 1;
262 5 : REQUIRE_NE(&dh[0], &(*dh2Ptr)[0]);
263 5 : REQUIRE_EQ(*dhMap, *dh2Map);
264 5 : REQUIRE_EQ(&(*dhMap)[0], &dh[size / 2]);
265 : }
266 : }
267 :
268 90 : WHEN("copy assigning a partial DataHandlerMapCPU through base pointers")
269 : {
270 10 : DataHandler<data_t>* dhPtr = &dh;
271 20 : const auto dhCopy = dh;
272 :
273 20 : auto randVec = generateRandomMatrix<data_t>(2 * size);
274 20 : const DataHandlerCPU<data_t> dh2{randVec};
275 20 : const auto dh2Map = dh2.getBlock(0, size);
276 :
277 15 : THEN("sizes must match")
278 : {
279 10 : const auto bigDh = dh2.getBlock(0, size + 1);
280 10 : REQUIRE_THROWS(*dhPtr = *bigDh);
281 : }
282 :
283 10 : *dhPtr = *dh2Map;
284 15 : THEN("a deep copy is performed")
285 : {
286 5 : REQUIRE_EQ(useCount(dh), 1);
287 5 : REQUIRE_EQ(useCount(dhCopy), 1);
288 5 : REQUIRE_EQ(dh, *dh2Map);
289 5 : REQUIRE_EQ(&(*dhMap)[0], &dh[size / 2]);
290 5 : REQUIRE_EQ(dhMap->getSize(), size / 3);
291 : }
292 : }
293 :
294 90 : WHEN("copy assigning a full DataHandlerMapCPU (aka a view) through base pointers")
295 : {
296 10 : DataHandler<data_t>* dhPtr = &dh;
297 :
298 20 : auto randVec = generateRandomMatrix<data_t>(size);
299 20 : const DataHandlerCPU<data_t> dh2{randVec};
300 20 : const auto dh2View = dh2.getBlock(0, size);
301 20 : const auto dh2Map = dh2.getBlock(size / 2, size / 3);
302 :
303 15 : THEN("sizes must match")
304 : {
305 10 : std::unique_ptr<DataHandler<data_t>> bigDh =
306 5 : std::make_unique<DataHandlerCPU<data_t>>(2 * size);
307 10 : auto bigDhView = bigDh->getBlock(0, 2 * size);
308 10 : REQUIRE_THROWS(*dhPtr = *bigDhView);
309 : }
310 :
311 10 : *dhPtr = *dh2View;
312 15 : THEN("a shallow copy is performed and associated maps are updated")
313 : {
314 5 : REQUIRE_EQ(useCount(dh), 2);
315 5 : REQUIRE_EQ(dh, *dh2View);
316 5 : REQUIRE_EQ(*dhMap, *dh2Map);
317 5 : dh[0] = 1;
318 5 : REQUIRE_NE(&dh[0], &(*dh2View)[0]);
319 5 : REQUIRE_EQ(*dhMap, *dh2Map);
320 5 : REQUIRE_EQ(&(*dhMap)[0], &dh[size / 2]);
321 : }
322 : }
323 :
324 90 : WHEN("move assigning a DataHandlerCPU through base pointers")
325 : {
326 10 : DataHandler<data_t>* dhPtr = &dh;
327 :
328 20 : auto randVec = generateRandomMatrix<data_t>(size);
329 20 : std::unique_ptr<DataHandler<data_t>> dh2Ptr =
330 : std::make_unique<DataHandlerCPU<data_t>>(randVec);
331 20 : const auto dh2View = dh2Ptr->getBlock(0, size);
332 20 : DataHandlerCPU<data_t> testDh{randVec};
333 :
334 15 : THEN("sizes must match")
335 : {
336 10 : std::unique_ptr<DataHandler<data_t>> bigDh =
337 5 : std::make_unique<DataHandlerCPU<data_t>>(2 * size);
338 10 : REQUIRE_THROWS(*dhPtr = std::move(*bigDh));
339 : }
340 :
341 10 : *dhPtr = std::move(*dh2Ptr);
342 15 : THEN("data is moved and associated Maps are updated")
343 : {
344 5 : REQUIRE_EQ(useCount(dh), 1);
345 5 : REQUIRE_EQ(dh, testDh);
346 5 : REQUIRE_EQ(&(*dhMap)[0], &dh[size / 2]);
347 5 : REQUIRE_EQ(dhMap->getSize(), size / 3);
348 5 : REQUIRE_EQ(&(*dh2View)[0], &dh[0]);
349 5 : REQUIRE_EQ(dh2View->getSize(), size);
350 : }
351 : }
352 :
353 90 : WHEN("\"move\" assigning a partial DataHandlerMapCPU through base pointers")
354 : {
355 10 : DataHandler<data_t>* dhPtr = &dh;
356 20 : const auto dhCopy = dh;
357 :
358 20 : auto randVec = generateRandomMatrix<data_t>(2 * size);
359 20 : DataHandlerCPU<data_t> dh2{randVec};
360 20 : const auto dh2Map = dh2.getBlock(0, size);
361 :
362 15 : THEN("sizes must match")
363 : {
364 15 : REQUIRE_THROWS(*dhPtr = std::move(*dh2.getBlock(0, 2 * size)));
365 : }
366 :
367 10 : *dhPtr = std::move(*dh2Map);
368 15 : THEN("a deep copy is performed")
369 : {
370 5 : REQUIRE_EQ(useCount(dh), 1);
371 5 : REQUIRE_EQ(useCount(dhCopy), 1);
372 5 : REQUIRE_EQ(dh, *dh2.getBlock(0, size));
373 5 : REQUIRE_EQ(&(*dhMap)[0], &dh[size / 2]);
374 5 : REQUIRE_EQ(dhMap->getSize(), size / 3);
375 : }
376 : }
377 :
378 90 : WHEN("\"move\" assigning a full DataHandlerMapCPU (aka a view) through base pointers")
379 : {
380 10 : DataHandler<data_t>* dhPtr = &dh;
381 :
382 20 : auto randVec = generateRandomMatrix<data_t>(size);
383 20 : DataHandlerCPU<data_t> dh2{randVec};
384 20 : const auto dh2View = dh2.getBlock(0, size);
385 20 : const auto dh2Map = dh2.getBlock(size / 2, size / 3);
386 :
387 15 : THEN("sizes must match")
388 : {
389 10 : const std::unique_ptr<const DataHandler<data_t>> bigDh =
390 5 : std::make_unique<const DataHandlerCPU<data_t>>(2 * size);
391 15 : REQUIRE_THROWS(*dhPtr = std::move(*bigDh->getBlock(0, 2 * size)));
392 : }
393 :
394 10 : *dhPtr = std::move(*dh2View);
395 15 : THEN("a shallow copy is performed and associated maps are updated")
396 : {
397 5 : REQUIRE_EQ(useCount(dh), 2);
398 5 : REQUIRE_EQ(dh, *dh2View);
399 5 : REQUIRE_EQ(*dhMap, *dh2Map);
400 5 : dh[0] = 1;
401 5 : REQUIRE_NE(&dh[0], &dh2[0]);
402 5 : REQUIRE_EQ(*dhMap, *dh2Map);
403 5 : REQUIRE_EQ(&(*dhMap)[0], &dh[size / 2]);
404 : }
405 : }
406 : }
407 80 : }
408 :
409 : #ifdef ELSA_CUDA_VECTOR
410 : TEST_CASE_TEMPLATE("DataHandlers: Testing clone()", TestType, DataHandlerCPU<float>,
411 : DataHandlerGPU<float>)
412 : #else
413 73 : TEST_CASE_TEMPLATE("DataHandlers: Testing clone()", TestType, DataHandlerCPU<float>)
414 : #endif
415 : {
416 2 : GIVEN("some DataHandler")
417 : {
418 1 : index_t size = 728;
419 2 : TestType dh(size);
420 1 : dh = 1.0f;
421 :
422 2 : WHEN("cloning")
423 : {
424 2 : auto dhClone = dh.clone();
425 :
426 2 : THEN("a shallow copy is produced")
427 : {
428 1 : REQUIRE_NE(dhClone.get(), &dh);
429 :
430 1 : REQUIRE_EQ(useCount(dh), 2);
431 1 : REQUIRE_EQ(*dhClone, dh);
432 :
433 1 : REQUIRE_EQ(dhClone->getSize(), dh.getSize());
434 :
435 1 : dh[0] = 2.f;
436 1 : REQUIRE_NE(dh, *dhClone);
437 : }
438 : }
439 : }
440 1 : }
441 :
442 226 : TEST_CASE_TEMPLATE_DEFINE("DataHandlers: Testing the reduction operations", TestType,
443 : datahandler_reduction)
444 : {
445 : using data_t = typename TestType::value_type;
446 :
447 20 : GIVEN("some DataHandler")
448 : {
449 10 : index_t size = 16;
450 :
451 20 : WHEN("putting in some random data")
452 : {
453 20 : auto randVec = generateRandomMatrix<data_t>(size);
454 20 : TestType dh(randVec);
455 :
456 15 : THEN("the reductions work as expected")
457 : {
458 5 : auto eps = std::numeric_limits<GetFloatingPointType_t<data_t>>::epsilon();
459 5 : REQUIRE_UNARY(checkApproxEq(dh.sum(), randVec.sum()));
460 5 : REQUIRE_UNARY(
461 : checkApproxEq(dh.l0PseudoNorm(), (randVec.array().cwiseAbs() >= eps).count()));
462 5 : REQUIRE_UNARY(checkApproxEq(dh.l1Norm(), randVec.array().abs().sum()));
463 5 : REQUIRE_UNARY(checkApproxEq(dh.lInfNorm(), randVec.array().abs().maxCoeff()));
464 5 : REQUIRE_UNARY(checkApproxEq(dh.squaredL2Norm(), randVec.squaredNorm()));
465 5 : REQUIRE_UNARY(checkApproxEq(dh.l2Norm(), randVec.norm()));
466 :
467 10 : auto randVec2 = generateRandomMatrix<data_t>(size);
468 10 : TestType dh2(randVec2);
469 :
470 5 : REQUIRE_UNARY(checkApproxEq(dh.dot(dh2), randVec.dot(randVec2)));
471 :
472 5 : auto dhMap = dh2.getBlock(0, dh2.getSize());
473 :
474 5 : REQUIRE_UNARY(checkApproxEq(dh.dot(*dhMap), randVec.dot(randVec2)));
475 : }
476 :
477 15 : THEN("the dot product expects correctly sized arguments")
478 : {
479 5 : index_t wrongSize = size - 1;
480 :
481 10 : auto randVec2 = generateRandomMatrix<data_t>(wrongSize);
482 10 : TestType dh2(randVec2);
483 :
484 10 : REQUIRE_THROWS_AS(dh.dot(dh2), InvalidArgumentError);
485 : }
486 : }
487 : }
488 10 : }
489 :
490 231 : TEST_CASE_TEMPLATE_DEFINE("DataHandlers: Testing the element-wise operations", TestType,
491 : datahandler_elementwise)
492 : {
493 : using data_t = typename TestType::value_type;
494 :
495 30 : GIVEN("some DataHandler")
496 : {
497 15 : index_t size = 567;
498 :
499 30 : WHEN("putting in some random data")
500 : {
501 30 : auto randVec = generateRandomMatrix<data_t>(size);
502 30 : TestType dh(randVec);
503 :
504 20 : THEN("the element-wise binary vector operations work as expected")
505 : {
506 10 : TestType oldDh = dh;
507 :
508 10 : auto randVec2 = generateRandomMatrix<data_t>(size);
509 10 : TestType dh2(randVec2);
510 :
511 10 : auto dhMap = dh2.getBlock(0, dh2.getSize());
512 :
513 10 : TestType bigDh{size + 1};
514 10 : REQUIRE_THROWS(dh += bigDh);
515 10 : REQUIRE_THROWS(dh -= bigDh);
516 10 : REQUIRE_THROWS(dh *= bigDh);
517 10 : REQUIRE_THROWS(dh /= bigDh);
518 :
519 5 : dh += dh2;
520 2840 : for (index_t i = 0; i < size; ++i)
521 2835 : REQUIRE_UNARY(checkApproxEq(dh[i], oldDh[i] + dh2[i]));
522 :
523 5 : dh = oldDh;
524 5 : dh += *dhMap;
525 2840 : for (index_t i = 0; i < size; ++i)
526 2835 : REQUIRE_UNARY(checkApproxEq(dh[i], oldDh[i] + dh2[i]));
527 :
528 5 : dh = oldDh;
529 5 : dh -= dh2;
530 2840 : for (index_t i = 0; i < size; ++i)
531 2835 : REQUIRE_UNARY(checkApproxEq(dh[i], oldDh[i] - dh2[i]));
532 :
533 5 : dh = oldDh;
534 5 : dh -= *dhMap;
535 2840 : for (index_t i = 0; i < size; ++i)
536 2835 : REQUIRE_UNARY(checkApproxEq(dh[i], oldDh[i] - dh2[i]));
537 :
538 5 : dh = oldDh;
539 5 : dh *= dh2;
540 2840 : for (index_t i = 0; i < size; ++i)
541 2835 : REQUIRE_UNARY(checkApproxEq(dh[i], oldDh[i] * dh2[i]));
542 :
543 5 : dh = oldDh;
544 5 : dh *= *dhMap;
545 2840 : for (index_t i = 0; i < size; ++i)
546 2835 : REQUIRE_UNARY(checkApproxEq(dh[i], oldDh[i] * dh2[i]));
547 :
548 5 : dh = oldDh;
549 5 : dh /= dh2;
550 2840 : for (index_t i = 0; i < size; ++i)
551 2835 : if (dh2[i] != data_t(0))
552 : // due to floating point arithmetic less precision
553 2835 : REQUIRE_UNARY(checkApproxEq(dh[i], oldDh[i] / dh2[i]));
554 :
555 5 : dh = oldDh;
556 5 : dh /= *dhMap;
557 2840 : for (index_t i = 0; i < size; ++i)
558 2835 : if (dh2[i] != data_t(0))
559 : // due to floating point arithmetic less precision
560 2835 : REQUIRE_UNARY(checkApproxEq(dh[i], oldDh[i] / dh2[i]));
561 : }
562 :
563 20 : THEN("the element-wise binary scalar operations work as expected")
564 : {
565 10 : TestType oldDh = dh;
566 5 : data_t scalar = std::is_integral_v<data_t> ? 3 : 3.5;
567 :
568 5 : dh += scalar;
569 2840 : for (index_t i = 0; i < size; ++i)
570 2835 : REQUIRE_UNARY(checkApproxEq(dh[i], oldDh[i] + scalar));
571 :
572 5 : dh = oldDh;
573 5 : dh -= scalar;
574 2840 : for (index_t i = 0; i < size; ++i)
575 2835 : REQUIRE_UNARY(checkApproxEq(dh[i], oldDh[i] - scalar));
576 :
577 5 : dh = oldDh;
578 5 : dh *= scalar;
579 2840 : for (index_t i = 0; i < size; ++i)
580 2835 : REQUIRE_UNARY(checkApproxEq(dh[i], oldDh[i] * scalar));
581 :
582 5 : dh = oldDh;
583 5 : dh /= scalar;
584 2840 : for (index_t i = 0; i < size; ++i)
585 2835 : REQUIRE_UNARY(checkApproxEq(dh[i], oldDh[i] / scalar));
586 : }
587 :
588 20 : THEN("the element-wise assignment of a scalar works as expected")
589 : {
590 5 : auto scalar = std::is_integral_v<data_t> ? data_t(47) : data_t(47.11f);
591 :
592 5 : dh = scalar;
593 2840 : for (index_t i = 0; i < size; ++i)
594 2835 : REQUIRE_UNARY(checkApproxEq(dh[i], scalar));
595 : }
596 : }
597 : }
598 15 : }
599 :
600 226 : TEST_CASE_TEMPLATE_DEFINE("DataHandlers: Testing referencing blocks", TestType,
601 : datahandler_blockreferencing)
602 : {
603 : using data_t = typename TestType::value_type;
604 :
605 20 : GIVEN("some DataHandler")
606 : {
607 10 : index_t size = 728;
608 20 : Eigen::Matrix<data_t, Eigen::Dynamic, 1> dataVec(size);
609 20 : TestType dh(dataVec);
610 :
611 15 : WHEN("getting the reference to a block")
612 : {
613 10 : REQUIRE_THROWS(dh.getBlock(size, 1));
614 10 : REQUIRE_THROWS(dh.getBlock(0, size + 1));
615 :
616 10 : auto dhBlock = dh.getBlock(size / 3, size / 2);
617 :
618 10 : THEN("returned data handler references the correct elements")
619 : {
620 5 : REQUIRE_EQ(dhBlock->getSize(), size / 2);
621 :
622 1825 : for (index_t i = 0; i < size / 2; i++)
623 1820 : REQUIRE_UNARY(checkApproxEq(&(*dhBlock)[i], &dh[i + size / 3]));
624 : }
625 : }
626 :
627 15 : WHEN("the whole volume is referenced")
628 : {
629 10 : auto dhBlock = dh.getBlock(0, size);
630 :
631 10 : THEN("the referenced volume and the actual volume are equal")
632 : {
633 :
634 5 : REQUIRE_EQ(dh, *dhBlock);
635 : }
636 : }
637 : }
638 10 : }
639 :
640 271 : TEST_CASE_TEMPLATE_DEFINE("DataHandlers: Testing the copy-on-write mechanism", TestType,
641 : datahandler_copyonwrite)
642 : {
643 : using data_t = typename TestType::value_type;
644 :
645 55 : const index_t size = 42;
646 :
647 110 : GIVEN("A random DataContainer")
648 : {
649 110 : auto randVec = generateRandomMatrix<data_t>(size);
650 110 : TestType dh{randVec};
651 :
652 60 : WHEN("const manipulating a copy constructed shallow copy")
653 : {
654 10 : TestType dh2 = dh;
655 :
656 10 : THEN("the data is the same")
657 : {
658 5 : REQUIRE_EQ(dh, dh2);
659 5 : REQUIRE_EQ(useCount(dh), 2);
660 : }
661 : }
662 :
663 100 : WHEN("non-const manipulating a copy constructed shallow copy")
664 : {
665 90 : TestType dh2 = dh;
666 45 : REQUIRE_EQ(useCount(dh), 2);
667 45 : REQUIRE_EQ(useCount(dh2), 2);
668 :
669 50 : THEN("copy-on-write is invoked")
670 : {
671 5 : dh2 += 2;
672 5 : REQUIRE_NE(dh2, dh);
673 5 : REQUIRE_EQ(useCount(dh2), 1);
674 5 : REQUIRE_EQ(useCount(dh), 1);
675 : }
676 :
677 50 : THEN("copy-on-write is invoked")
678 : {
679 5 : dh2 += dh;
680 5 : REQUIRE_NE(dh2, dh);
681 5 : REQUIRE_EQ(useCount(dh2), 1);
682 5 : REQUIRE_EQ(useCount(dh), 1);
683 : }
684 :
685 50 : THEN("copy-on-write is invoked")
686 : {
687 5 : dh2 -= 2;
688 5 : REQUIRE_NE(dh2, dh);
689 : }
690 :
691 50 : THEN("copy-on-write is invoked")
692 : {
693 5 : dh2 -= dh;
694 5 : REQUIRE_NE(dh2, dh);
695 : }
696 :
697 50 : THEN("copy-on-write is invoked")
698 : {
699 5 : dh2 /= 2;
700 5 : REQUIRE_NE(dh2, dh);
701 : }
702 :
703 50 : THEN("copy-on-write is invoked")
704 : {
705 5 : dh2 /= dh;
706 5 : REQUIRE_NE(dh2, dh);
707 : }
708 :
709 50 : THEN("copy-on-write is invoked")
710 : {
711 5 : dh2 *= 2;
712 5 : REQUIRE_NE(dh2, dh);
713 : }
714 :
715 50 : THEN("copy-on-write is invoked")
716 : {
717 5 : dh2 *= dh;
718 5 : REQUIRE_NE(dh2, dh);
719 : }
720 :
721 50 : THEN("copy-on-write is invoked")
722 : {
723 5 : dh[0] += 2;
724 5 : REQUIRE_NE(dh2, dh);
725 : }
726 : }
727 :
728 60 : WHEN("manipulating a non-shallow-copied container")
729 : {
730 215 : for (index_t i = 0; i < dh.getSize(); ++i) {
731 210 : dh[i] += 2;
732 : }
733 :
734 5 : THEN("copy-on-write should not be invoked") { REQUIRE_EQ(useCount(dh), 1); }
735 : }
736 : }
737 55 : }
738 :
739 : // "instantiate" the test templates for cpu types
740 : TEST_CASE_TEMPLATE_APPLY(datahandler_construction, CPUTypeTuple);
741 : TEST_CASE_TEMPLATE_APPLY(datahandler_equality, CPUTypeTuple);
742 : TEST_CASE_TEMPLATE_APPLY(datahandler_assigncpu, CPUTypeTuple);
743 : TEST_CASE_TEMPLATE_APPLY(datahandler_reduction, CPUTypeTuple);
744 : TEST_CASE_TEMPLATE_APPLY(datahandler_elementwise, CPUTypeTuple);
745 : TEST_CASE_TEMPLATE_APPLY(datahandler_blockreferencing, CPUTypeTuple);
746 : TEST_CASE_TEMPLATE_APPLY(datahandler_copyonwrite, CPUTypeTuple);
747 :
748 : #ifdef ELSA_CUDA_VECTOR
749 : // "instantiate" the test templates for GPU types
750 : TEST_CASE_TEMPLATE_APPLY(datahandler_construction, CPUTypeTuple);
751 : TEST_CASE_TEMPLATE_APPLY(datahandler_equality, CPUTypeTuple);
752 : TEST_CASE_TEMPLATE_APPLY(datahandler_assigncpu, CPUTypeTuple);
753 : TEST_CASE_TEMPLATE_APPLY(datahandler_reduction, CPUTypeTuple);
754 : TEST_CASE_TEMPLATE_APPLY(datahandler_elementwise, CPUTypeTuple);
755 : TEST_CASE_TEMPLATE_APPLY(datahandler_blockreferencing, CPUTypeTuple);
756 : TEST_CASE_TEMPLATE_APPLY(datahandler_copyonwrite, CPUTypeTuple);
757 : #endif
758 :
759 : TEST_SUITE_END();
|