21.10.2021, 9:00 - 11:00: Due to updates GitLab may be unavailable for some minutes between 09:00 and 11:00.

test_DataHandlers.cpp 22.3 KB
Newer Older
Tobias Lasser's avatar
Tobias Lasser committed
1
/**
2
 * \file test_DataHandlers.cpp
Tobias Lasser's avatar
Tobias Lasser committed
3
 *
4
 * \brief Common tests for DataHandlers class
Tobias Lasser's avatar
Tobias Lasser committed
5
6
7
 *
 * \author David Frank - initial code
 * \author Tobias Lasser - rewrite and code coverage
8
 * \author Jens Petit - refactoring to general DataHandler test
Tobias Lasser's avatar
Tobias Lasser committed
9
10
11
12
 */

#include <catch2/catch.hpp>
#include "DataHandlerCPU.h"
13
#include "DataHandlerMapCPU.h"
Jens Petit's avatar
Jens Petit committed
14
#include "testHelpers.h"
15
16

template <typename data_t>
17
long elsa::useCount(const DataHandlerCPU<data_t>& dh)
18
19
20
{
    return dh._data.use_count();
}
Tobias Lasser's avatar
Tobias Lasser committed
21
22
23

using namespace elsa;

24
25
TEMPLATE_PRODUCT_TEST_CASE("Scenario: Constructing DataHandler", "", (DataHandlerCPU),
                           (float, double, std::complex<float>, std::complex<double>, index_t))
Jens Petit's avatar
Jens Petit committed
26
{
27
28
    using data_t = typename TestType::value_type;

Jens Petit's avatar
Jens Petit committed
29
30
    GIVEN("a certain size")
    {
Tobias Lasser's avatar
Tobias Lasser committed
31
32
        index_t size = 314;

Jens Petit's avatar
Jens Petit committed
33
34
        WHEN("constructing with zeros")
        {
35
            const TestType dh{size};
Tobias Lasser's avatar
Tobias Lasser committed
36

Jens Petit's avatar
Jens Petit committed
37
            THEN("it has the correct size") { REQUIRE(size == dh.getSize()); }
Tobias Lasser's avatar
Tobias Lasser committed
38

Jens Petit's avatar
Jens Petit committed
39
40
            THEN("it has a zero vector")
            {
Tobias Lasser's avatar
Tobias Lasser committed
41
                for (index_t i = 0; i < size; ++i)
42
                    REQUIRE(dh[i] == static_cast<data_t>(0));
Tobias Lasser's avatar
Tobias Lasser committed
43
44
45
            }
        }

Jens Petit's avatar
Jens Petit committed
46
47
        WHEN("constructing with a given vector")
        {
48
            Eigen::Matrix<data_t, Eigen::Dynamic, 1> randVec{size};
49
            randVec.setRandom();
50
            const TestType dh{randVec};
Tobias Lasser's avatar
Tobias Lasser committed
51
52
53
54

            for (index_t i = 0; i < size; ++i)
                REQUIRE(dh[i] == randVec(i));
        }
55
56
57

        WHEN("copy constructing")
        {
58
            const TestType dh{size};
59
60
            const auto dhView = dh.getBlock(0, size);

61
            TestType dh2 = dh;
62
63
64
65
66
67
68
69
70

            THEN("a shallow copy is created")
            {
                REQUIRE(dh2 == dh);
                REQUIRE(useCount(dh) == 2);

                const auto dh2View = dh2.getBlock(0, size);
                AND_THEN("associated maps are not transferred")
                {
71
72
73
74
                    dh2[0] = data_t(1);
                    REQUIRE(dh2[0] == data_t(1));
                    REQUIRE((*dhView)[0] == data_t(0));
                    REQUIRE((*dh2View)[0] == data_t(1));
75
76
77
78
79
80
                }
            }
        }

        WHEN("move constructing")
        {
81
            Eigen::Matrix<data_t, Eigen::Dynamic, 1> randVec{size};
82
            randVec.setRandom();
83
            TestType dh{randVec};
84
            const auto dhView = dh.getBlock(0, size);
85
            TestType testDh{randVec};
86

87
            const TestType dh2 = std::move(dh);
88
89
90
91
92
93
94
95
96
97
98

            THEN("data and associated maps are moved to the new handler")
            {
                REQUIRE(useCount(dh2) == 1);
                REQUIRE(dh2 == testDh);
                REQUIRE(&(*dhView)[0] == &dh2[0]);
            }
        }
    }
}

