Line data Source code
1 : /**
2 : * @file Fragment_test.cxx Fragment class 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 : // Disable warnings in light of the use of intentially bad constructors during testing
10 :
11 : #pragma GCC diagnostic ignored "-Warray-bounds"
12 : #pragma GCC diagnostic ignored "-Wstringop-overflow="
13 : #pragma GCC diagnostic ignored "-Walloc-size-larger-than="
14 :
15 : #include "daqdataformats/Fragment.hpp"
16 :
17 : #pragma GCC diagnostic pop
18 : #pragma GCC diagnostic pop
19 : #pragma GCC diagnostic pop
20 :
21 : /**
22 : * @brief Name of this test module
23 : */
24 : #define BOOST_TEST_MODULE Fragment_test // NOLINT
25 :
26 : #include "boost/test/unit_test.hpp"
27 :
28 : #include <memory>
29 : #include <string>
30 : #include <utility>
31 : #include <vector>
32 :
33 : using namespace dunedaq::daqdataformats;
34 :
35 : BOOST_AUTO_TEST_SUITE(Fragment_test)
36 :
37 : /**
38 : * @brief Check that Fragments have appropriate Copy/Move semantics
39 : */
40 2 : BOOST_AUTO_TEST_CASE(CopyAndMoveSemantics)
41 : {
42 1 : BOOST_REQUIRE(!std::is_copy_constructible_v<Fragment>);
43 1 : BOOST_REQUIRE(!std::is_copy_assignable_v<Fragment>);
44 1 : BOOST_REQUIRE(std::is_move_constructible_v<Fragment>);
45 1 : BOOST_REQUIRE(std::is_move_assignable_v<Fragment>);
46 1 : }
47 :
48 : /**
49 : * @brief Test constructors that gather existing data buffers
50 : */
51 2 : BOOST_AUTO_TEST_CASE(DataConstructors)
52 : {
53 1 : std::vector<uint8_t> buf1(10);
54 1 : Fragment single_frag(buf1.data(), buf1.size());
55 1 : BOOST_REQUIRE_EQUAL(single_frag.get_size(), sizeof(FragmentHeader) + buf1.size());
56 :
57 1 : std::vector<uint8_t> buf2(20);
58 1 : Fragment collect_frag({ { buf1.data(), buf1.size() }, { buf2.data(), buf2.size() } });
59 1 : BOOST_REQUIRE_EQUAL(collect_frag.get_size(), sizeof(FragmentHeader) + buf1.size() + buf2.size());
60 1 : }
61 :
62 : /**
63 : * @brief Test construction of invalid Fragments
64 : */
65 2 : BOOST_AUTO_TEST_CASE(BadConstructors)
66 : {
67 1 : std::unique_ptr<Fragment> fragment_ptr{};
68 :
69 4 : BOOST_REQUIRE_EXCEPTION(fragment_ptr.reset(new Fragment(nullptr, size_t(100))),
70 : std::invalid_argument,
71 : [&](std::invalid_argument) { return true; });
72 4 : BOOST_REQUIRE_EXCEPTION(
73 : fragment_ptr.reset(new Fragment(nullptr, size_t(-1))), std::length_error, [&](std::length_error) { return true; });
74 :
75 4 : BOOST_REQUIRE_EXCEPTION(
76 : fragment_ptr.reset(new Fragment({ nullptr, size_t(-1) - sizeof(dunedaq::daqdataformats::FragmentHeader) })),
77 : std::bad_alloc,
78 : [&](std::bad_alloc) { return true; });
79 :
80 1 : auto bufsize = 10;
81 1 : std::vector<uint8_t> buf1(bufsize);
82 1 : fragment_ptr.reset(new Fragment(buf1.data(), buf1.size()));
83 1 : BOOST_REQUIRE_EQUAL(fragment_ptr->get_size(), sizeof(FragmentHeader) + bufsize);
84 1 : }
85 :
86 : /**
87 : * @brief Check that Fragment constructors function correctly
88 : */
89 2 : BOOST_AUTO_TEST_CASE(ExistingFragmentConstructor)
90 : {
91 1 : FragmentHeader header;
92 1 : header.size = sizeof(FragmentHeader) + 4;
93 1 : header.trigger_number = 1;
94 1 : header.trigger_timestamp = 2;
95 1 : header.run_number = 3;
96 :
97 1 : auto frag = malloc(sizeof(FragmentHeader) + 4);
98 1 : memcpy(frag, &header, sizeof(FragmentHeader));
99 :
100 1 : uint8_t one = 1, two = 2, three = 3, four = 4; // NOLINT(build/unsigned)
101 1 : memcpy(static_cast<uint8_t*>(frag) + sizeof(FragmentHeader), &one, 1); // NOLINT(build/unsigned)
102 1 : memcpy(static_cast<uint8_t*>(frag) + sizeof(FragmentHeader) + 1, &two, 1); // NOLINT(build/unsigned)
103 1 : memcpy(static_cast<uint8_t*>(frag) + sizeof(FragmentHeader) + 2, &three, 1); // NOLINT(build/unsigned)
104 1 : memcpy(static_cast<uint8_t*>(frag) + sizeof(FragmentHeader) + 3, &four, 1); // NOLINT(build/unsigned)
105 :
106 1 : {
107 1 : Fragment test_frag(frag, Fragment::BufferAdoptionMode::kReadOnlyMode);
108 :
109 1 : BOOST_REQUIRE_EQUAL(test_frag.get_storage_location(), frag);
110 :
111 1 : BOOST_REQUIRE_EQUAL(test_frag.get_trigger_number(), 1);
112 1 : BOOST_REQUIRE_EQUAL(test_frag.get_trigger_timestamp(), 2);
113 1 : BOOST_REQUIRE_EQUAL(test_frag.get_run_number(), 3);
114 :
115 1 : BOOST_REQUIRE_EQUAL(*static_cast<uint8_t*>(test_frag.get_data()), one); // NOLINT(build/unsigned)
116 1 : BOOST_REQUIRE_EQUAL(*(static_cast<uint8_t*>(test_frag.get_data()) + 1), two); // NOLINT(build/unsigned)
117 1 : BOOST_REQUIRE_EQUAL(*(static_cast<uint8_t*>(test_frag.get_data()) + 2), three); // NOLINT(build/unsigned)
118 1 : BOOST_REQUIRE_EQUAL(*(static_cast<uint8_t*>(test_frag.get_data()) + 3), four); // NOLINT(build/unsigned)
119 1 : }
120 1 : free(frag); // Should not cause errors
121 :
122 1 : frag = malloc(sizeof(FragmentHeader) + 4);
123 1 : memcpy(frag, &header, sizeof(FragmentHeader));
124 1 : memcpy(static_cast<uint8_t*>(frag) + sizeof(FragmentHeader), &four, 1); // NOLINT(build/unsigned)
125 1 : memcpy(static_cast<uint8_t*>(frag) + sizeof(FragmentHeader) + 1, &three, 1); // NOLINT(build/unsigned)
126 1 : memcpy(static_cast<uint8_t*>(frag) + sizeof(FragmentHeader) + 2, &two, 1); // NOLINT(build/unsigned)
127 1 : memcpy(static_cast<uint8_t*>(frag) + sizeof(FragmentHeader) + 3, &one, 1); // NOLINT(build/unsigned)
128 :
129 1 : {
130 1 : Fragment test_frag(frag, Fragment::BufferAdoptionMode::kCopyFromBuffer);
131 :
132 1 : BOOST_REQUIRE(test_frag.get_storage_location() != frag);
133 1 : BOOST_REQUIRE_EQUAL(test_frag.get_trigger_number(), 1);
134 1 : BOOST_REQUIRE_EQUAL(test_frag.get_trigger_timestamp(), 2);
135 1 : BOOST_REQUIRE_EQUAL(test_frag.get_run_number(), 3);
136 :
137 1 : BOOST_REQUIRE_EQUAL(*static_cast<uint8_t*>(test_frag.get_data()), four); // NOLINT(build/unsigned)
138 1 : BOOST_REQUIRE_EQUAL(*(static_cast<uint8_t*>(test_frag.get_data()) + 1), three); // NOLINT(build/unsigned)
139 1 : BOOST_REQUIRE_EQUAL(*(static_cast<uint8_t*>(test_frag.get_data()) + 2), two); // NOLINT(build/unsigned)
140 1 : BOOST_REQUIRE_EQUAL(*(static_cast<uint8_t*>(test_frag.get_data()) + 3), one); // NOLINT(build/unsigned)
141 1 : }
142 :
143 1 : {
144 1 : using blobtype_t = uint8_t; // NOLINT(build/unsigned)
145 :
146 1 : constexpr int blob1_num_elements = 123;
147 1 : constexpr int blob2_num_elements = 456;
148 1 : std::array<blobtype_t, blob1_num_elements> blob1;
149 1 : std::array<blobtype_t, blob2_num_elements> blob2;
150 :
151 1 : Fragment test_frag(
152 1 : std::vector<std::pair<void*, size_t>>({ { &blob1[0], blob1_num_elements }, { &blob2[0], blob2_num_elements } }));
153 :
154 1 : BOOST_REQUIRE_EQUAL(sizeof(FragmentHeader) + blob1_num_elements + blob2_num_elements, test_frag.get_size());
155 1 : }
156 :
157 1 : free(frag); // Should not cause errors
158 1 : }
159 :
160 2 : BOOST_AUTO_TEST_CASE(BadExistingFragmentConstructor)
161 : {
162 1 : FragmentHeader header;
163 1 : header.size = -1;
164 1 : header.trigger_number = 1;
165 1 : header.trigger_timestamp = 2;
166 1 : header.run_number = 3;
167 :
168 1 : auto frag = malloc(sizeof(FragmentHeader) + 4);
169 1 : memcpy(frag, &header, sizeof(FragmentHeader));
170 :
171 1 : std::unique_ptr<Fragment> fragment_ptr{};
172 4 : BOOST_REQUIRE_EXCEPTION(fragment_ptr.reset(new Fragment(frag, Fragment::BufferAdoptionMode::kCopyFromBuffer)),
173 : std::bad_alloc,
174 : [&](std::bad_alloc) { return true; });
175 1 : free(frag);
176 :
177 : // Use fragment_ptr
178 1 : auto bufsize = 10;
179 1 : auto buf1 = malloc(bufsize);
180 1 : fragment_ptr.reset(new Fragment(buf1, size_t(bufsize)));
181 1 : BOOST_REQUIRE_EQUAL(fragment_ptr->get_size(), sizeof(FragmentHeader) + bufsize);
182 1 : }
183 :
184 2 : BOOST_AUTO_TEST_CASE(MoveConstructor)
185 : {
186 1 : auto buf1 = malloc(10);
187 1 : auto single_frag = new Fragment(buf1, size_t(10));
188 1 : BOOST_REQUIRE_EQUAL(single_frag->get_size(), sizeof(FragmentHeader) + 10);
189 :
190 1 : Fragment another_frag(std::move(*single_frag));
191 :
192 1 : delete single_frag; // NOLINT We are specifically testing what happens when the original frag is deleted
193 :
194 1 : BOOST_REQUIRE_EQUAL(another_frag.get_size(), sizeof(FragmentHeader) + 10);
195 1 : }
196 :
197 2 : BOOST_AUTO_TEST_CASE(MoveAssignment)
198 : {
199 1 : auto buf1 = malloc(10);
200 1 : auto single_frag = new Fragment(buf1, size_t(10));
201 1 : BOOST_REQUIRE_EQUAL(single_frag->get_size(), sizeof(FragmentHeader) + 10);
202 :
203 1 : auto another_frag = std::move(*single_frag);
204 :
205 1 : delete single_frag; // NOLINT We are specifically testing what happens when the original frag is deleted
206 :
207 1 : BOOST_REQUIRE_EQUAL(another_frag.get_size(), sizeof(FragmentHeader) + 10);
208 1 : }
209 :
210 : /**
211 : * @brief Test header field manipulation methods
212 : */
213 2 : BOOST_AUTO_TEST_CASE(HeaderFields)
214 : {
215 :
216 1 : FragmentHeader header;
217 1 : header.size = sizeof(FragmentHeader) + 4;
218 1 : header.trigger_number = 1;
219 1 : header.trigger_timestamp = 2;
220 1 : header.run_number = 3;
221 1 : header.window_begin = 4;
222 1 : header.window_end = 5;
223 :
224 1 : SourceID component{ SourceID::Subsystem::kDetectorReadout, 123456789 };
225 1 : header.element_id = component;
226 :
227 1 : header.error_bits = 0x12345678;
228 1 : header.fragment_type = 8;
229 1 : header.sequence_number = 9;
230 :
231 1 : auto buf1 = malloc(10);
232 1 : Fragment frag(buf1, size_t(10));
233 1 : BOOST_REQUIRE_EQUAL(frag.get_size(), sizeof(FragmentHeader) + 10);
234 :
235 1 : frag.set_header_fields(header);
236 1 : BOOST_REQUIRE_EQUAL(frag.get_size(), sizeof(FragmentHeader) + 10);
237 1 : BOOST_REQUIRE_EQUAL(frag.get_header().run_number, header.run_number);
238 1 : BOOST_REQUIRE_EQUAL(frag.get_trigger_number(), header.trigger_number);
239 1 : BOOST_REQUIRE_EQUAL(frag.get_run_number(), header.run_number);
240 1 : BOOST_REQUIRE_EQUAL(frag.get_trigger_timestamp(), header.trigger_timestamp);
241 1 : BOOST_REQUIRE_EQUAL(frag.get_window_begin(), header.window_begin);
242 1 : BOOST_REQUIRE_EQUAL(frag.get_window_end(), header.window_end);
243 1 : BOOST_REQUIRE_EQUAL(frag.get_element_id(), header.element_id);
244 :
245 1 : BOOST_REQUIRE_EQUAL(frag.get_error_bits().to_ulong(), header.error_bits);
246 1 : BOOST_REQUIRE_EQUAL(frag.get_error_bit(static_cast<FragmentErrorBits>(3)), true);
247 :
248 1 : BOOST_REQUIRE_EQUAL(frag.get_fragment_type_code(), header.fragment_type);
249 1 : BOOST_REQUIRE_EQUAL(static_cast<fragment_type_t>(frag.get_fragment_type()), header.fragment_type);
250 :
251 1 : BOOST_REQUIRE_EQUAL(frag.get_sequence_number(), header.sequence_number);
252 :
253 1 : auto theHeader = static_cast<const FragmentHeader*>(frag.get_storage_location());
254 1 : frag.set_trigger_number(0x11);
255 1 : BOOST_REQUIRE_EQUAL(theHeader->trigger_number, 0x11);
256 1 : frag.set_run_number(0x33);
257 1 : BOOST_REQUIRE_EQUAL(theHeader->run_number, 0x33);
258 1 : frag.set_trigger_timestamp(0x22);
259 1 : BOOST_REQUIRE_EQUAL(theHeader->trigger_timestamp, 0x22);
260 1 : frag.set_window_begin(0x44);
261 1 : BOOST_REQUIRE_EQUAL(theHeader->window_begin, 0x44);
262 1 : frag.set_window_end(0x55);
263 1 : BOOST_REQUIRE_EQUAL(theHeader->window_end, 0x55);
264 1 : frag.set_type(static_cast<FragmentType>(0x88));
265 1 : BOOST_REQUIRE_EQUAL(theHeader->fragment_type, 0x88);
266 1 : frag.set_sequence_number(0x99);
267 1 : BOOST_REQUIRE_EQUAL(theHeader->sequence_number, 0x99);
268 :
269 1 : SourceID new_component{ SourceID::Subsystem::kDetectorReadout, 0x6677 };
270 1 : frag.set_element_id(new_component);
271 1 : BOOST_REQUIRE_EQUAL(theHeader->element_id.subsystem, SourceID::Subsystem::kDetectorReadout);
272 1 : BOOST_REQUIRE_EQUAL(theHeader->element_id.id, 0x6677);
273 :
274 1 : std::bitset<32> no_errors(0);
275 1 : frag.set_error_bits(no_errors);
276 1 : BOOST_REQUIRE_EQUAL(theHeader->error_bits, 0);
277 1 : frag.set_error_bit(static_cast<FragmentErrorBits>(1), true);
278 1 : BOOST_REQUIRE_EQUAL(theHeader->error_bits, 2);
279 1 : frag.set_error_bit(static_cast<FragmentErrorBits>(1), false);
280 1 : BOOST_REQUIRE_EQUAL(theHeader->error_bits, 0);
281 1 : }
282 :
283 : BOOST_AUTO_TEST_SUITE_END()
|