Line data Source code
1 : // Copyright (c) 2025 Made to Order Software Corp. All Rights Reserved
2 : //
3 : // https://snapwebsites.org/project/prinbee
4 : // contact@m2osw.com
5 : //
6 : // This program is free software: you can redistribute it and/or modify
7 : // it under the terms of the GNU General Public License as published by
8 : // the Free Software Foundation, either version 3 of the License, or
9 : // (at your option) any later version.
10 : //
11 : // This program is distributed in the hope that it will be useful,
12 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : // GNU General Public License for more details.
15 : //
16 : // You should have received a copy of the GNU General Public License
17 : // along with this program. If not, see <https://www.gnu.org/licenses/>.
18 :
19 : // self
20 : //
21 : #include "catch_main.h"
22 :
23 :
24 : // prinbee
25 : //
26 : #include <prinbee/network/binary_client.h>
27 : #include <prinbee/network/binary_message.h>
28 : #include <prinbee/network/crc16.h>
29 :
30 :
31 :
32 : // advgetopt
33 : //
34 : //#include <advgetopt/options.h>
35 :
36 :
37 : // snapdev
38 : //
39 : //#include <snapdev/enum_class_math.h>
40 : #include <snapdev/raii_generic_deleter.h>
41 :
42 :
43 : // eventdispatcher
44 : //
45 : #include <eventdispatcher/communicator.h>
46 : //#include <eventdispatcher/dispatcher.h>
47 : //#include <eventdispatcher/names.h>
48 : //#include <eventdispatcher/tcp_client_permanent_message_connection.h>
49 :
50 : #include <eventdispatcher/reporter/executor.h>
51 : //#include <eventdispatcher/reporter/lexer.h>
52 : #include <eventdispatcher/reporter/parser.h>
53 : //#include <eventdispatcher/reporter/state.h>
54 : //#include <eventdispatcher/reporter/variable_string.h>
55 :
56 :
57 : // C++
58 : //
59 : //#include <iomanip>
60 :
61 :
62 : // last include
63 : //
64 : #include <snapdev/poison.h>
65 :
66 :
67 :
68 : namespace
69 : {
70 :
71 :
72 :
73 1 : addr::addr get_address()
74 : {
75 1 : addr::addr a;
76 1 : sockaddr_in ip = {
77 : .sin_family = AF_INET,
78 1 : .sin_port = htons(20002),
79 : .sin_addr = {
80 1 : .s_addr = htonl(0x7f000001),
81 : },
82 : .sin_zero = {},
83 1 : };
84 1 : a.set_ipv4(ip);
85 2 : return a;
86 0 : }
87 :
88 :
89 : class binary_client_test
90 : : public prinbee::binary_client
91 : {
92 : public:
93 1 : binary_client_test(addr::addr const & a)
94 1 : : binary_client(a)
95 : {
96 1 : }
97 :
98 0 : virtual void process_message(prinbee::binary_message & msg) override
99 : {
100 0 : throw std::runtime_error("boom -- " + std::to_string(msg.get_data_size()));
101 : }
102 :
103 1 : virtual void process_connected() override
104 : {
105 2 : SNAP_LOG_ERROR << "--------- process connected!" << SNAP_LOG_SEND;
106 1 : prinbee::binary_message msg;
107 1 : msg.set_name(prinbee::g_message_ping);
108 1 : send_message(msg);
109 :
110 : // important, we need to call this one to disable the timer otherwise
111 : // we'll try to reconnect over and over again
112 : //
113 1 : binary_client::process_connected();
114 2 : }
115 :
116 : private:
117 : };
118 :
119 :
120 :
121 : }
122 : // no name namespace
123 :
124 :
125 2 : CATCH_TEST_CASE("network_crc16", "[crc16][valid][invalid]")
126 : {
127 4 : CATCH_START_SECTION("network_crc16: verify empty buffer")
128 : {
129 1 : std::vector<std::uint8_t> data;
130 1 : std::uint16_t const crc16(prinbee::crc16_compute(data.data(), data.size()));
131 1 : CATCH_REQUIRE(crc16 == 0);
132 1 : data.push_back(0);
133 1 : data.push_back(0);
134 1 : CATCH_REQUIRE(prinbee::crc16_compute(data.data(), data.size()) == 0);
135 1 : }
136 3 : CATCH_END_SECTION()
137 :
138 4 : CATCH_START_SECTION("network_crc16: verify negation")
139 : {
140 1 : std::size_t const size(rand() % 64536 + 1024);
141 3 : std::vector<std::uint8_t> data(size);
142 17503 : for(std::size_t i(0); i < size; ++i)
143 : {
144 17502 : data[i] = rand();
145 : }
146 1 : std::uint16_t const crc16(prinbee::crc16_compute(data.data(), data.size()));
147 :
148 : // test against all values
149 : //
150 1 : data.push_back(0);
151 1 : data.push_back(0);
152 65537 : for(std::uint32_t check(0); check < 0x10000; ++check)
153 : {
154 65536 : data[data.size() - 2] = check;
155 65536 : data[data.size() - 1] = check >> 8;
156 65536 : if(check == crc16)
157 : {
158 : // only one that works
159 : //
160 1 : CATCH_REQUIRE(prinbee::crc16_compute(data.data(), data.size()) == 0);
161 : }
162 : else
163 : {
164 65535 : CATCH_REQUIRE_FALSE(prinbee::crc16_compute(data.data(), data.size()) == 0);
165 : }
166 : }
167 1 : }
168 3 : CATCH_END_SECTION()
169 :
170 : // this doesn't work--the CRC16 has to be at the end
171 : //CATCH_START_SECTION("network_crc16: verify negation inserting CRC16 at the start")
172 : //{
173 : // std::size_t const size(rand() % 64536 + 1024);
174 : // std::vector<std::uint8_t> data(size);
175 : // for(std::size_t i(0); i < size; ++i)
176 : // {
177 : // data[i] = rand();
178 : // }
179 : // std::uint16_t const crc16(prinbee::crc16_compute(data.data(), data.size()));
180 : // data.insert(data.begin(), crc16);
181 : // data.insert(data.begin(), crc16 >> 8);
182 : // CATCH_REQUIRE(prinbee::crc16_compute(data.data(), data.size()) == 0);
183 : //}
184 : //CATCH_END_SECTION()
185 2 : }
186 :
187 :
188 5 : CATCH_TEST_CASE("network_message", "[network][message][valid]")
189 : {
190 7 : CATCH_START_SECTION("network_message: verify name")
191 : {
192 1 : prinbee::message_name_t one(prinbee::create_message_name("1"));
193 1 : char const * p1(reinterpret_cast<char const *>(&one));
194 1 : CATCH_REQUIRE(p1[0] == '1');
195 1 : CATCH_REQUIRE(p1[1] == '\0');
196 1 : CATCH_REQUIRE(p1[2] == '\0');
197 1 : CATCH_REQUIRE(p1[3] == '\0');
198 :
199 1 : prinbee::message_name_t two(prinbee::create_message_name("!?"));
200 1 : char const * p2(reinterpret_cast<char const *>(&two));
201 1 : CATCH_REQUIRE(p2[0] == '!');
202 1 : CATCH_REQUIRE(p2[1] == '?');
203 1 : CATCH_REQUIRE(p2[2] == '\0');
204 1 : CATCH_REQUIRE(p2[3] == '\0');
205 :
206 1 : prinbee::message_name_t abc(prinbee::create_message_name("ABC"));
207 1 : char const * p3(reinterpret_cast<char const *>(&abc));
208 1 : CATCH_REQUIRE(p3[0] == 'A');
209 1 : CATCH_REQUIRE(p3[1] == 'B');
210 1 : CATCH_REQUIRE(p3[2] == 'C');
211 1 : CATCH_REQUIRE(p3[3] == '\0');
212 :
213 1 : prinbee::message_name_t name(prinbee::create_message_name("NAME"));
214 1 : char const * p4(reinterpret_cast<char const *>(&name));
215 1 : CATCH_REQUIRE(p4[0] == 'N');
216 1 : CATCH_REQUIRE(p4[1] == 'A');
217 1 : CATCH_REQUIRE(p4[2] == 'M');
218 1 : CATCH_REQUIRE(p4[3] == 'E');
219 : }
220 6 : CATCH_END_SECTION()
221 :
222 7 : CATCH_START_SECTION("network_message: check defaults")
223 : {
224 1 : prinbee::binary_message msg;
225 :
226 1 : CATCH_REQUIRE(msg.get_name() == prinbee::g_message_unknown);
227 :
228 1 : CATCH_REQUIRE_FALSE(msg.has_pointer());
229 :
230 1 : std::size_t size(0);
231 1 : CATCH_REQUIRE_FALSE(msg.get_data_pointer(size));
232 1 : CATCH_REQUIRE(size == 0);
233 :
234 1 : CATCH_REQUIRE(msg.get_data().empty());
235 1 : }
236 6 : CATCH_END_SECTION()
237 :
238 7 : CATCH_START_SECTION("network_message: check name")
239 : {
240 1 : prinbee::binary_message msg;
241 :
242 1 : CATCH_REQUIRE(msg.get_name() == prinbee::g_message_unknown);
243 :
244 101 : for(int i(0); i < 100; ++i)
245 : {
246 100 : std::stringstream ss;
247 100 : ss << "i" << i;
248 100 : msg.set_name(prinbee::create_message_name(ss.str().c_str()));
249 :
250 100 : char name[4] = {};
251 100 : int p(0);
252 100 : name[p++] = 'i';
253 100 : if(i >= 10)
254 : {
255 90 : name[p++] = i / 10 + '0';
256 : }
257 100 : name[p++] = i % 10 + '0';
258 100 : prinbee::message_name_t expected = *reinterpret_cast<std::uint32_t *>(name);
259 100 : CATCH_REQUIRE(msg.get_name() == expected);
260 100 : }
261 :
262 1 : std::size_t size(0);
263 1 : CATCH_REQUIRE_FALSE(msg.get_data_pointer(size));
264 1 : CATCH_REQUIRE(size == 0);
265 :
266 1 : CATCH_REQUIRE(msg.get_data().empty());
267 1 : }
268 6 : CATCH_END_SECTION()
269 :
270 7 : CATCH_START_SECTION("network_message: check pointer")
271 : {
272 1 : prinbee::binary_message msg;
273 :
274 1 : CATCH_REQUIRE_FALSE(msg.has_pointer());
275 :
276 1 : std::size_t const size(rand() % 1000 + 10);
277 1 : void * ptr(malloc(size));
278 1 : CATCH_REQUIRE(ptr != nullptr);
279 :
280 : // we're responsible for that pointer...
281 : // (this is ugly, but that way we avoid one copy per message, some
282 : // of which are really large)
283 : //
284 1 : snapdev::raii_buffer_t auto_free(reinterpret_cast<char *>(ptr));
285 :
286 1 : msg.set_data_by_pointer(ptr, size);
287 :
288 1 : CATCH_REQUIRE(msg.has_pointer());
289 :
290 1 : std::size_t sz(0);
291 1 : CATCH_REQUIRE(msg.get_data_pointer(sz) == ptr);
292 1 : CATCH_REQUIRE(sz == size);
293 :
294 : // if we have a pointer, there is no data buffer
295 : //
296 1 : CATCH_REQUIRE(msg.get_data().size() == 0);
297 1 : }
298 6 : CATCH_END_SECTION()
299 :
300 7 : CATCH_START_SECTION("network_message: check data")
301 : {
302 1 : prinbee::binary_message msg;
303 :
304 1 : CATCH_REQUIRE_FALSE(msg.has_pointer());
305 :
306 1 : std::size_t const size(rand() % 1000 + 10);
307 1 : void * ptr(malloc(size));
308 1 : CATCH_REQUIRE(ptr != nullptr);
309 :
310 : // we're responsible for that pointer...
311 : // (this is ugly, but that way we avoid one copy per message, some
312 : // of which are really large)
313 : //
314 1 : snapdev::raii_buffer_t auto_free(reinterpret_cast<char *>(ptr));
315 :
316 1 : msg.set_data(ptr, size);
317 :
318 1 : std::vector<std::uint8_t> data(msg.get_data());
319 1 : CATCH_REQUIRE(data.size() == size);
320 1 : CATCH_REQUIRE(memcmp(data.data(), ptr, size) == 0);
321 :
322 : // if we a buffer, there is no pointer
323 : //
324 1 : CATCH_REQUIRE_FALSE(msg.has_pointer());
325 1 : }
326 6 : CATCH_END_SECTION()
327 5 : }
328 :
329 :
330 3 : CATCH_TEST_CASE("network_message_invalid", "[network][message][invalid]")
331 : {
332 5 : CATCH_START_SECTION("network_message_invalid: the nullptr string is not a valid name")
333 : {
334 3 : CATCH_REQUIRE_THROWS_MATCHES(
335 : prinbee::create_message_name(nullptr)
336 : , prinbee::invalid_parameter
337 : , Catch::Matchers::ExceptionMessage(
338 : "prinbee_exception: name cannot be null."));
339 : }
340 4 : CATCH_END_SECTION()
341 :
342 5 : CATCH_START_SECTION("network_message: the empty string is not a valid name")
343 : {
344 3 : CATCH_REQUIRE_THROWS_MATCHES(
345 : prinbee::create_message_name("")
346 : , prinbee::invalid_parameter
347 : , Catch::Matchers::ExceptionMessage(
348 : "prinbee_exception: name cannot be empty."));
349 : }
350 4 : CATCH_END_SECTION()
351 :
352 5 : CATCH_START_SECTION("network_message: too many characters")
353 : {
354 3 : CATCH_REQUIRE_THROWS_MATCHES(
355 : prinbee::create_message_name("ELEPHANT")
356 : , prinbee::invalid_parameter
357 : , Catch::Matchers::ExceptionMessage(
358 : "prinbee_exception: name cannot be more than 4 characters."));
359 : }
360 4 : CATCH_END_SECTION()
361 3 : }
362 :
363 :
364 1 : CATCH_TEST_CASE("network_binary_client", "[network][message][valid]")
365 : {
366 3 : CATCH_START_SECTION("network_binary_client: verify readiness")
367 : {
368 1 : std::string const source_dir(SNAP_CATCH2_NAMESPACE::g_source_dir());
369 1 : std::string const filename(source_dir + "/tests/rprtr/binary_client.rprtr");
370 1 : SNAP_CATCH2_NAMESPACE::reporter::lexer::pointer_t l(SNAP_CATCH2_NAMESPACE::reporter::create_lexer(filename));
371 1 : CATCH_REQUIRE(l != nullptr);
372 1 : SNAP_CATCH2_NAMESPACE::reporter::state::pointer_t s(std::make_shared<SNAP_CATCH2_NAMESPACE::reporter::state>());
373 1 : SNAP_CATCH2_NAMESPACE::reporter::parser::pointer_t p(std::make_shared<SNAP_CATCH2_NAMESPACE::reporter::parser>(l, s));
374 1 : p->parse_program();
375 1 : SNAP_CATCH2_NAMESPACE::reporter::executor::pointer_t e(std::make_shared<SNAP_CATCH2_NAMESPACE::reporter::executor>(s));
376 1 : e->start();
377 1 : binary_client_test::pointer_t client(std::make_shared<binary_client_test>(get_address()));
378 1 : ed::communicator::instance()->add_connection(client);
379 1 : e->set_thread_done_callback([client]()
380 : {
381 1 : ed::communicator::instance()->remove_connection(client);
382 1 : });
383 : try
384 : {
385 1 : CATCH_REQUIRE(e->run());
386 : }
387 0 : catch(std::exception const & ex)
388 : {
389 0 : SNAP_LOG_FATAL
390 : << "an exception occurred while running cluckd (1 cluckd): "
391 : << ex
392 : << SNAP_LOG_SEND;
393 0 : libexcept::exception_base_t const * b(dynamic_cast<libexcept::exception_base_t const *>(&ex));
394 0 : if(b != nullptr) for(auto const & line : b->get_stack_trace())
395 : {
396 0 : SNAP_LOG_FATAL
397 : << " "
398 : << line
399 : << SNAP_LOG_SEND;
400 : }
401 0 : throw;
402 0 : }
403 1 : CATCH_REQUIRE(s->get_exit_code() == 0);
404 1 : }
405 2 : CATCH_END_SECTION()
406 1 : }
407 :
408 :
409 :
410 : // vim: ts=4 sw=4 et
|