99
100
101
TEMPLATE_PRODUCT_TEST_CASE("Scenario: Testing equality operator on DataHandler", "",
                           (DataHandlerCPU),
                           (float, double, std::complex<float>, std::complex<double>, index_t))
102
{
103
104
105
    using data_t = typename TestType::value_type;

    GIVEN("some DataHandler")
106
107
    {
        index_t size = 314;
108
        Eigen::Matrix<data_t, Eigen::Dynamic, 1> randVec{size};
109
        randVec.setRandom();
110
        const TestType dh{randVec};
111
112
113

        WHEN("comparing to a handler with a different size")
        {
114
            const TestType dh2{size + 1};
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
            THEN("the result is false")
            {
                REQUIRE_FALSE(dh == dh2);
                REQUIRE_FALSE(dh == *dh2.getBlock(0, size + 1));
            }
        }

        WHEN("comparing to a shallow copy or view of the handler")
        {
            const auto dh2 = dh;
            THEN("the result is true")
            {
                REQUIRE(dh == dh2);
                REQUIRE(dh == *dh.getBlock(0, size));
            }
        }

        WHEN("comparing to a deep copy or a view of the deep copy")
        {
134
            const TestType dh2{randVec};
135
136
137
138
139
140
141
142
143
144
            THEN("the result is true")
            {
                REQUIRE(dh == dh2);
                REQUIRE(dh == *dh2.getBlock(0, size));
            }
        }

        WHEN("comparing to a handler or map with different data")
        {
            randVec[0] += 1;
145
            const TestType dh2{randVec};
146
147
148
149
150
151
152
153
154
            THEN("the result is false")
            {
                REQUIRE_FALSE(dh == dh2);
                REQUIRE_FALSE(dh == *dh2.getBlock(0, size));
            }
        }
    }
}

155
156
TEMPLATE_PRODUCT_TEST_CASE("Scenario: Assigning to DataHandlerCPU", "", (DataHandlerCPU),
                           (float, double, std::complex<float>, std::complex<double>, index_t))
