LCOV - code coverage report
Current view: top level - serialization/unittest - Serialization_test.cxx (source / functions) Coverage Total Hit
Test: code.result Lines: 100.0 % 72 72
Test Date: 2025-12-21 13:07:08 Functions: 100.0 % 20 20

            Line data    Source code
       1              : /**
       2              :  * @file Serialization_test.cxx Serialization namespace Unit Tests
       3              :  *
       4              :  * This is part of the DUNE DAQ Application Framework, copyright 2020.
       5              :  * Licensing/copyright details are in the COPYING file that you should have
       6              :  * received with this code.
       7              :  */
       8              : 
       9              : #include "serialization/Serialization.hpp"
      10              : #include "serialization/serialize_variant.hpp"
      11              : 
      12              : #include "logging/Logging.hpp"
      13              : 
      14              : /**
      15              :  * @brief Name of this test module
      16              :  */
      17              : #define BOOST_TEST_MODULE Serialization_test // NOLINT
      18              : 
      19              : #include "boost/test/data/test_case.hpp"
      20              : #include "boost/test/unit_test.hpp"
      21              : 
      22              : #include <string>
      23              : #include <thread>
      24              : #include <vector>
      25              : 
      26              : // A type that's made serializable "intrusively", ie, by changing the type itself
      27              : struct MyTypeIntrusive
      28              : {
      29              :   int count;
      30              :   std::string name;
      31              :   std::vector<double> values;
      32              : 
      33            4 :   DUNE_DAQ_SERIALIZE(MyTypeIntrusive, count, name, values);
      34              : };
      35              : 
      36              : namespace test {
      37              : // A type that's made serializable "intrusively", ie, by changing the type itself
      38              : struct MyTypeNonIntrusive
      39              : {
      40              :   float a_float;
      41              :   std::vector<int> values;
      42              : };
      43              : 
      44              : } // namespace test
      45              : 
      46            2 : DUNE_DAQ_SERIALIZE_NON_INTRUSIVE(test, MyTypeNonIntrusive, a_float, values)
      47              : 
      48              : enum Fakeness
      49              : {
      50              :   Unknown,
      51              :   Fake,
      52              :   SuperFake
      53              : };
      54      2000000 : DUNE_DAQ_SERIALIZE_ENUM(Fakeness)
      55              : 
      56              : struct FakeData
      57              : {
      58              :   int32_t fake_count;
      59              : 
      60     40000000 :   DUNE_DAQ_SERIALIZE(FakeData, fake_count);
      61              : };
      62              : 
      63              : struct AnotherFakeData
      64              : {
      65              :   int32_t fake_count;
      66              :   int64_t fake_timestamp;
      67              :   std::vector<FakeData> fake_datas;
      68              :   Fakeness fakeness;
      69              : 
      70      2000000 :   DUNE_DAQ_SERIALIZE(AnotherFakeData, fake_count, fake_timestamp, fake_datas, fakeness);
      71              : };
      72              : 
      73              : BOOST_AUTO_TEST_SUITE(Serialization_test)
      74              : 
      75              : /**
      76              :  * @brief Check that we can serialize -> deserialize and get back what we started with
      77              :  */
      78            2 : BOOST_DATA_TEST_CASE(SerializationRoundTrip, boost::unit_test::data::make({ dunedaq::serialization::kMsgPack }))
      79              : {
      80              : 
      81            1 :   MyTypeIntrusive m;
      82            1 :   m.count = 3;
      83            1 :   m.name = "foo";
      84            1 :   m.values.push_back(3.1416);
      85            1 :   m.values.push_back(2.781);
      86              : 
      87              :   namespace ser = dunedaq::serialization;
      88              : 
      89            1 :   std::vector<uint8_t> bytes = ser::serialize(m, sample); // NOLINT(build/unsigned)
      90            1 :   MyTypeIntrusive m_recv = ser::deserialize<MyTypeIntrusive>(bytes);
      91            1 :   BOOST_CHECK_EQUAL(m_recv.count, m.count);
      92            1 :   BOOST_CHECK_EQUAL(m_recv.name, m.name);
      93            1 :   BOOST_CHECK_EQUAL_COLLECTIONS(m_recv.values.begin(), m_recv.values.end(), m.values.begin(), m.values.end());
      94            1 : }
      95              : 
      96            2 : BOOST_DATA_TEST_CASE(SerializeVariant, boost::unit_test::data::make({ dunedaq::serialization::kMsgPack }))
      97              : {
      98            1 :   MyTypeIntrusive m;
      99            1 :   m.count = 3;
     100            1 :   m.name = "foo";
     101            1 :   m.values.push_back(3.1416);
     102            1 :   m.values.push_back(2.781);
     103              : 
     104              :   namespace ser = dunedaq::serialization;
     105              : 
     106              :   using VariantType = std::variant<MyTypeIntrusive, test::MyTypeNonIntrusive>;
     107              : 
     108              :   {
     109            1 :     VariantType v = m;
     110            1 :     std::vector<uint8_t> bytes = ser::serialize(v, sample); // NOLINT(build/unsigned)
     111              :     // std::cout << "Serialized bytes: ";
     112              :     // for(auto& byte : bytes) std::cout << byte << " ";
     113              :     // std::cout << std::endl;
     114            1 :     VariantType v_recv = ser::deserialize<VariantType>(bytes);
     115            1 :     BOOST_CHECK_EQUAL(v_recv.index(), v.index());
     116            1 :     MyTypeIntrusive mv_recv = std::get<MyTypeIntrusive>(v_recv);
     117            1 :     BOOST_CHECK_EQUAL(mv_recv.count, m.count);
     118            1 :     BOOST_CHECK_EQUAL(mv_recv.name, m.name);
     119            1 :     BOOST_CHECK_EQUAL_COLLECTIONS(mv_recv.values.begin(), mv_recv.values.end(), m.values.begin(), m.values.end());
     120            1 :   }
     121              : 
     122              :   {
     123            1 :     test::MyTypeNonIntrusive m2;
     124            1 :     m2.a_float = 1.0;
     125            1 :     m2.values = { 1, 2, 3 };
     126              : 
     127            1 :     VariantType v = m2;
     128            1 :     std::vector<uint8_t> bytes = ser::serialize(v, sample); // NOLINT(build/unsigned)
     129              :     // std::cout << "Serialized bytes: ";
     130              :     // for(auto& byte : bytes) std::cout << byte << " ";
     131              :     // std::cout << std::endl;
     132            1 :     VariantType v_recv = ser::deserialize<VariantType>(bytes);
     133            1 :     BOOST_CHECK_EQUAL(v_recv.index(), v.index());
     134            1 :     test::MyTypeNonIntrusive mv_recv = std::get<test::MyTypeNonIntrusive>(v_recv);
     135            1 :     BOOST_CHECK_EQUAL(mv_recv.a_float, m2.a_float);
     136            1 :     BOOST_CHECK_EQUAL_COLLECTIONS(mv_recv.values.begin(), mv_recv.values.end(), m2.values.begin(), m2.values.end());
     137            1 :   }
     138            1 : }
     139              : 
     140            2 : BOOST_AUTO_TEST_CASE(InvalidSerializationTypes)
     141              : {
     142            3 :   BOOST_CHECK_THROW(dunedaq::serialization::from_string("not a real type"),
     143              :                     dunedaq::serialization::UnknownSerializationTypeString);
     144              : 
     145              :   // The first byte, which indicates the message serialization type,
     146              :   // should be 'M' or 'J': check we get an exception when it's not
     147            1 :   std::vector<char> invalid_message = { '0', '2', '3', '4' };
     148            2 :   BOOST_CHECK_THROW(dunedaq::serialization::deserialize<int>(invalid_message),
     149              :                     dunedaq::serialization::UnknownSerializationTypeByte);
     150              : 
     151              :   // An invalid msgpack message: we have our serialization type byte,
     152              :   // 'M', followed by 0xce, which indicates that a four-byte integer
     153              :   // follows. But we only have two more bytes after that, so the
     154              :   // message is invalid
     155            1 :   std::vector<unsigned char> invalid_msgpack_message = { 'M', 0xce, 0x0, 0x0 };
     156            2 :   BOOST_CHECK_THROW(dunedaq::serialization::deserialize<int>(invalid_msgpack_message),
     157              :                     dunedaq::serialization::CannotDeserializeMessage);
     158            1 : }
     159              : 
     160            2 : BOOST_DATA_TEST_CASE(SerializationSpeedTest, boost::unit_test::data::make({ dunedaq::serialization::kMsgPack }))
     161              : {
     162            1 :   const int N = 1000000;
     163            1 :   int total = 0;
     164            1 :   auto start_time = std::chrono::steady_clock::now();
     165            1 :   AnotherFakeData fd;
     166           21 :   for (int i = 0; i < 20; ++i) {
     167           20 :     fd.fake_datas.push_back(FakeData{ 3 });
     168              :   }
     169      1000001 :   for (int i = 0; i < N; ++i) {
     170      1000000 :     fd.fake_count = i;
     171      1000000 :     fd.fakeness = Fakeness::SuperFake;
     172      1000000 :     std::vector<uint8_t> bytes = dunedaq::serialization::serialize(fd, sample); // NOLINT(build/unsigned)
     173      1000000 :     AnotherFakeData fd_recv = dunedaq::serialization::deserialize<AnotherFakeData>(bytes);
     174      1000000 :     total += fd_recv.fake_count;
     175              :   }
     176            3 :   TLOG(TLVL_INFO) << "total: " << total;
     177            1 :   auto end_time = std::chrono::steady_clock::now();
     178            1 :   double time_taken_s =
     179            1 :     std::chrono::duration_cast<std::chrono::duration<double, std::ratio<1>>>(end_time - start_time).count();
     180            1 :   double kHz = 1e-3 * N / time_taken_s;
     181            3 :   TLOG(TLVL_INFO) << "Sent " << N << " messages in " << time_taken_s << "s (" << kHz << " kHz)";
     182            1 :   BOOST_REQUIRE(true);
     183            1 : }
     184              : 
     185              : BOOST_AUTO_TEST_SUITE_END()
        

Generated by: LCOV version 2.0-1