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()
|