157
{
158
159
    using data_t = typename TestType::value_type;

160
161
162
    GIVEN("a DataHandlerCPU with an associated map")
    {
        index_t size = 314;
163
        DataHandlerCPU<data_t> dh{size};
164
165
166
167
        auto dhMap = dh.getBlock(size / 2, size / 3);

        WHEN("copy assigning")
        {
168
            Eigen::Matrix<data_t, Eigen::Dynamic, 1> randVec{size};
169
170
171
172
173
174
            randVec.setRandom();
            const DataHandlerCPU dh2{randVec};
            const auto dh2Map = dh2.getBlock(size / 2, size / 3);

            THEN("sizes must match")
            {
175
                const DataHandlerCPU<data_t> bigDh{2 * size};
176
177
178
179
180
181
182
183
184
185
186
187
188
189
                REQUIRE_THROWS(dh = bigDh);
            }

            dh = dh2;
            THEN("a shallow copy is performed and associated Maps are updated")
            {
                REQUIRE(useCount(dh) == 2);
                REQUIRE(dh == dh2);
                REQUIRE(*dhMap == *dh2Map);
            }
        }

        WHEN("move assigning")
        {
190
            Eigen::Matrix<data_t, Eigen::Dynamic, 1> randVec{size};
191
192
193
194
195
196
197
            randVec.setRandom();
            DataHandlerCPU dh2{randVec};
            const auto dh2View = dh2.getBlock(0, size);
            DataHandlerCPU testDh{randVec};

            THEN("sizes must match")
            {
198
                DataHandlerCPU<data_t> bigDh{2 * size};
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
                REQUIRE_THROWS(dh = std::move(bigDh));
            }

            dh = std::move(dh2);
            THEN("data is moved, associated maps are merged")
            {
                REQUIRE(useCount(dh) == 1);
                REQUIRE(dh == testDh);
                REQUIRE(&(*dhMap)[0] == &dh[size / 2]);
                REQUIRE(dhMap->getSize() == size / 3);
                REQUIRE(&(*dh2View)[0] == &dh[0]);
                REQUIRE(dh2View->getSize() == size);
            }
        }

        WHEN("copy assigning a DataHandlerCPU through base pointers")
        {
216
            DataHandler<data_t>* dhPtr = &dh;
217
            Eigen::Matrix<data_t, Eigen::Dynamic, 1> randVec{size};
218
            randVec.setRandom();
219
            const auto dh2Ptr = std::make_unique<const DataHandlerCPU<data_t>>(randVec);
220
221
222
223
            const auto dh2Map = dh2Ptr->getBlock(size / 2, size / 3);

            THEN("sizes must match")
            {
224
225
                std::unique_ptr<DataHandler<data_t>> bigDh =
                    std::make_unique<DataHandlerCPU<data_t>>(2 * size);
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
                REQUIRE_THROWS(*dhPtr = *bigDh);
            }

            *dhPtr = *dh2Ptr;
            THEN("a shallow copy is performed and associated Maps are updated")
            {
                REQUIRE(useCount(dh) == 2);
                REQUIRE(dh == *dh2Ptr);
                REQUIRE(*dhMap == *dh2Map);
                dh[0] = 1;
                REQUIRE(&dh[0] != &(*dh2Ptr)[0]);
                REQUIRE(*dhMap == *dh2Map);
                REQUIRE(&(*dhMap)[0] == &dh[size / 2]);
            }
        }

        WHEN("copy assigning a partial DataHandlerMapCPU through base pointers")
        {
244
            DataHandler<data_t>* dhPtr = &dh;
245
            const auto dhCopy = dh;
246
            Eigen::Matrix<data_t, Eigen::Dynamic, 1> randVec{2 * size};
247
            randVec.setRandom();
248
            const DataHandlerCPU<data_t> dh2{randVec};
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
            const auto dh2Map = dh2.getBlock(0, size);

            THEN("sizes must match")
            {
                const auto bigDh = dh2.getBlock(0, size + 1);
                REQUIRE_THROWS(*dhPtr = *bigDh);
            }

            *dhPtr = *dh2Map;
            THEN("a deep copy is performed")
            {
                REQUIRE(useCount(dh) == 1);
                REQUIRE(useCount(dhCopy) == 1);
                REQUIRE(dh == *dh2Map);
                REQUIRE(&(*dhMap)[0] == &dh[size / 2]);
                REQUIRE(dhMap->getSize() == size / 3);
            }
        }

        WHEN("copy assigning a full DataHandlerMapCPU (aka a view) through base pointers")
        {
270
            DataHandler<data_t>* dhPtr = &dh;
271
            Eigen::Matrix<data_t, Eigen::Dynamic, 1> randVec{size};
272
            randVec.setRandom();
273
            const DataHandlerCPU<data_t> dh2{randVec};
274
275
276
277
278
            const auto dh2View = dh2.getBlock(0, size);
            const auto dh2Map = dh2.getBlock(size / 2, size / 3);

            THEN("sizes must match")
            {
279
280
                std::unique_ptr<DataHandler<data_t>> bigDh =
                    std::make_unique<DataHandlerCPU<data_t>>(2 * size);
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
                auto bigDhView = bigDh->getBlock(0, 2 * size);
                REQUIRE_THROWS(*dhPtr = *bigDhView);
            }

            *dhPtr = *dh2View;
            THEN("a shallow copy is performed and associated maps are updated")
            {
                REQUIRE(useCount(dh) == 2);
                REQUIRE(dh == *dh2View);
                REQUIRE(*dhMap == *dh2Map);
                dh[0] = 1;
                REQUIRE(&dh[0] != &(*dh2View)[0]);
                REQUIRE(*dhMap == *dh2Map);
                REQUIRE(&(*dhMap)[0] == &dh[size / 2]);
            }
        }

        WHEN("move assigning a DataHandlerCPU through base pointers")
        {
300
            DataHandler<data_t>* dhPtr = &dh;
301
            Eigen::Matrix<data_t, Eigen::Dynamic, 1> randVec{size};
302
            randVec.setRandom();
303
304
            std::unique_ptr<DataHandler<data_t>> dh2Ptr =
                std::make_unique<DataHandlerCPU<data_t>>(randVec);
305
            const auto dh2View = dh2Ptr->getBlock(0, size);
306
            DataHandlerCPU<data_t> testDh{randVec};
307
308
309

            THEN("sizes must match")
            {
310
311
                std::unique_ptr<DataHandler<data_t>> bigDh =
                    std::make_unique<DataHandlerCPU<data_t>>(2 * size);
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
                REQUIRE_THROWS(*dhPtr = std::move(*bigDh));
            }

            *dhPtr = std::move(*dh2Ptr);
            THEN("data is moved and associated Maps are updated")
            {
                REQUIRE(useCount(dh) == 1);
                REQUIRE(dh == testDh);
                REQUIRE(&(*dhMap)[0] == &dh[size / 2]);
                REQUIRE(dhMap->getSize() == size / 3);
                REQUIRE(&(*dh2View)[0] == &dh[0]);
                REQUIRE(dh2View->getSize() == size);
            }
        }

        WHEN("\"move\" assigning a partial DataHandlerMapCPU through base pointers")
        {
329
            DataHandler<data_t>* dhPtr = &dh;
330
            const auto dhCopy = dh;
331
            Eigen::Matrix<data_t, Eigen::Dynamic, 1> randVec{2 * size};
332
            randVec.setRandom();
333
            DataHandlerCPU<data_t> dh2{randVec};
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
            const auto dh2Map = dh2.getBlock(0, size);

            THEN("sizes must match")
            {
                REQUIRE_THROWS(*dhPtr = std::move(*dh2.getBlock(0, 2 * size)));
            }

            *dhPtr = std::move(*dh2Map);
            THEN("a deep copy is performed")
            {
                REQUIRE(useCount(dh) == 1);
                REQUIRE(useCount(dhCopy) == 1);
                REQUIRE(dh == *dh2.getBlock(0, size));
                REQUIRE(&(*dhMap)[0] == &dh[size / 2]);
                REQUIRE(dhMap->getSize() == size / 3);
            }
        }

        WHEN("\"move\" assigning a full DataHandlerMapCPU (aka a view) through base pointers")
        {
354
            DataHandler<data_t>* dhPtr = &dh;
355
            Eigen::Matrix<data_t, Eigen::Dynamic, 1> randVec{size};
356
            randVec.setRandom();
357
            DataHandlerCPU<data_t> dh2{randVec};
358
359
360
361
362
            const auto dh2View = dh2.getBlock(0, size);
            const auto dh2Map = dh2.getBlock(size / 2, size / 3);

            THEN("sizes must match")
            {
363
364
                const std::unique_ptr<const DataHandler<data_t>> bigDh =
                    std::make_unique<const DataHandlerCPU<data_t>>(2 * size);
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
                REQUIRE_THROWS(*dhPtr = std::move(*bigDh->getBlock(0, 2 * size)));
            }

            *dhPtr = std::move(*dh2View);
            THEN("a shallow copy is performed and associated maps are updated")
            {
                REQUIRE(useCount(dh) == 2);
                REQUIRE(dh == *dh2View);
                REQUIRE(*dhMap == *dh2Map);
                dh[0] = 1;
                REQUIRE(&dh[0] != &dh2[0]);
                REQUIRE(*dhMap == *dh2Map);
                REQUIRE(&(*dhMap)[0] == &dh[size / 2]);
            }
        }
Tobias Lasser's avatar
Tobias Lasser committed
380
381
382
    }
}

