LCOV - code coverage report
Current view: top level - elsa/core/tests - test_DataHandlers.cpp (source / functions) Hit Total Coverage
Test: coverage-all.lcov Lines: 543 543 100.0 %
Date: 2022-08-25 03:05:39 Functions: 41 41 100.0 %

          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             : long elsa::useCount(const DataHandlerCPU<data_t>& dh)
      23         306 : {
      24         306 :     return dh._data.use_count();
      25         306 : }
      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             : TYPE_TO_STRING(DataHandlerCPU<float>);
      43             : TYPE_TO_STRING(DataHandlerCPU<double>);
      44             : TYPE_TO_STRING(DataHandlerCPU<index_t>);
      45             : TYPE_TO_STRING(DataHandlerCPU<complex<float>>);
      46             : 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             : TEST_CASE_TEMPLATE_DEFINE("DataHandlers: Testing Construction", TestType, datahandler_construction)
      63          20 : {
      64          20 :     using data_t = typename TestType::value_type;
      65             : 
      66          20 :     GIVEN("a certain size")
      67          20 :     {
      68          20 :         index_t size = 314;
      69             : 
      70          20 :         WHEN("constructing")
      71          20 :         {
      72           5 :             const TestType dh{size};
      73             : 
      74           5 :             THEN("it has the correct size") { REQUIRE_EQ(size, dh.getSize()); }
      75           5 :         }
      76             : 
      77          20 :         WHEN("constructing with a given vector")
      78          20 :         {
      79           5 :             auto randVec = generateRandomMatrix<data_t>(size);
      80           5 :             const TestType dh{randVec};
      81             : 
      82        1575 :             for (index_t i = 0; i < size; ++i)
      83           5 :                 REQUIRE_UNARY(checkApproxEq(dh[i], randVec(i)));
      84           5 :         }
      85             : 
      86          20 :         WHEN("copy constructing")
      87          20 :         {
      88           5 :             auto randVec = generateRandomMatrix<data_t>(size);
      89           5 :             const TestType dh{randVec};
      90           5 :             const auto dhView = dh.getBlock(0, size);
      91             : 
      92           5 :             TestType dh2 = dh;
      93             : 
      94           5 :             THEN("a shallow copy is created")
      95           5 :             {
      96           5 :                 REQUIRE_EQ(dh2, dh);
      97           5 :                 REQUIRE_EQ(useCount(dh), 2);
      98             : 
      99           5 :                 const auto dh2View = dh2.getBlock(0, size);
     100           5 :                 AND_THEN("associated maps are not transferred")
     101           5 :                 {
     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           5 :                 }
     107           5 :             }
     108           5 :         }
     109             : 
     110          20 :         WHEN("move constructing")
     111          20 :         {
     112           5 :             auto randVec = generateRandomMatrix<data_t>(size);
     113           5 :             TestType dh{randVec};
     114           5 :             const auto dhView = dh.getBlock(0, size);
     115           5 :             TestType testDh{randVec};
     116             : 
     117           5 :             const TestType dh2 = std::move(dh);
     118             : 
     119           5 :             THEN("data and associated maps are moved to the new handler")
     120           5 :             {
     121           5 :                 REQUIRE_EQ(useCount(dh2), 1);
     122           5 :                 REQUIRE_EQ(dh2, testDh);
     123           5 :                 REQUIRE_EQ(&(*dhView)[0], &dh2[0]);
     124           5 :             }
     125           5 :         }
     126          20 :     }
     127          20 : }
     128             : 
     129             : TEST_CASE_TEMPLATE_DEFINE("DataHandlers: Testing equality operator", TestType, datahandler_equality)
     130          20 : {
     131          20 :     using data_t = typename TestType::value_type;
     132             : 
     133          20 :     GIVEN("some DataHandler")
     134          20 :     {
     135          20 :         const index_t size = 314;
     136          20 :         auto randVec = generateRandomMatrix<data_t>(size);
     137          20 :         const TestType dh{randVec};
     138             : 
     139          20 :         WHEN("comparing to a handler with a different size")
     140          20 :         {
     141           5 :             const TestType dh2{size + 1};
     142           5 :             THEN("the result is false")
     143           5 :             {
     144           5 :                 REQUIRE_NE(dh, dh2);
     145           5 :                 REQUIRE_NE(dh, *dh2.getBlock(0, size + 1));
     146           5 :             }
     147           5 :         }
     148             : 
     149          20 :         WHEN("comparing to a shallow copy or view of the handler")
     150          20 :         {
     151           5 :             const auto dh2 = dh;
     152           5 :             THEN("the result is true")
     153           5 :             {
     154           5 :                 REQUIRE_EQ(dh, dh2);
     155           5 :                 REQUIRE_EQ(dh, *dh.getBlock(0, size));
     156           5 :             }
     157           5 :         }
     158             : 
     159          20 :         WHEN("comparing to a deep copy or a view of the deep copy")
     160          20 :         {
     161           5 :             const TestType dh2{randVec};
     162           5 :             THEN("the result is true")
     163           5 :             {
     164           5 :                 REQUIRE_EQ(dh, dh2);
     165           5 :                 REQUIRE_EQ(dh, *dh2.getBlock(0, size));
     166           5 :             }
     167           5 :         }
     168             : 
     169          20 :         WHEN("comparing to a handler or map with different data")
     170          20 :         {
     171           5 :             randVec[0] += 1;
     172           5 :             const TestType dh2{randVec};
     173           5 :             THEN("the result is false")
     174           5 :             {
     175           5 :                 REQUIRE_NE(dh, dh2);
     176           5 :                 REQUIRE_NE(dh, *dh2.getBlock(0, size));
     177           5 :             }
     178           5 :         }
     179          20 :     }
     180          20 : }
     181             : 
     182             : TEST_CASE_TEMPLATE_DEFINE("DataHandlers: Assigning to DataHandlerCPU", TestType,
     183             :                           datahandler_assigncpu)
     184          80 : {
     185          80 :     using data_t = typename TestType::value_type;
     186             : 
     187          80 :     GIVEN("a DataHandlerCPU with an associated map")
     188          80 :     {
     189          80 :         const index_t size = 314;
     190          80 :         DataHandlerCPU<data_t> dh{size};
     191          80 :         auto dhMap = dh.getBlock(size / 2, size / 3);
     192             : 
     193          80 :         WHEN("copy assigning")
     194          80 :         {
     195             : 
     196          10 :             auto randVec = generateRandomMatrix<data_t>(size);
     197          10 :             const DataHandlerCPU dh2{randVec};
     198          10 :             const auto dh2Map = dh2.getBlock(size / 2, size / 3);
     199             : 
     200          10 :             THEN("sizes must match")
     201          10 :             {
     202           5 :                 const DataHandlerCPU<data_t> bigDh{2 * size};
     203           5 :                 REQUIRE_THROWS(dh = bigDh);
     204           5 :             }
     205             : 
     206          10 :             dh = dh2;
     207          10 :             THEN("a shallow copy is performed and associated Maps are updated")
     208          10 :             {
     209           5 :                 REQUIRE_EQ(useCount(dh), 2);
     210           5 :                 REQUIRE_EQ(dh, dh2);
     211           5 :                 REQUIRE_EQ(*dhMap, *dh2Map);
     212           5 :             }
     213          10 :         }
     214             : 
     215          80 :         WHEN("move assigning")
     216          80 :         {
     217          10 :             auto randVec = generateRandomMatrix<data_t>(size);
     218          10 :             DataHandlerCPU dh2{randVec};
     219          10 :             const auto dh2View = dh2.getBlock(0, size);
     220          10 :             DataHandlerCPU testDh{randVec};
     221             : 
     222          10 :             THEN("sizes must match")
     223          10 :             {
     224           5 :                 DataHandlerCPU<data_t> bigDh{2 * size};
     225           5 :                 REQUIRE_THROWS(dh = std::move(bigDh));
     226           5 :             }
     227             : 
     228          10 :             dh = std::move(dh2);
     229          10 :             THEN("data is moved, associated maps are merged")
     230          10 :             {
     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           5 :             }
     238          10 :         }
     239             : 
     240          80 :         WHEN("copy assigning a DataHandlerCPU through base pointers")
     241          80 :         {
     242          10 :             DataHandler<data_t>* dhPtr = &dh;
     243             : 
     244          10 :             auto randVec = generateRandomMatrix<data_t>(size);
     245          10 :             const auto dh2Ptr = std::make_unique<const DataHandlerCPU<data_t>>(randVec);
     246          10 :             const auto dh2Map = dh2Ptr->getBlock(size / 2, size / 3);
     247             : 
     248          10 :             THEN("sizes must match")
     249          10 :             {
     250           5 :                 std::unique_ptr<DataHandler<data_t>> bigDh =
     251           5 :                     std::make_unique<DataHandlerCPU<data_t>>(2 * size);
     252           5 :                 REQUIRE_THROWS(*dhPtr = *bigDh);
     253           5 :             }
     254             : 
     255          10 :             *dhPtr = *dh2Ptr;
     256          10 :             THEN("a shallow copy is performed and associated Maps are updated")
     257          10 :             {
     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           5 :             }
     266          10 :         }
     267             : 
     268          80 :         WHEN("copy assigning a partial DataHandlerMapCPU through base pointers")
     269          80 :         {
     270          10 :             DataHandler<data_t>* dhPtr = &dh;
     271          10 :             const auto dhCopy = dh;
     272             : 
     273          10 :             auto randVec = generateRandomMatrix<data_t>(2 * size);
     274          10 :             const DataHandlerCPU<data_t> dh2{randVec};
     275          10 :             const auto dh2Map = dh2.getBlock(0, size);
     276             : 
     277          10 :             THEN("sizes must match")
     278          10 :             {
     279           5 :                 const auto bigDh = dh2.getBlock(0, size + 1);
     280           5 :                 REQUIRE_THROWS(*dhPtr = *bigDh);
     281           5 :             }
     282             : 
     283          10 :             *dhPtr = *dh2Map;
     284          10 :             THEN("a deep copy is performed")
     285          10 :             {
     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           5 :             }
     292          10 :         }
     293             : 
     294          80 :         WHEN("copy assigning a full DataHandlerMapCPU (aka a view) through base pointers")
     295          80 :         {
     296          10 :             DataHandler<data_t>* dhPtr = &dh;
     297             : 
     298          10 :             auto randVec = generateRandomMatrix<data_t>(size);
     299          10 :             const DataHandlerCPU<data_t> dh2{randVec};
     300          10 :             const auto dh2View = dh2.getBlock(0, size);
     301          10 :             const auto dh2Map = dh2.getBlock(size / 2, size / 3);
     302             : 
     303          10 :             THEN("sizes must match")
     304          10 :             {
     305           5 :                 std::unique_ptr<DataHandler<data_t>> bigDh =
     306           5 :                     std::make_unique<DataHandlerCPU<data_t>>(2 * size);
     307           5 :                 auto bigDhView = bigDh->getBlock(0, 2 * size);
     308           5 :                 REQUIRE_THROWS(*dhPtr = *bigDhView);
     309           5 :             }
     310             : 
     311          10 :             *dhPtr = *dh2View;
     312          10 :             THEN("a shallow copy is performed and associated maps are updated")
     313          10 :             {
     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           5 :             }
     322          10 :         }
     323             : 
     324          80 :         WHEN("move assigning a DataHandlerCPU through base pointers")
     325          80 :         {
     326          10 :             DataHandler<data_t>* dhPtr = &dh;
     327             : 
     328          10 :             auto randVec = generateRandomMatrix<data_t>(size);
     329          10 :             std::unique_ptr<DataHandler<data_t>> dh2Ptr =
     330          10 :                 std::make_unique<DataHandlerCPU<data_t>>(randVec);
     331          10 :             const auto dh2View = dh2Ptr->getBlock(0, size);
     332          10 :             DataHandlerCPU<data_t> testDh{randVec};
     333             : 
     334          10 :             THEN("sizes must match")
     335          10 :             {
     336           5 :                 std::unique_ptr<DataHandler<data_t>> bigDh =
     337           5 :                     std::make_unique<DataHandlerCPU<data_t>>(2 * size);
     338           5 :                 REQUIRE_THROWS(*dhPtr = std::move(*bigDh));
     339           5 :             }
     340             : 
     341          10 :             *dhPtr = std::move(*dh2Ptr);
     342          10 :             THEN("data is moved and associated Maps are updated")
     343          10 :             {
     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           5 :             }
     351          10 :         }
     352             : 
     353          80 :         WHEN("\"move\" assigning a partial DataHandlerMapCPU through base pointers")
     354          80 :         {
     355          10 :             DataHandler<data_t>* dhPtr = &dh;
     356          10 :             const auto dhCopy = dh;
     357             : 
     358          10 :             auto randVec = generateRandomMatrix<data_t>(2 * size);
     359          10 :             DataHandlerCPU<data_t> dh2{randVec};
     360          10 :             const auto dh2Map = dh2.getBlock(0, size);
     361             : 
     362          10 :             THEN("sizes must match")
     363          10 :             {
     364           5 :                 REQUIRE_THROWS(*dhPtr = std::move(*dh2.getBlock(0, 2 * size)));
     365           5 :             }
     366             : 
     367          10 :             *dhPtr = std::move(*dh2Map);
     368          10 :             THEN("a deep copy is performed")
     369          10 :             {
     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           5 :             }
     376          10 :         }
     377             : 
     378          80 :         WHEN("\"move\" assigning a full DataHandlerMapCPU (aka a view) through base pointers")
     379          80 :         {
     380          10 :             DataHandler<data_t>* dhPtr = &dh;
     381             : 
     382          10 :             auto randVec = generateRandomMatrix<data_t>(size);
     383          10 :             DataHandlerCPU<data_t> dh2{randVec};
     384          10 :             const auto dh2View = dh2.getBlock(0, size);
     385          10 :             const auto dh2Map = dh2.getBlock(size / 2, size / 3);
     386             : 
     387          10 :             THEN("sizes must match")
     388          10 :             {
     389           5 :                 const std::unique_ptr<const DataHandler<data_t>> bigDh =
     390           5 :                     std::make_unique<const DataHandlerCPU<data_t>>(2 * size);
     391           5 :                 REQUIRE_THROWS(*dhPtr = std::move(*bigDh->getBlock(0, 2 * size)));
     392           5 :             }
     393             : 
     394          10 :             *dhPtr = std::move(*dh2View);
     395          10 :             THEN("a shallow copy is performed and associated maps are updated")
     396          10 :             {
     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           5 :             }
     405          10 :         }
     406          80 :     }
     407          80 : }
     408             : 
     409             : #ifdef ELSA_CUDA_VECTOR
     410             : TEST_CASE_TEMPLATE("DataHandlers: Testing clone()", TestType, DataHandlerCPU<float>,
     411             :                    DataHandlerGPU<float>)
     412             : #else
     413             : TEST_CASE_TEMPLATE("DataHandlers: Testing clone()", TestType, DataHandlerCPU<float>)
     414             : #endif
     415           1 : {
     416           1 :     GIVEN("some DataHandler")
     417           1 :     {
     418           1 :         index_t size = 728;
     419           1 :         TestType dh(size);
     420           1 :         dh = 1.0f;
     421             : 
     422           1 :         WHEN("cloning")
     423           1 :         {
     424           1 :             auto dhClone = dh.clone();
     425             : 
     426           1 :             THEN("a shallow copy is produced")
     427           1 :             {
     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           1 :             }
     438           1 :         }
     439           1 :     }
     440           1 : }
     441             : 
     442             : TEST_CASE_TEMPLATE_DEFINE("DataHandlers: Testing the reduction operations", TestType,
     443             :                           datahandler_reduction)
     444          10 : {
     445          10 :     using data_t = typename TestType::value_type;
     446             : 
     447          10 :     GIVEN("some DataHandler")
     448          10 :     {
     449          10 :         index_t size = 16;
     450             : 
     451          10 :         WHEN("putting in some random data")
     452          10 :         {
     453          10 :             auto randVec = generateRandomMatrix<data_t>(size);
     454          10 :             TestType dh(randVec);
     455             : 
     456          10 :             THEN("the reductions work as expected")
     457          10 :             {
     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           5 :                     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           5 :                 auto randVec2 = generateRandomMatrix<data_t>(size);
     468           5 :                 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           5 :             }
     476             : 
     477          10 :             THEN("the dot product expects correctly sized arguments")
     478          10 :             {
     479           5 :                 index_t wrongSize = size - 1;
     480             : 
     481           5 :                 auto randVec2 = generateRandomMatrix<data_t>(wrongSize);
     482           5 :                 TestType dh2(randVec2);
     483             : 
     484           5 :                 REQUIRE_THROWS_AS(dh.dot(dh2), InvalidArgumentError);
     485           5 :             }
     486          10 :         }
     487          10 :     }
     488          10 : }
     489             : 
     490             : TEST_CASE_TEMPLATE_DEFINE("DataHandlers: Testing the element-wise operations", TestType,
     491             :                           datahandler_elementwise)
     492          15 : {
     493          15 :     using data_t = typename TestType::value_type;
     494             : 
     495          15 :     GIVEN("some DataHandler")
     496          15 :     {
     497          15 :         index_t size = 567;
     498             : 
     499          15 :         WHEN("putting in some random data")
     500          15 :         {
     501          15 :             auto randVec = generateRandomMatrix<data_t>(size);
     502          15 :             TestType dh(randVec);
     503             : 
     504          15 :             THEN("the element-wise binary vector operations work as expected")
     505          15 :             {
     506           5 :                 TestType oldDh = dh;
     507             : 
     508           5 :                 auto randVec2 = generateRandomMatrix<data_t>(size);
     509           5 :                 TestType dh2(randVec2);
     510             : 
     511           5 :                 auto dhMap = dh2.getBlock(0, dh2.getSize());
     512             : 
     513           5 :                 TestType bigDh{size + 1};
     514           5 :                 REQUIRE_THROWS(dh += bigDh);
     515           5 :                 REQUIRE_THROWS(dh -= bigDh);
     516           5 :                 REQUIRE_THROWS(dh *= bigDh);
     517           5 :                 REQUIRE_THROWS(dh /= bigDh);
     518             : 
     519           5 :                 dh += dh2;
     520        2840 :                 for (index_t i = 0; i < size; ++i)
     521           5 :                     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           5 :                     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           5 :                     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           5 :                     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           5 :                     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           5 :                     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           5 :             }
     562             : 
     563          15 :             THEN("the element-wise binary scalar operations work as expected")
     564          15 :             {
     565           5 :                 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           5 :                     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           5 :                     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           5 :                     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           5 :                     REQUIRE_UNARY(checkApproxEq(dh[i], oldDh[i] / scalar));
     586           5 :             }
     587             : 
     588          15 :             THEN("the element-wise assignment of a scalar works as expected")
     589          15 :             {
     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           5 :                     REQUIRE_UNARY(checkApproxEq(dh[i], scalar));
     595           5 :             }
     596          15 :         }
     597          15 :     }
     598          15 : }
     599             : 
     600             : TEST_CASE_TEMPLATE_DEFINE("DataHandlers: Testing referencing blocks", TestType,
     601             :                           datahandler_blockreferencing)
     602          10 : {
     603          10 :     using data_t = typename TestType::value_type;
     604             : 
     605          10 :     GIVEN("some DataHandler")
     606          10 :     {
     607          10 :         index_t size = 728;
     608          10 :         Eigen::Matrix<data_t, Eigen::Dynamic, 1> dataVec(size);
     609          10 :         TestType dh(dataVec);
     610             : 
     611          10 :         WHEN("getting the reference to a block")
     612          10 :         {
     613           5 :             REQUIRE_THROWS(dh.getBlock(size, 1));
     614           5 :             REQUIRE_THROWS(dh.getBlock(0, size + 1));
     615             : 
     616           5 :             auto dhBlock = dh.getBlock(size / 3, size / 2);
     617             : 
     618           5 :             THEN("returned data handler references the correct elements")
     619           5 :             {
     620           5 :                 REQUIRE_EQ(dhBlock->getSize(), size / 2);
     621             : 
     622        1825 :                 for (index_t i = 0; i < size / 2; i++)
     623           5 :                     REQUIRE_UNARY(checkApproxEq(&(*dhBlock)[i], &dh[i + size / 3]));
     624           5 :             }
     625           5 :         }
     626             : 
     627          10 :         WHEN("the whole volume is referenced")
     628          10 :         {
     629           5 :             auto dhBlock = dh.getBlock(0, size);
     630             : 
     631           5 :             THEN("the referenced volume and the actual volume are equal")
     632           5 :             {
     633             : 
     634           5 :                 REQUIRE_EQ(dh, *dhBlock);
     635           5 :             }
     636           5 :         }
     637          10 :     }
     638          10 : }
     639             : 
     640             : TEST_CASE_TEMPLATE_DEFINE("DataHandlers: Testing the copy-on-write mechanism", TestType,
     641             :                           datahandler_copyonwrite)
     642          55 : {
     643          55 :     using data_t = typename TestType::value_type;
     644             : 
     645          55 :     const index_t size = 42;
     646             : 
     647          55 :     GIVEN("A random DataContainer")
     648          55 :     {
     649          55 :         auto randVec = generateRandomMatrix<data_t>(size);
     650          55 :         TestType dh{randVec};
     651             : 
     652          55 :         WHEN("const manipulating a copy constructed shallow copy")
     653          55 :         {
     654           5 :             TestType dh2 = dh;
     655             : 
     656           5 :             THEN("the data is the same")
     657           5 :             {
     658           5 :                 REQUIRE_EQ(dh, dh2);
     659           5 :                 REQUIRE_EQ(useCount(dh), 2);
     660           5 :             }
     661           5 :         }
     662             : 
     663          55 :         WHEN("non-const manipulating a copy constructed shallow copy")
     664          55 :         {
     665          45 :             TestType dh2 = dh;
     666          45 :             REQUIRE_EQ(useCount(dh), 2);
     667          45 :             REQUIRE_EQ(useCount(dh2), 2);
     668             : 
     669          45 :             THEN("copy-on-write is invoked")
     670          45 :             {
     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           5 :             }
     676             : 
     677          45 :             THEN("copy-on-write is invoked")
     678          45 :             {
     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           5 :             }
     684             : 
     685          45 :             THEN("copy-on-write is invoked")
     686          45 :             {
     687           5 :                 dh2 -= 2;
     688           5 :                 REQUIRE_NE(dh2, dh);
     689           5 :             }
     690             : 
     691          45 :             THEN("copy-on-write is invoked")
     692          45 :             {
     693           5 :                 dh2 -= dh;
     694           5 :                 REQUIRE_NE(dh2, dh);
     695           5 :             }
     696             : 
     697          45 :             THEN("copy-on-write is invoked")
     698          45 :             {
     699           5 :                 dh2 /= 2;
     700           5 :                 REQUIRE_NE(dh2, dh);
     701           5 :             }
     702             : 
     703          45 :             THEN("copy-on-write is invoked")
     704          45 :             {
     705           5 :                 dh2 /= dh;
     706           5 :                 REQUIRE_NE(dh2, dh);
     707           5 :             }
     708             : 
     709          45 :             THEN("copy-on-write is invoked")
     710          45 :             {
     711           5 :                 dh2 *= 2;
     712           5 :                 REQUIRE_NE(dh2, dh);
     713           5 :             }
     714             : 
     715          45 :             THEN("copy-on-write is invoked")
     716          45 :             {
     717           5 :                 dh2 *= dh;
     718           5 :                 REQUIRE_NE(dh2, dh);
     719           5 :             }
     720             : 
     721          45 :             THEN("copy-on-write is invoked")
     722          45 :             {
     723           5 :                 dh[0] += 2;
     724           5 :                 REQUIRE_NE(dh2, dh);
     725           5 :             }
     726          45 :         }
     727             : 
     728          55 :         WHEN("manipulating a non-shallow-copied container")
     729          55 :         {
     730         215 :             for (index_t i = 0; i < dh.getSize(); ++i) {
     731         210 :                 dh[i] += 2;
     732         210 :             }
     733             : 
     734           5 :             THEN("copy-on-write should not be invoked") { REQUIRE_EQ(useCount(dh), 1); }
     735           5 :         }
     736          55 :     }
     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();

Generated by: LCOV version 1.14