383
TEMPLATE_TEST_CASE("Scenario: Cloning DataHandler", "", DataHandlerCPU<float>)
Jens Petit's avatar
Jens Petit committed
384
{
385
    GIVEN("some DataHandler")
Jens Petit's avatar
Jens Petit committed
386
    {
Tobias Lasser's avatar
Tobias Lasser committed
387
        index_t size = 728;
388
        TestType dh(size);
Tobias Lasser's avatar
Tobias Lasser committed
389

Jens Petit's avatar
Jens Petit committed
390
391
        WHEN("cloning")
        {
Tobias Lasser's avatar
Tobias Lasser committed
392
393
            auto dhClone = dh.clone();

394
            THEN("a shallow copy is produced")
Jens Petit's avatar
Jens Petit committed
395
            {
Tobias Lasser's avatar
Tobias Lasser committed
396
                REQUIRE(dhClone.get() != &dh);
397
398

                REQUIRE(useCount(dh) == 2);
Tobias Lasser's avatar
Tobias Lasser committed
399
400
401
                REQUIRE(*dhClone == dh);

                REQUIRE(dhClone->getSize() == dh.getSize());
402
403
404

                dh[0] = 1;
                REQUIRE(dh != *dhClone);
Tobias Lasser's avatar
Tobias Lasser committed
405
406
407
408
409
            }
        }
    }
}

410
411
412
TEMPLATE_PRODUCT_TEST_CASE("Scenario: Testing the reduction operatios of DataHandler", "",
                           (DataHandlerCPU),
                           (float, double, std::complex<float>, std::complex<double>, index_t))
Jens Petit's avatar
Jens Petit committed
413
{
414
415
416
    using data_t = typename TestType::value_type;

    GIVEN("some DataHandler")
Jens Petit's avatar
Jens Petit committed
417
    {
Tobias Lasser's avatar
Tobias Lasser committed
418
419
        index_t size = 284;

Jens Petit's avatar
Jens Petit committed
420
421
        WHEN("putting in some random data")
        {
422
            Eigen::Matrix<data_t, Eigen::Dynamic, 1> randVec{size};
423
            randVec.setRandom();
424
            TestType dh(randVec);
Tobias Lasser's avatar
Tobias Lasser committed
425

Jens Petit's avatar
Jens Petit committed
426
427
            THEN("the reductions work as expected")
            {
428
                REQUIRE(dh.sum() == randVec.sum());
Tobias Lasser's avatar
Tobias Lasser committed
429
430
                REQUIRE(dh.l1Norm() == Approx(randVec.array().abs().sum()));
                REQUIRE(dh.lInfNorm() == Approx(randVec.array().abs().maxCoeff()));
Jens Petit's avatar
Jens Petit committed
431
                REQUIRE(dh.squaredL2Norm() == Approx(randVec.squaredNorm()));
Tobias Lasser's avatar
Tobias Lasser committed
432

433
434
                Eigen::Matrix<data_t, Eigen::Dynamic, 1> randVec2 =
                    Eigen::Matrix<data_t, Eigen::Dynamic, 1>::Random(size);
435
                TestType dh2(randVec2);
Tobias Lasser's avatar
Tobias Lasser committed
436

Jens Petit's avatar
Jens Petit committed
437
                REQUIRE(checkSameNumbers(dh.dot(dh2), randVec.dot(randVec2)));
438
439
440

                auto dhMap = dh2.getBlock(0, dh2.getSize());

Jens Petit's avatar
Jens Petit committed
441
                REQUIRE(checkSameNumbers(dh.dot(*dhMap), randVec.dot(randVec2)));
Tobias Lasser's avatar
Tobias Lasser committed
442
443
            }

Jens Petit's avatar
Jens Petit committed
444
445
            THEN("the dot product expects correctly sized arguments")
            {
Tobias Lasser's avatar
Tobias Lasser committed
446
                index_t wrongSize = size - 1;
447
448
                Eigen::Matrix<data_t, Eigen::Dynamic, 1> randVec2 =
                    Eigen::Matrix<data_t, Eigen::Dynamic, 1>::Random(wrongSize);
449
                TestType dh2(randVec2);
Tobias Lasser's avatar
Tobias Lasser committed
450
451
452
453
454
455
456

                REQUIRE_THROWS_AS(dh.dot(dh2), std::invalid_argument);
            }
        }
    }
}

457
458
459
TEMPLATE_PRODUCT_TEST_CASE("Scenario: Testing the element-wise operations of DataHandler", "",
                           (DataHandlerCPU),
                           (float, double, std::complex<float>, std::complex<double>, index_t))
Jens Petit's avatar
Jens Petit committed
460
{
461
462
463
    using data_t = typename TestType::value_type;

    GIVEN("some DataHandler")
Jens Petit's avatar
Jens Petit committed
464
    {
Tobias Lasser's avatar
Tobias Lasser committed
465
466
        index_t size = 567;

Jens Petit's avatar
Jens Petit committed
467
468
        WHEN("putting in some random data")
        {
469
470
            Eigen::Matrix<data_t, Eigen::Dynamic, 1> randVec =
                Eigen::Matrix<data_t, Eigen::Dynamic, 1>::Random(size);
471
            TestType dh(randVec);
Tobias Lasser's avatar
Tobias Lasser committed
472

Jens Petit's avatar
Jens Petit committed
473
474
            THEN("the element-wise binary vector operations work as expected")
            {
475
                TestType oldDh = dh;
Tobias Lasser's avatar
Tobias Lasser committed
476

477
478
                Eigen::Matrix<data_t, Eigen::Dynamic, 1> randVec2 =
                    Eigen::Matrix<data_t, Eigen::Dynamic, 1>::Random(size);
479
                TestType dh2(randVec2);
Tobias Lasser's avatar
Tobias Lasser committed
480

481
482
                auto dhMap = dh2.getBlock(0, dh2.getSize());

483
                TestType bigDh{size + 1};
484
485
486
487
488
                REQUIRE_THROWS(dh += bigDh);
                REQUIRE_THROWS(dh -= bigDh);
                REQUIRE_THROWS(dh *= bigDh);
                REQUIRE_THROWS(dh /= bigDh);

Tobias Lasser's avatar
Tobias Lasser committed
489
490
491
492
                dh += dh2;
                for (index_t i = 0; i < size; ++i)
                    REQUIRE(dh[i] == oldDh[i] + dh2[i]);

493
494
495
496
497
                dh = oldDh;
                dh += *dhMap;
                for (index_t i = 0; i < size; ++i)
                    REQUIRE(dh[i] == oldDh[i] + dh2[i]);

Tobias Lasser's avatar
Tobias Lasser committed
498
499
500
501
502
                dh = oldDh;
                dh -= dh2;
                for (index_t i = 0; i < size; ++i)
                    REQUIRE(dh[i] == oldDh[i] - dh2[i]);

503
504
505
506
507
                dh = oldDh;
                dh -= *dhMap;
                for (index_t i = 0; i < size; ++i)
                    REQUIRE(dh[i] == oldDh[i] - dh2[i]);

Tobias Lasser's avatar
Tobias Lasser committed
508
509
510
511
512
                dh = oldDh;
                dh *= dh2;
                for (index_t i = 0; i < size; ++i)
                    REQUIRE(dh[i] == oldDh[i] * dh2[i]);

513
514
515
516
517
                dh = oldDh;
                dh *= *dhMap;
                for (index_t i = 0; i < size; ++i)
                    REQUIRE(dh[i] == oldDh[i] * dh2[i]);

Tobias Lasser's avatar
Tobias Lasser committed
518
519
520
                dh = oldDh;
                dh /= dh2;
                for (index_t i = 0; i < size; ++i)
521
                    if (dh2[i] != data_t(0))
Jens Petit's avatar
Jens Petit committed
522
                        REQUIRE(checkSameNumbers(dh[i], oldDh[i] / dh2[i]));
523
524
525
526

                dh = oldDh;
                dh /= *dhMap;
                for (index_t i = 0; i < size; ++i)
527
                    if (dh2[i] != data_t(0))
Jens Petit's avatar
Jens Petit committed
528
                        REQUIRE(checkSameNumbers(dh[i], oldDh[i] / dh2[i]));
Tobias Lasser's avatar
Tobias Lasser committed
529
530
            }

Jens Petit's avatar
Jens Petit committed
531
532
            THEN("the element-wise binary scalar operations work as expected")
            {
533
534
                TestType oldDh = dh;
                data_t scalar = std::is_integral_v<data_t> ? 3 : 3.5;
Tobias Lasser's avatar
Tobias Lasser committed
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552

                dh += scalar;
                for (index_t i = 0; i < size; ++i)
                    REQUIRE(dh[i] == oldDh[i] + scalar);

                dh = oldDh;
                dh -= scalar;
                for (index_t i = 0; i < size; ++i)
                    REQUIRE(dh[i] == oldDh[i] - scalar);

                dh = oldDh;
                dh *= scalar;
                for (index_t i = 0; i < size; ++i)
                    REQUIRE(dh[i] == oldDh[i] * scalar);

                dh = oldDh;
                dh /= scalar;
                for (index_t i = 0; i < size; ++i)
Jens Petit's avatar
Jens Petit committed
553
                    REQUIRE(checkSameNumbers(dh[i], oldDh[i] / scalar));
Tobias Lasser's avatar
Tobias Lasser committed
554
555
            }

Jens Petit's avatar
Jens Petit committed
556
557
            THEN("the element-wise assignment of a scalar works as expected")
            {
558
                auto scalar = std::is_integral_v<data_t> ? data_t(47) : data_t(47.11f);
Tobias Lasser's avatar
Tobias Lasser committed
559
560
561
562
563
564
565
566
567

                dh = scalar;
                for (index_t i = 0; i < size; ++i)
                    REQUIRE(dh[i] == scalar);
            }
        }
    }
}

568
569
TEMPLATE_PRODUCT_TEST_CASE("Scenario: Referencing blocks of DataHandler", "", (DataHandlerCPU),
                           (float, double, std::complex<float>, std::complex<double>, index_t))
570
{
571
572
573
    using data_t = typename TestType::value_type;

    GIVEN("some DataHandler")
574
575
    {
        index_t size = 728;
576
577
        Eigen::Matrix<data_t, Eigen::Dynamic, 1> dataVec(size);
        TestType dh(dataVec);
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607

        WHEN("getting the reference to a block")
        {
            REQUIRE_THROWS(dh.getBlock(size, 1));
            REQUIRE_THROWS(dh.getBlock(0, size + 1));

            auto dhBlock = dh.getBlock(size / 3, size / 2);

            THEN("returned data handler references the correct elements")
            {
                REQUIRE(dhBlock->getSize() == size / 2);

                for (index_t i = 0; i < size / 2; i++)
                    REQUIRE(&(*dhBlock)[i] == &dh[i + size / 3]);
            }
        }

        WHEN("the whole volume is referenced")
        {
            auto dhBlock = dh.getBlock(0, size);

            THEN("the referenced volume and the actual volume are equal")
            {

                REQUIRE(dh == *dhBlock);
            }
        }
    }
}

608
609
TEMPLATE_PRODUCT_TEST_CASE("Scenario: Testing the copy-on-write mechanism", "", (DataHandlerCPU),
                           (float, double, std::complex<float>, std::complex<double>, index_t))
610
{
611
612
    using data_t = typename TestType::value_type;

613
614
    GIVEN("A random DataContainer")
    {
615
616
        Eigen::Matrix<data_t, Eigen::Dynamic, 1> randVec =
            Eigen::Matrix<data_t, Eigen::Dynamic, 1>::Random(42);
617
        TestType dh{randVec};
618
619
620

        WHEN("const manipulating a copy constructed shallow copy")
        {
621
            TestType dh2 = dh;
622
623
624
625
626
627
628
629
630
631

            THEN("the data is the same")
            {
                REQUIRE(dh == dh2);
                REQUIRE(useCount(dh) == 2);
            }
        }

        WHEN("non-const manipulating a copy constructed shallow copy")
        {
632
            TestType dh2 = dh;
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
            REQUIRE(useCount(dh) == 2);
            REQUIRE(useCount(dh2) == 2);

            THEN("copy-on-write is invoked")
            {
                dh2 += 2;
                REQUIRE(dh2 != dh);
                REQUIRE(useCount(dh2) == 1);
                REQUIRE(useCount(dh) == 1);
            }

            THEN("copy-on-write is invoked")
            {
                dh2 += dh;
                REQUIRE(dh2 != dh);
                REQUIRE(useCount(dh2) == 1);
                REQUIRE(useCount(dh) == 1);
            }

            THEN("copy-on-write is invoked")
            {
                dh2 -= 2;
                REQUIRE(dh2 != dh);
            }

            THEN("copy-on-write is invoked")
            {
                dh2 -= dh;
                REQUIRE(dh2 != dh);
            }

            THEN("copy-on-write is invoked")
            {
                dh2 /= 2;
                REQUIRE(dh2 != dh);
            }

            THEN("copy-on-write is invoked")
            {
                dh2 /= dh;
                REQUIRE(dh2 != dh);
            }

            THEN("copy-on-write is invoked")
            {
                dh2 *= 2;
                REQUIRE(dh2 != dh);
            }

            THEN("copy-on-write is invoked")
            {
                dh2 *= dh;
                REQUIRE(dh2 != dh);
            }

            THEN("copy-on-write is invoked")
            {
                dh[0] += 2;
                REQUIRE(dh2 != dh);
            }
        }

        WHEN("manipulating a non-shallow-copied container")
        {
            for (index_t i = 0; i < dh.getSize(); ++i) {
                dh[i] += 2;
            }

            THEN("copy-on-write should not be invoked") { REQUIRE(useCount(dh) == 1); }
        }
    }
704
}