Line data Source code
1 : // Copyright (c) 2023-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/exception.h>
27 : #include <prinbee/journal/journal.h>
28 :
29 :
30 : // snapdev
31 : //
32 : #include <snapdev/file_contents.h>
33 : #include <snapdev/mkdir_p.h>
34 : #include <snapdev/pathinfo.h>
35 :
36 :
37 : // C++
38 : //
39 : #include <random>
40 :
41 :
42 : // advgetopt
43 : //
44 : #include <advgetopt/conf_file.h>
45 :
46 :
47 : // last include
48 : //
49 : #include <snapdev/poison.h>
50 :
51 :
52 :
53 : namespace
54 : {
55 :
56 :
57 :
58 222935 : std::string conf_filename(std::string const & path)
59 : {
60 222935 : return path + "/journal.conf";
61 : }
62 :
63 :
64 111621 : std::string event_filename(std::string const & path, int index)
65 : {
66 223242 : return path + "/journal-" + std::to_string(index) + ".events";
67 : }
68 :
69 :
70 111523 : void unlink_conf(std::string const & path)
71 : {
72 111523 : std::string const filename(conf_filename(path));
73 111523 : if(unlink(filename.c_str()) != 0)
74 : {
75 116 : if(errno != ENOENT)
76 : {
77 0 : perror("unlink() returned unexpected error.");
78 0 : CATCH_REQUIRE(!"unlink() returned an unexpected error");
79 : }
80 : }
81 223046 : }
82 :
83 :
84 111523 : void unlink_events(std::string const & path)
85 : {
86 111523 : for(int idx(0);; ++idx)
87 : {
88 111566 : std::string const filename(event_filename(path, idx));
89 111566 : if(unlink(filename.c_str()) != 0)
90 : {
91 111523 : if(errno != ENOENT)
92 : {
93 0 : perror("unlink() returned unexpected error.");
94 0 : CATCH_REQUIRE(!"unlink() returned an unexpected error");
95 : }
96 111523 : break;
97 : }
98 111609 : }
99 111523 : }
100 :
101 :
102 111523 : std::string conf_path(std::string const & sub_path, bool create_directory = false)
103 : {
104 111523 : std::string const path(SNAP_CATCH2_NAMESPACE::g_tmp_dir() + '/' + sub_path);
105 111523 : if(create_directory)
106 : {
107 1 : CATCH_REQUIRE(snapdev::mkdir_p(path) == 0);
108 : }
109 111523 : unlink_conf(path);
110 111523 : unlink_events(path);
111 111523 : return path;
112 0 : }
113 :
114 :
115 : typedef std::map<std::string, std::string> conf_values_t;
116 :
117 111403 : conf_values_t load_conf(std::string const & path)
118 : {
119 111403 : conf_values_t conf_values;
120 111403 : snapdev::file_contents file(conf_filename(path));
121 111403 : bool const valid(file.read_all());
122 111403 : if(!valid)
123 : {
124 : std::cerr << "--- error loading configuration file: "
125 0 : << file.last_error()
126 0 : << "\n";
127 : }
128 111403 : CATCH_REQUIRE(valid);
129 111403 : std::string const contents(file.contents());
130 111403 : std::list<std::string> lines;
131 334209 : snapdev::tokenize_string(lines, contents, "\r\n", true);
132 1336836 : for(auto const & l : lines)
133 : {
134 1225433 : if(l.empty()
135 1225433 : || l[0] == '#')
136 : {
137 334209 : continue;
138 : }
139 891224 : std::string::size_type const pos(l.find('='));
140 891224 : CATCH_REQUIRE(std::string::npos != pos);
141 891224 : std::string const name(l.substr(0, pos));
142 891224 : std::string const value(l.substr(pos + 1));
143 891224 : conf_values.emplace(name, value);
144 891224 : }
145 111403 : return conf_values;
146 111403 : }
147 :
148 :
149 : }
150 :
151 :
152 1 : CATCH_TEST_CASE("journal_helper_functions", "[journal]")
153 : {
154 3 : CATCH_START_SECTION("journal_helper_functions: id_to_string()")
155 : {
156 1 : std::uint32_t const id((0x31 << 24) | (0x32 << 16) | (0x33 << 8) | 0x34);
157 1 : CATCH_REQUIRE(prinbee::id_to_string(id) == "1234");
158 : }
159 2 : CATCH_END_SECTION()
160 1 : }
161 :
162 :
163 16 : CATCH_TEST_CASE("journal_options", "[journal]")
164 : {
165 18 : CATCH_START_SECTION("journal_options: set_maximum_number_of_files(): default does nothing")
166 : {
167 3 : std::string const path(conf_path("journal_options"));
168 1 : advgetopt::conf_file::reset_conf_files();
169 1 : prinbee::journal j(path);
170 1 : CATCH_REQUIRE(j.is_valid());
171 1 : CATCH_REQUIRE(j.set_maximum_number_of_files(prinbee::JOURNAL_DEFAULT_NUMBER_OF_FILES));
172 1 : std::string const filename(conf_filename(path));
173 1 : struct stat s;
174 1 : if(stat(filename.c_str(), &s) != 0)
175 : {
176 1 : CATCH_REQUIRE(errno == ENOENT);
177 : }
178 : else
179 : {
180 0 : CATCH_REQUIRE(!"set_maximum_number_of_files() default created a configuration file.");
181 : }
182 1 : }
183 17 : CATCH_END_SECTION()
184 :
185 18 : CATCH_START_SECTION("journal_options: set_maximum_file_size(): default does nothing")
186 : {
187 3 : std::string const path(conf_path("journal_options"));
188 1 : advgetopt::conf_file::reset_conf_files();
189 1 : prinbee::journal j(path);
190 1 : CATCH_REQUIRE(j.is_valid());
191 1 : CATCH_REQUIRE(j.set_maximum_file_size(prinbee::JOURNAL_DEFAULT_FILE_SIZE));
192 1 : std::string const filename(conf_filename(path));
193 1 : struct stat s;
194 1 : if(stat(filename.c_str(), &s) != 0)
195 : {
196 1 : CATCH_REQUIRE(errno == ENOENT);
197 : }
198 : else
199 : {
200 0 : CATCH_REQUIRE(!"set_maximum_file_size() default created a configuration file.");
201 : }
202 1 : }
203 17 : CATCH_END_SECTION()
204 :
205 18 : CATCH_START_SECTION("journal_options: set_maximum_events(): default does nothing")
206 : {
207 3 : std::string const path(conf_path("journal_options"));
208 1 : advgetopt::conf_file::reset_conf_files();
209 1 : prinbee::journal j(path);
210 1 : CATCH_REQUIRE(j.is_valid());
211 1 : CATCH_REQUIRE(j.set_maximum_events(prinbee::JOURNAL_DEFAULT_EVENTS));
212 1 : std::string const filename(conf_filename(path));
213 1 : struct stat s;
214 1 : if(stat(filename.c_str(), &s) != 0)
215 : {
216 1 : CATCH_REQUIRE(errno == ENOENT);
217 : }
218 : else
219 : {
220 0 : CATCH_REQUIRE(!"set_maximum_events() default created a configuration file.");
221 : }
222 1 : }
223 17 : CATCH_END_SECTION()
224 :
225 18 : CATCH_START_SECTION("journal_options: set_inline_attachment_size_threshold(): default does nothing")
226 : {
227 3 : std::string const path(conf_path("journal_options"));
228 1 : advgetopt::conf_file::reset_conf_files();
229 1 : prinbee::journal j(path);
230 1 : CATCH_REQUIRE(j.is_valid());
231 1 : CATCH_REQUIRE(j.set_inline_attachment_size_threshold(prinbee::JOURNAL_INLINE_ATTACHMENT_SIZE_DEFAULT_THRESHOLD));
232 1 : std::string const filename(conf_filename(path));
233 1 : struct stat s;
234 1 : if(stat(filename.c_str(), &s) != 0)
235 : {
236 1 : CATCH_REQUIRE(errno == ENOENT);
237 : }
238 : else
239 : {
240 0 : CATCH_REQUIRE(!"set_inline_attachment_size_threshold() default created a configuration file.");
241 : }
242 1 : }
243 17 : CATCH_END_SECTION()
244 :
245 18 : CATCH_START_SECTION("journal_options: set_sync(): default does nothing")
246 : {
247 3 : std::string const path(conf_path("journal_options"));
248 1 : advgetopt::conf_file::reset_conf_files();
249 1 : prinbee::journal j(path);
250 1 : CATCH_REQUIRE(j.is_valid());
251 1 : CATCH_REQUIRE(j.set_sync(prinbee::sync_t::SYNC_NONE));
252 1 : std::string const filename(conf_filename(path));
253 1 : struct stat s;
254 1 : if(stat(filename.c_str(), &s) != 0)
255 : {
256 1 : CATCH_REQUIRE(errno == ENOENT);
257 : }
258 : else
259 : {
260 0 : CATCH_REQUIRE(!"set_sync() default created a configuration file.");
261 : }
262 1 : }
263 17 : CATCH_END_SECTION()
264 :
265 18 : CATCH_START_SECTION("journal_options: set_file_management(): default does nothing")
266 : {
267 3 : std::string const path(conf_path("journal_options"));
268 1 : advgetopt::conf_file::reset_conf_files();
269 1 : prinbee::journal j(path);
270 1 : CATCH_REQUIRE(j.is_valid());
271 1 : CATCH_REQUIRE(j.set_file_management(prinbee::file_management_t::FILE_MANAGEMENT_KEEP));
272 1 : std::string const filename(conf_filename(path));
273 1 : struct stat s;
274 1 : if(stat(filename.c_str(), &s) != 0)
275 : {
276 1 : CATCH_REQUIRE(errno == ENOENT);
277 : }
278 : else
279 : {
280 0 : CATCH_REQUIRE(!"set_file_management() default created a configuration file.");
281 : }
282 1 : }
283 17 : CATCH_END_SECTION()
284 :
285 18 : CATCH_START_SECTION("journal_options: set_compress_when_full(): default does nothing")
286 : {
287 3 : std::string const path(conf_path("journal_options"));
288 1 : advgetopt::conf_file::reset_conf_files();
289 1 : prinbee::journal j(path);
290 1 : CATCH_REQUIRE(j.is_valid());
291 1 : CATCH_REQUIRE(j.set_compress_when_full(false));
292 1 : std::string const filename(conf_filename(path));
293 1 : struct stat s;
294 1 : if(stat(filename.c_str(), &s) != 0)
295 : {
296 1 : CATCH_REQUIRE(errno == ENOENT);
297 : }
298 : else
299 : {
300 0 : CATCH_REQUIRE(!"set_compress_when_full() default created a configuration file.");
301 : }
302 1 : }
303 17 : CATCH_END_SECTION()
304 :
305 18 : CATCH_START_SECTION("journal_options: set_attachment_copy_handling(): default does nothing")
306 : {
307 : {
308 3 : std::string const path(conf_path("journal_options"));
309 1 : advgetopt::conf_file::reset_conf_files();
310 1 : prinbee::journal j(path);
311 1 : CATCH_REQUIRE(j.is_valid());
312 1 : CATCH_REQUIRE(j.set_attachment_copy_handling(prinbee::attachment_copy_handling_t::ATTACHMENT_COPY_HANDLING_SOFTLINK));
313 1 : std::string const filename(conf_filename(path));
314 1 : struct stat s;
315 1 : if(stat(filename.c_str(), &s) != 0)
316 : {
317 1 : CATCH_REQUIRE(errno == ENOENT);
318 : }
319 : else
320 : {
321 0 : CATCH_REQUIRE(!"set_attachment_copy_handling() default created a configuration file.");
322 : }
323 1 : CATCH_REQUIRE(j.get_attachment_copy_handling() == prinbee::attachment_copy_handling_t::ATTACHMENT_COPY_HANDLING_SOFTLINK);
324 1 : }
325 :
326 : // "default" is viewed as "softlink" so it's also the default
327 : {
328 3 : std::string const path(conf_path("journal_options"));
329 1 : advgetopt::conf_file::reset_conf_files();
330 1 : prinbee::journal j(path);
331 1 : CATCH_REQUIRE(j.is_valid());
332 1 : CATCH_REQUIRE(j.set_attachment_copy_handling(prinbee::attachment_copy_handling_t::ATTACHMENT_COPY_HANDLING_DEFAULT));
333 1 : std::string const filename(conf_filename(path));
334 1 : struct stat s;
335 1 : if(stat(filename.c_str(), &s) != 0)
336 : {
337 1 : CATCH_REQUIRE(errno == ENOENT);
338 : }
339 : else
340 : {
341 0 : CATCH_REQUIRE(!"set_attachment_copy_handling() default created a configuration file.");
342 : }
343 1 : CATCH_REQUIRE(j.get_attachment_copy_handling() == prinbee::attachment_copy_handling_t::ATTACHMENT_COPY_HANDLING_SOFTLINK);
344 1 : }
345 : }
346 17 : CATCH_END_SECTION()
347 :
348 18 : CATCH_START_SECTION("journal_options: verify set options")
349 : {
350 : enum
351 : {
352 : COMPRESS_WHEN_FULL,
353 : FILE_MANAGEMENT,
354 : MAXIMUM_EVENTS,
355 : MAXIMUM_FILE_SIZE,
356 : MAXIMUM_NUMBER_OF_FILES,
357 : FLUSH,
358 : SYNC,
359 : INLINE_ATTACHMENT_SIZE_THRESHOLD,
360 : ATTACHMENT_COPY_HANDLING_SOFTLINK,
361 : ATTACHMENT_COPY_HANDLING_HARDLINK,
362 : ATTACHMENT_COPY_HANDLING_REFLINK,
363 : ATTACHMENT_COPY_HANDLING_FULL,
364 :
365 : max_options
366 : };
367 13 : for(int index(0); index < max_options; ++index)
368 : {
369 12 : std::string expected_result;
370 36 : std::string const path(conf_path("journal_options"));
371 12 : advgetopt::conf_file::reset_conf_files();
372 12 : prinbee::journal j(path);
373 12 : CATCH_REQUIRE(j.is_valid());
374 12 : switch(index)
375 : {
376 1 : case COMPRESS_WHEN_FULL:
377 1 : CATCH_REQUIRE(j.set_compress_when_full(true));
378 : //CATCH_REQUIRE(j.get_compress_when_full());
379 1 : break;
380 :
381 1 : case FILE_MANAGEMENT:
382 : {
383 1 : prinbee::file_management_t const value(static_cast<prinbee::file_management_t>(rand() % 3));
384 :
385 : // just setting the default does not re-save the configuration file
386 : // which we need to happen for this test
387 : //
388 1 : if(value == prinbee::file_management_t::FILE_MANAGEMENT_KEEP)
389 : {
390 1 : CATCH_REQUIRE(j.set_file_management(rand() % 2 == 0
391 : ? prinbee::file_management_t::FILE_MANAGEMENT_TRUNCATE
392 : : prinbee::file_management_t::FILE_MANAGEMENT_DELETE));
393 : }
394 :
395 1 : CATCH_REQUIRE(j.set_file_management(value));
396 1 : CATCH_REQUIRE(j.get_file_management() == value);
397 1 : switch(value)
398 : {
399 1 : case prinbee::file_management_t::FILE_MANAGEMENT_KEEP:
400 1 : expected_result = "keep";
401 1 : break;
402 :
403 0 : case prinbee::file_management_t::FILE_MANAGEMENT_TRUNCATE:
404 0 : expected_result = "truncate";
405 0 : break;
406 :
407 0 : case prinbee::file_management_t::FILE_MANAGEMENT_DELETE:
408 0 : expected_result = "delete";
409 0 : break;
410 :
411 : }
412 : }
413 : break;
414 :
415 1 : case MAXIMUM_EVENTS:
416 : {
417 1 : std::uint32_t value(0);
418 : do
419 : {
420 1 : value = rand();
421 : }
422 1 : while(value == prinbee::JOURNAL_DEFAULT_EVENTS);
423 1 : CATCH_REQUIRE(j.set_maximum_events(value));
424 1 : if(value < prinbee::JOURNAL_MINIMUM_EVENTS)
425 : {
426 0 : expected_result = std::to_string(prinbee::JOURNAL_MINIMUM_EVENTS);
427 : }
428 1 : else if(value > prinbee::JOURNAL_MAXIMUM_EVENTS)
429 : {
430 1 : expected_result = std::to_string(prinbee::JOURNAL_MAXIMUM_EVENTS);
431 : }
432 : else
433 : {
434 0 : expected_result = std::to_string(value);
435 : }
436 : }
437 1 : break;
438 :
439 1 : case MAXIMUM_FILE_SIZE:
440 : {
441 : std::uint32_t value;
442 : do
443 : {
444 1 : value = rand() + 1;
445 : }
446 1 : while(value == prinbee::JOURNAL_DEFAULT_FILE_SIZE);
447 1 : CATCH_REQUIRE(j.set_maximum_file_size(value));
448 1 : if(value < prinbee::JOURNAL_MINIMUM_FILE_SIZE)
449 : {
450 0 : expected_result = std::to_string(prinbee::JOURNAL_MINIMUM_FILE_SIZE);
451 : }
452 1 : else if(value > prinbee::JOURNAL_MAXIMUM_FILE_SIZE)
453 : {
454 1 : expected_result = std::to_string(prinbee::JOURNAL_MAXIMUM_FILE_SIZE);
455 : }
456 : else
457 : {
458 0 : expected_result = std::to_string(value);
459 : }
460 : }
461 1 : break;
462 :
463 1 : case MAXIMUM_NUMBER_OF_FILES:
464 : {
465 : // avoid the default (i.e. 2) so the configuration file
466 : // gets saved
467 : //
468 1 : int const value(rand() % (256 - 3) + 3);
469 1 : CATCH_REQUIRE(j.set_maximum_number_of_files(value));
470 1 : expected_result = std::to_string(value);
471 : }
472 1 : break;
473 :
474 1 : case FLUSH:
475 1 : CATCH_REQUIRE(j.set_sync(prinbee::sync_t::SYNC_FLUSH));
476 : //CATCH_REQUIRE(j.get_sync() == prinbee::sync_t::SYNC_FLUSH);
477 1 : break;
478 :
479 1 : case SYNC:
480 1 : CATCH_REQUIRE(j.set_sync(prinbee::sync_t::SYNC_FULL));
481 : //CATCH_REQUIRE(j.get_sync() == prinbee::sync_t::SYNC_FULL);
482 1 : break;
483 :
484 1 : case INLINE_ATTACHMENT_SIZE_THRESHOLD:
485 : {
486 1 : int value(0);
487 : do
488 : {
489 1 : value = (rand() % (prinbee::JOURNAL_INLINE_ATTACHMENT_SIZE_MAXIMUM_THRESHOLD - prinbee::JOURNAL_INLINE_ATTACHMENT_SIZE_MINIMUM_THRESHOLD)
490 1 : + prinbee::JOURNAL_INLINE_ATTACHMENT_SIZE_MINIMUM_THRESHOLD);
491 : }
492 1 : while(value == prinbee::JOURNAL_INLINE_ATTACHMENT_SIZE_DEFAULT_THRESHOLD);
493 1 : CATCH_REQUIRE(j.set_inline_attachment_size_threshold(value));
494 1 : expected_result = std::to_string(value);
495 : }
496 1 : break;
497 :
498 1 : case ATTACHMENT_COPY_HANDLING_SOFTLINK:
499 : // SOFTLINK is the default, to make sure we get a conf file,
500 : // first set HARDLINK and then switch back
501 : //
502 1 : CATCH_REQUIRE(j.set_attachment_copy_handling(prinbee::attachment_copy_handling_t::ATTACHMENT_COPY_HANDLING_HARDLINK));
503 1 : CATCH_REQUIRE(j.set_attachment_copy_handling(prinbee::attachment_copy_handling_t::ATTACHMENT_COPY_HANDLING_SOFTLINK));
504 1 : CATCH_REQUIRE(j.get_attachment_copy_handling() == prinbee::attachment_copy_handling_t::ATTACHMENT_COPY_HANDLING_SOFTLINK);
505 1 : break;
506 :
507 1 : case ATTACHMENT_COPY_HANDLING_HARDLINK:
508 1 : CATCH_REQUIRE(j.set_attachment_copy_handling(prinbee::attachment_copy_handling_t::ATTACHMENT_COPY_HANDLING_HARDLINK));
509 1 : CATCH_REQUIRE(j.get_attachment_copy_handling() == prinbee::attachment_copy_handling_t::ATTACHMENT_COPY_HANDLING_HARDLINK);
510 1 : break;
511 :
512 1 : case ATTACHMENT_COPY_HANDLING_REFLINK:
513 1 : CATCH_REQUIRE(j.set_attachment_copy_handling(prinbee::attachment_copy_handling_t::ATTACHMENT_COPY_HANDLING_REFLINK));
514 1 : CATCH_REQUIRE(j.get_attachment_copy_handling() == prinbee::attachment_copy_handling_t::ATTACHMENT_COPY_HANDLING_REFLINK);
515 1 : break;
516 :
517 1 : case ATTACHMENT_COPY_HANDLING_FULL:
518 1 : CATCH_REQUIRE(j.set_attachment_copy_handling(prinbee::attachment_copy_handling_t::ATTACHMENT_COPY_HANDLING_FULL));
519 1 : CATCH_REQUIRE(j.get_attachment_copy_handling() == prinbee::attachment_copy_handling_t::ATTACHMENT_COPY_HANDLING_FULL);
520 1 : break;
521 :
522 0 : default:
523 0 : CATCH_REQUIRE(!"the test is invalid, add another case as required");
524 0 : break;
525 :
526 : }
527 :
528 : // load configuration we just updated
529 : //
530 24 : conf_values_t conf_values(load_conf(path));
531 :
532 36 : auto it(conf_values.find("compress_when_full"));
533 12 : CATCH_REQUIRE(it != conf_values.end());
534 12 : CATCH_REQUIRE((index == COMPRESS_WHEN_FULL ? "true" : "false") == it->second);
535 12 : conf_values.erase(it);
536 :
537 36 : it = conf_values.find("file_management");
538 12 : CATCH_REQUIRE(it != conf_values.end());
539 34 : CATCH_REQUIRE((index == FILE_MANAGEMENT ? expected_result : "keep") == it->second);
540 12 : conf_values.erase(it);
541 :
542 36 : it = conf_values.find("maximum_events");
543 12 : CATCH_REQUIRE(it != conf_values.end());
544 34 : CATCH_REQUIRE((index == MAXIMUM_EVENTS ? expected_result : "4096") == it->second);
545 12 : conf_values.erase(it);
546 :
547 36 : it = conf_values.find("maximum_file_size");
548 12 : CATCH_REQUIRE(it != conf_values.end());
549 34 : CATCH_REQUIRE((index == MAXIMUM_FILE_SIZE ? expected_result : "1048576") == it->second);
550 12 : conf_values.erase(it);
551 :
552 36 : it = conf_values.find("maximum_number_of_files");
553 12 : CATCH_REQUIRE(it != conf_values.end());
554 34 : CATCH_REQUIRE((index == MAXIMUM_NUMBER_OF_FILES ? expected_result : "2") == it->second);
555 12 : conf_values.erase(it);
556 :
557 36 : it = conf_values.find("sync");
558 12 : CATCH_REQUIRE(it != conf_values.end());
559 12 : switch(index)
560 : {
561 1 : case FLUSH:
562 1 : CATCH_REQUIRE("flush" == it->second);
563 1 : break;
564 :
565 1 : case SYNC:
566 1 : CATCH_REQUIRE("full" == it->second);
567 1 : break;
568 :
569 10 : default:
570 10 : CATCH_REQUIRE("none" == it->second);
571 10 : break;
572 :
573 : }
574 12 : conf_values.erase(it);
575 :
576 36 : it = conf_values.find("inline_attachment_size_threshold");
577 12 : CATCH_REQUIRE(it != conf_values.end());
578 12 : CATCH_REQUIRE((index == INLINE_ATTACHMENT_SIZE_THRESHOLD
579 : ? expected_result
580 : : std::to_string(prinbee::JOURNAL_INLINE_ATTACHMENT_SIZE_DEFAULT_THRESHOLD)) == it->second);
581 12 : conf_values.erase(it);
582 :
583 36 : it = conf_values.find("attachment_copy_handling");
584 12 : CATCH_REQUIRE(it != conf_values.end());
585 12 : switch(index)
586 : {
587 1 : case ATTACHMENT_COPY_HANDLING_HARDLINK:
588 1 : CATCH_REQUIRE("hardlink" == it->second);
589 1 : break;
590 :
591 1 : case ATTACHMENT_COPY_HANDLING_REFLINK:
592 1 : CATCH_REQUIRE("reflink" == it->second);
593 1 : break;
594 :
595 1 : case ATTACHMENT_COPY_HANDLING_FULL:
596 1 : CATCH_REQUIRE("full" == it->second);
597 1 : break;
598 :
599 : // the default is "softlink"
600 9 : case ATTACHMENT_COPY_HANDLING_SOFTLINK:
601 : default:
602 9 : CATCH_REQUIRE("softlink" == it->second);
603 9 : break;
604 :
605 : }
606 12 : conf_values.erase(it);
607 :
608 12 : CATCH_REQUIRE(conf_values.empty());
609 12 : }
610 : }
611 17 : CATCH_END_SECTION()
612 :
613 18 : CATCH_START_SECTION("journal_options: reducing the number of files generates a TODO")
614 : {
615 3 : std::string const path(conf_path("journal_options"));
616 1 : advgetopt::conf_file::reset_conf_files();
617 1 : prinbee::journal j(path);
618 1 : CATCH_REQUIRE(j.is_valid());
619 1 : CATCH_REQUIRE(j.set_maximum_file_size(10));
620 1 : CATCH_REQUIRE(j.set_maximum_file_size(5)); // <- here (TODO: add logger output capture to verify what happens)
621 1 : }
622 17 : CATCH_END_SECTION()
623 :
624 18 : CATCH_START_SECTION("journal_options: invalid file management numbers")
625 : {
626 3 : std::string const path(conf_path("journal_options"));
627 1 : advgetopt::conf_file::reset_conf_files();
628 1 : prinbee::journal j(path);
629 1 : CATCH_REQUIRE(j.is_valid());
630 :
631 257 : for(int i(0); i < 256; ++i)
632 : {
633 256 : switch(static_cast<prinbee::file_management_t>(i))
634 : {
635 3 : case prinbee::file_management_t::FILE_MANAGEMENT_KEEP:
636 : case prinbee::file_management_t::FILE_MANAGEMENT_TRUNCATE:
637 : case prinbee::file_management_t::FILE_MANAGEMENT_DELETE:
638 : // these are valid, ignore
639 3 : break;
640 :
641 253 : default:
642 759 : CATCH_REQUIRE_THROWS_MATCHES(
643 : j.set_file_management(static_cast<prinbee::file_management_t>(i))
644 : , prinbee::invalid_parameter
645 : , Catch::Matchers::ExceptionMessage(
646 : "prinbee_exception: unsupported file management number"));
647 : break;
648 :
649 : }
650 : }
651 1 : }
652 17 : CATCH_END_SECTION()
653 :
654 18 : CATCH_START_SECTION("journal_options: invalid attachment copy handling numbers")
655 : {
656 3 : std::string const path(conf_path("journal_options"));
657 1 : advgetopt::conf_file::reset_conf_files();
658 1 : prinbee::journal j(path);
659 1 : CATCH_REQUIRE(j.is_valid());
660 :
661 257 : for(int i(0); i < 256; ++i)
662 : {
663 256 : switch(static_cast<prinbee::attachment_copy_handling_t>(i))
664 : {
665 5 : case prinbee::attachment_copy_handling_t::ATTACHMENT_COPY_HANDLING_DEFAULT:
666 : case prinbee::attachment_copy_handling_t::ATTACHMENT_COPY_HANDLING_SOFTLINK:
667 : case prinbee::attachment_copy_handling_t::ATTACHMENT_COPY_HANDLING_HARDLINK:
668 : case prinbee::attachment_copy_handling_t::ATTACHMENT_COPY_HANDLING_REFLINK:
669 : case prinbee::attachment_copy_handling_t::ATTACHMENT_COPY_HANDLING_FULL:
670 : // these are valid, ignore
671 5 : break;
672 :
673 251 : default:
674 : {
675 251 : std::stringstream ss;
676 251 : ss << "prinbee_exception: unknown attachment_copy_handling_t enumeration type ("
677 : << i
678 251 : << ").";
679 251 : CATCH_REQUIRE_THROWS_MATCHES(
680 : j.set_attachment_copy_handling(static_cast<prinbee::attachment_copy_handling_t>(i))
681 : , prinbee::invalid_parameter
682 : , Catch::Matchers::ExceptionMessage(ss.str()));
683 251 : }
684 : break;
685 :
686 : }
687 : }
688 1 : }
689 17 : CATCH_END_SECTION()
690 :
691 18 : CATCH_START_SECTION("journal_options: minimum number of events")
692 : {
693 102 : for(std::uint32_t count(0); count <= prinbee::JOURNAL_MINIMUM_EVENTS; ++count)
694 : {
695 303 : std::string const path(conf_path("journal_options"));
696 101 : advgetopt::conf_file::reset_conf_files();
697 101 : prinbee::journal j(path);
698 101 : CATCH_REQUIRE(j.is_valid());
699 101 : CATCH_REQUIRE(j.set_maximum_events(count));
700 101 : conf_values_t conf_values(load_conf(path));
701 :
702 303 : auto const it(conf_values.find("maximum_events"));
703 101 : CATCH_REQUIRE(it != conf_values.end());
704 101 : CATCH_REQUIRE(std::to_string(prinbee::JOURNAL_MINIMUM_EVENTS) == it->second);
705 101 : }
706 : }
707 17 : CATCH_END_SECTION()
708 :
709 18 : CATCH_START_SECTION("journal_options: maximum number of events")
710 : {
711 732 : for(std::uint32_t count(prinbee::JOURNAL_MAXIMUM_EVENTS); count <= 1'000'000; count += rand() % 2'500 + 1)
712 : {
713 2193 : std::string const path(conf_path("journal_options"));
714 731 : advgetopt::conf_file::reset_conf_files();
715 731 : prinbee::journal j(path);
716 731 : CATCH_REQUIRE(j.is_valid());
717 731 : CATCH_REQUIRE(j.set_maximum_events(count));
718 731 : conf_values_t conf_values(load_conf(path));
719 :
720 2193 : auto const it(conf_values.find("maximum_events"));
721 731 : CATCH_REQUIRE(it != conf_values.end());
722 731 : CATCH_REQUIRE(std::to_string(prinbee::JOURNAL_MAXIMUM_EVENTS) == it->second);
723 731 : }
724 : }
725 17 : CATCH_END_SECTION()
726 :
727 18 : CATCH_START_SECTION("journal_options: minimum file size")
728 : {
729 65538 : for(std::uint32_t size(0); size <= prinbee::JOURNAL_MINIMUM_FILE_SIZE; ++size)
730 : {
731 196611 : std::string const path(conf_path("journal_options"));
732 65537 : advgetopt::conf_file::reset_conf_files();
733 65537 : prinbee::journal j(path);
734 65537 : CATCH_REQUIRE(j.is_valid());
735 65537 : CATCH_REQUIRE(j.set_maximum_file_size(size));
736 65537 : conf_values_t conf_values(load_conf(path));
737 :
738 196611 : auto const it(conf_values.find("maximum_file_size"));
739 65537 : CATCH_REQUIRE(it != conf_values.end());
740 65537 : CATCH_REQUIRE(std::to_string(prinbee::JOURNAL_MINIMUM_FILE_SIZE) == it->second);
741 65537 : }
742 : }
743 17 : CATCH_END_SECTION()
744 :
745 18 : CATCH_START_SECTION("journal_options: maximum file size")
746 : {
747 45023 : for(std::uint32_t size(prinbee::JOURNAL_MAXIMUM_FILE_SIZE); size <= 0x6000'0000; size += rand() % 65536 + 1)
748 : {
749 135066 : std::string const path(conf_path("journal_options"));
750 45022 : advgetopt::conf_file::reset_conf_files();
751 45022 : prinbee::journal j(path);
752 45022 : CATCH_REQUIRE(j.is_valid());
753 45022 : CATCH_REQUIRE(j.set_maximum_file_size(size));
754 45022 : conf_values_t conf_values(load_conf(path));
755 :
756 135066 : auto const it(conf_values.find("maximum_file_size"));
757 45022 : CATCH_REQUIRE(it != conf_values.end());
758 45022 : CATCH_REQUIRE(std::to_string(prinbee::JOURNAL_MAXIMUM_FILE_SIZE) == it->second);
759 45022 : }
760 : }
761 17 : CATCH_END_SECTION()
762 16 : }
763 :
764 :
765 4 : CATCH_TEST_CASE("journal_event_status_sequence", "[journal]")
766 : {
767 6 : CATCH_START_SECTION("journal_event_status_sequence: all valid/invalid sequences")
768 : {
769 1 : std::vector<prinbee::status_t> next_status[]
770 : {
771 : // ready -> ... -> completed
772 : {
773 : prinbee::status_t::STATUS_FORWARDED,
774 : prinbee::status_t::STATUS_ACKNOWLEDGED,
775 : prinbee::status_t::STATUS_COMPLETED,
776 : },
777 : {
778 : prinbee::status_t::STATUS_ACKNOWLEDGED,
779 : prinbee::status_t::STATUS_COMPLETED,
780 : },
781 : {
782 : prinbee::status_t::STATUS_FORWARDED,
783 : prinbee::status_t::STATUS_COMPLETED,
784 : },
785 : {
786 : prinbee::status_t::STATUS_COMPLETED,
787 : },
788 :
789 : // ready -> ... -> fails
790 : {
791 : prinbee::status_t::STATUS_FORWARDED,
792 : prinbee::status_t::STATUS_ACKNOWLEDGED,
793 : prinbee::status_t::STATUS_FAILED,
794 : },
795 : {
796 : prinbee::status_t::STATUS_ACKNOWLEDGED,
797 : prinbee::status_t::STATUS_FAILED,
798 : },
799 : {
800 : prinbee::status_t::STATUS_FORWARDED,
801 : prinbee::status_t::STATUS_FAILED,
802 : },
803 : {
804 : prinbee::status_t::STATUS_FAILED,
805 : },
806 :
807 : // impossible
808 : {
809 : prinbee::status_t::STATUS_FORWARDED,
810 : prinbee::status_t::STATUS_UNKNOWN,
811 : prinbee::status_t::STATUS_FORWARDED,
812 : },
813 : {
814 : prinbee::status_t::STATUS_FORWARDED,
815 : prinbee::status_t::STATUS_ACKNOWLEDGED,
816 : prinbee::status_t::STATUS_UNKNOWN,
817 : prinbee::status_t::STATUS_FORWARDED,
818 : },
819 : {
820 : prinbee::status_t::STATUS_FORWARDED,
821 : prinbee::status_t::STATUS_ACKNOWLEDGED,
822 : prinbee::status_t::STATUS_UNKNOWN,
823 : prinbee::status_t::STATUS_ACKNOWLEDGED,
824 : },
825 : {
826 : prinbee::status_t::STATUS_ACKNOWLEDGED,
827 : prinbee::status_t::STATUS_UNKNOWN,
828 : prinbee::status_t::STATUS_FORWARDED,
829 : },
830 : {
831 : prinbee::status_t::STATUS_ACKNOWLEDGED,
832 : prinbee::status_t::STATUS_UNKNOWN,
833 : prinbee::status_t::STATUS_ACKNOWLEDGED,
834 : },
835 : {
836 : prinbee::status_t::STATUS_FORWARDED,
837 : prinbee::status_t::STATUS_ACKNOWLEDGED,
838 : prinbee::status_t::STATUS_COMPLETED,
839 : prinbee::status_t::STATUS_UNKNOWN,
840 : prinbee::status_t::STATUS_FORWARDED,
841 : },
842 : {
843 : prinbee::status_t::STATUS_FORWARDED,
844 : prinbee::status_t::STATUS_ACKNOWLEDGED,
845 : prinbee::status_t::STATUS_COMPLETED,
846 : prinbee::status_t::STATUS_UNKNOWN,
847 : prinbee::status_t::STATUS_ACKNOWLEDGED,
848 : },
849 : {
850 : prinbee::status_t::STATUS_FORWARDED,
851 : prinbee::status_t::STATUS_ACKNOWLEDGED,
852 : prinbee::status_t::STATUS_COMPLETED,
853 : prinbee::status_t::STATUS_UNKNOWN,
854 : prinbee::status_t::STATUS_COMPLETED,
855 : },
856 : {
857 : prinbee::status_t::STATUS_ACKNOWLEDGED,
858 : prinbee::status_t::STATUS_COMPLETED,
859 : prinbee::status_t::STATUS_UNKNOWN,
860 : prinbee::status_t::STATUS_FORWARDED,
861 : },
862 : {
863 : prinbee::status_t::STATUS_ACKNOWLEDGED,
864 : prinbee::status_t::STATUS_COMPLETED,
865 : prinbee::status_t::STATUS_UNKNOWN,
866 : prinbee::status_t::STATUS_ACKNOWLEDGED,
867 : },
868 : {
869 : prinbee::status_t::STATUS_ACKNOWLEDGED,
870 : prinbee::status_t::STATUS_COMPLETED,
871 : prinbee::status_t::STATUS_UNKNOWN,
872 : prinbee::status_t::STATUS_COMPLETED,
873 : },
874 : {
875 : prinbee::status_t::STATUS_COMPLETED,
876 : prinbee::status_t::STATUS_UNKNOWN,
877 : prinbee::status_t::STATUS_FORWARDED,
878 : },
879 : {
880 : prinbee::status_t::STATUS_COMPLETED,
881 : prinbee::status_t::STATUS_UNKNOWN,
882 : prinbee::status_t::STATUS_ACKNOWLEDGED,
883 : },
884 : {
885 : prinbee::status_t::STATUS_COMPLETED,
886 : prinbee::status_t::STATUS_UNKNOWN,
887 : prinbee::status_t::STATUS_COMPLETED,
888 : },
889 : {
890 : prinbee::status_t::STATUS_FORWARDED,
891 : prinbee::status_t::STATUS_ACKNOWLEDGED,
892 : prinbee::status_t::STATUS_FAILED,
893 : prinbee::status_t::STATUS_UNKNOWN,
894 : prinbee::status_t::STATUS_FORWARDED,
895 : },
896 : {
897 : prinbee::status_t::STATUS_FORWARDED,
898 : prinbee::status_t::STATUS_ACKNOWLEDGED,
899 : prinbee::status_t::STATUS_FAILED,
900 : prinbee::status_t::STATUS_UNKNOWN,
901 : prinbee::status_t::STATUS_ACKNOWLEDGED,
902 : },
903 : {
904 : prinbee::status_t::STATUS_FORWARDED,
905 : prinbee::status_t::STATUS_ACKNOWLEDGED,
906 : prinbee::status_t::STATUS_FAILED,
907 : prinbee::status_t::STATUS_UNKNOWN,
908 : prinbee::status_t::STATUS_FAILED,
909 : },
910 : {
911 : prinbee::status_t::STATUS_ACKNOWLEDGED,
912 : prinbee::status_t::STATUS_FAILED,
913 : prinbee::status_t::STATUS_UNKNOWN,
914 : prinbee::status_t::STATUS_FORWARDED,
915 : },
916 : {
917 : prinbee::status_t::STATUS_ACKNOWLEDGED,
918 : prinbee::status_t::STATUS_FAILED,
919 : prinbee::status_t::STATUS_UNKNOWN,
920 : prinbee::status_t::STATUS_ACKNOWLEDGED,
921 : },
922 : {
923 : prinbee::status_t::STATUS_ACKNOWLEDGED,
924 : prinbee::status_t::STATUS_FAILED,
925 : prinbee::status_t::STATUS_UNKNOWN,
926 : prinbee::status_t::STATUS_FAILED,
927 : },
928 : {
929 : prinbee::status_t::STATUS_FAILED,
930 : prinbee::status_t::STATUS_UNKNOWN,
931 : prinbee::status_t::STATUS_FORWARDED,
932 : },
933 : {
934 : prinbee::status_t::STATUS_FAILED,
935 : prinbee::status_t::STATUS_UNKNOWN,
936 : prinbee::status_t::STATUS_ACKNOWLEDGED,
937 : },
938 : {
939 : prinbee::status_t::STATUS_FAILED,
940 : prinbee::status_t::STATUS_UNKNOWN,
941 : prinbee::status_t::STATUS_FAILED,
942 : },
943 : {
944 : prinbee::status_t::STATUS_FORWARDED,
945 : prinbee::status_t::STATUS_ACKNOWLEDGED,
946 : prinbee::status_t::STATUS_FAILED,
947 : prinbee::status_t::STATUS_UNKNOWN,
948 : prinbee::status_t::STATUS_COMPLETED,
949 : },
950 : {
951 : prinbee::status_t::STATUS_ACKNOWLEDGED,
952 : prinbee::status_t::STATUS_FAILED,
953 : prinbee::status_t::STATUS_UNKNOWN,
954 : prinbee::status_t::STATUS_COMPLETED,
955 : },
956 : {
957 : prinbee::status_t::STATUS_FAILED,
958 : prinbee::status_t::STATUS_UNKNOWN,
959 : prinbee::status_t::STATUS_COMPLETED,
960 : },
961 : {
962 : prinbee::status_t::STATUS_FORWARDED,
963 : prinbee::status_t::STATUS_ACKNOWLEDGED,
964 : prinbee::status_t::STATUS_COMPLETED,
965 : prinbee::status_t::STATUS_UNKNOWN,
966 : prinbee::status_t::STATUS_FAILED,
967 : },
968 : {
969 : prinbee::status_t::STATUS_ACKNOWLEDGED,
970 : prinbee::status_t::STATUS_COMPLETED,
971 : prinbee::status_t::STATUS_UNKNOWN,
972 : prinbee::status_t::STATUS_FAILED,
973 : },
974 : {
975 : prinbee::status_t::STATUS_COMPLETED,
976 : prinbee::status_t::STATUS_UNKNOWN,
977 : prinbee::status_t::STATUS_FAILED,
978 : },
979 41 : };
980 1 : int count(0);
981 38 : for(auto const & sequence : next_status)
982 : {
983 111 : std::string const path(conf_path("journal_events"));
984 37 : advgetopt::conf_file::reset_conf_files();
985 37 : prinbee::journal j(path);
986 37 : CATCH_REQUIRE(j.is_valid());
987 :
988 37 : ++count;
989 37 : std::cerr << "--- running sequence #" << count << "\n";
990 37 : std::size_t const size(rand() % 10 * 1024 + 1);
991 111 : std::vector<std::uint8_t> data(size);
992 211018 : for(std::size_t idx(0); idx < size; ++idx)
993 : {
994 210981 : data[idx] = static_cast<std::uint8_t>(rand());
995 : }
996 37 : std::string const request_id(SNAP_CATCH2_NAMESPACE::random_string(1, 255));
997 37 : prinbee::in_event event;
998 37 : event.set_request_id(request_id);
999 : {
1000 37 : prinbee::attachment a;
1001 37 : a.set_data(data.data(), data.size());
1002 37 : event.add_attachment(a);
1003 37 : }
1004 37 : snapdev::timespec_ex const event_time(snapdev::now());
1005 37 : snapdev::timespec_ex pass_time(event_time);
1006 37 : CATCH_REQUIRE(j.add_event(event, pass_time));
1007 37 : CATCH_REQUIRE(event_time == pass_time);
1008 :
1009 : // the only way to verify that the event was sent to the journal
1010 : // properly is to read it back using the next_event() function, but
1011 : // since we just added a first even, the next_event() won't find
1012 : // it (i.e. that iterator is already pointing to end()), so we'll
1013 : // need a rewind() call first
1014 : //
1015 37 : prinbee::out_event out_event;
1016 37 : CATCH_REQUIRE_FALSE(j.next_event(out_event));
1017 :
1018 37 : j.rewind();
1019 37 : CATCH_REQUIRE(j.next_event(out_event, true, true));
1020 :
1021 37 : std::string const filename(path + "/journal-0.events");
1022 37 : CATCH_REQUIRE(filename == out_event.get_debug_filename());
1023 37 : CATCH_REQUIRE(8U == out_event.get_debug_offset());
1024 :
1025 37 : CATCH_REQUIRE(request_id == out_event.get_request_id());
1026 37 : CATCH_REQUIRE(prinbee::status_t::STATUS_READY == out_event.get_status());
1027 37 : CATCH_REQUIRE(event_time == out_event.get_event_time());
1028 :
1029 : {
1030 37 : CATCH_REQUIRE(out_event.get_attachment_size() == 1);
1031 37 : prinbee::attachment a(out_event.get_attachment(0));
1032 37 : CATCH_REQUIRE(size == static_cast<std::size_t>(a.size()));
1033 185 : CATCH_REQUIRE_LONG_STRING(std::string(reinterpret_cast<char const *>(data.data()), data.size())
1034 : , std::string(reinterpret_cast<char const *>(a.data()), a.size()));
1035 37 : }
1036 :
1037 37 : CATCH_REQUIRE_FALSE(j.next_event(out_event));
1038 :
1039 111 : CATCH_REQUIRE_FALSE(j.event_forwarded("inexistant"));
1040 111 : CATCH_REQUIRE_FALSE(j.event_acknowledged("inexistant"));
1041 111 : CATCH_REQUIRE_FALSE(j.event_completed("inexistant"));
1042 111 : CATCH_REQUIRE_FALSE(j.event_failed("inexistant"));
1043 :
1044 : // Process sequence
1045 : //
1046 37 : bool expect_success(true);
1047 37 : bool gone(false);
1048 37 : prinbee::status_t last_success(prinbee::status_t::STATUS_UNKNOWN);
1049 166 : for(auto const & status : sequence)
1050 : {
1051 129 : switch(status)
1052 : {
1053 29 : case prinbee::status_t::STATUS_UNKNOWN:
1054 29 : expect_success = false;
1055 29 : continue;
1056 :
1057 0 : case prinbee::status_t::STATUS_READY:
1058 0 : CATCH_REQUIRE(status != prinbee::status_t::STATUS_READY);
1059 0 : break;
1060 :
1061 24 : case prinbee::status_t::STATUS_FORWARDED:
1062 24 : CATCH_REQUIRE(j.event_forwarded(request_id) == expect_success);
1063 24 : break;
1064 :
1065 32 : case prinbee::status_t::STATUS_ACKNOWLEDGED:
1066 32 : CATCH_REQUIRE(j.event_acknowledged(request_id) == expect_success);
1067 32 : break;
1068 :
1069 22 : case prinbee::status_t::STATUS_COMPLETED:
1070 22 : CATCH_REQUIRE(j.event_completed(request_id) == expect_success);
1071 22 : gone = true;
1072 22 : break;
1073 :
1074 22 : case prinbee::status_t::STATUS_FAILED:
1075 22 : CATCH_REQUIRE(j.event_failed(request_id) == expect_success);
1076 22 : gone = true;
1077 22 : break;
1078 :
1079 29 : }
1080 100 : CATCH_REQUIRE_FALSE(j.next_event(out_event));
1081 100 : j.rewind();
1082 100 : if(gone)
1083 : {
1084 : // if gone, a second attempt still fails
1085 : //
1086 56 : CATCH_REQUIRE_FALSE(j.next_event(out_event));
1087 : }
1088 : else
1089 : {
1090 : // not gone yet, all the data is still accessible
1091 : //
1092 44 : prinbee::out_event out_event2;
1093 44 : CATCH_REQUIRE(j.next_event(out_event2));
1094 :
1095 : // at the moment the debug does not get cleared, so we
1096 : // used a separate structure to verify that by default
1097 : // the debug data remains untouched
1098 : //
1099 44 : CATCH_REQUIRE("" == out_event2.get_debug_filename());
1100 44 : CATCH_REQUIRE(0 == out_event2.get_debug_offset());
1101 :
1102 44 : CATCH_REQUIRE(request_id == out_event2.get_request_id());
1103 :
1104 : {
1105 44 : CATCH_REQUIRE(out_event2.get_attachment_size() == 1);
1106 44 : prinbee::attachment a(out_event2.get_attachment(0));
1107 44 : CATCH_REQUIRE(size == static_cast<std::size_t>(a.size()));
1108 220 : CATCH_REQUIRE_LONG_STRING(std::string(reinterpret_cast<char const *>(data.data()), data.size())
1109 : , std::string(reinterpret_cast<char const *>(a.data()), a.size()));
1110 44 : }
1111 :
1112 44 : if(expect_success)
1113 : {
1114 39 : CATCH_REQUIRE(status == out_event2.get_status());
1115 39 : last_success = out_event2.get_status();
1116 : }
1117 : else
1118 : {
1119 : // on error, it does not change
1120 : //
1121 5 : CATCH_REQUIRE(last_success == out_event2.get_status());
1122 : }
1123 44 : CATCH_REQUIRE(event_time == out_event2.get_event_time());
1124 44 : }
1125 :
1126 100 : CATCH_REQUIRE_FALSE(j.next_event(out_event));
1127 : }
1128 37 : }
1129 38 : }
1130 5 : CATCH_END_SECTION()
1131 :
1132 6 : CATCH_START_SECTION("journal_event_status_sequence: verify the delete functionality when empty")
1133 : {
1134 1 : std::random_device rd;
1135 1 : std::mt19937 g(rd());
1136 :
1137 3 : std::string const path(conf_path("journal_delete"));
1138 :
1139 4 : for(int sync(0); sync < 3; ++sync)
1140 : {
1141 : {
1142 3 : advgetopt::conf_file::reset_conf_files();
1143 3 : prinbee::journal j(path);
1144 3 : CATCH_REQUIRE(j.set_file_management(prinbee::file_management_t::FILE_MANAGEMENT_DELETE));
1145 3 : CATCH_REQUIRE(j.set_maximum_events(5));
1146 3 : CATCH_REQUIRE(j.set_sync(static_cast<prinbee::sync_t>(sync)));
1147 3 : CATCH_REQUIRE(j.is_valid());
1148 :
1149 3 : std::vector<int> ids;
1150 33 : for(int id(1); id <= 10; ++id)
1151 : {
1152 30 : std::size_t const size(rand() % 1024 + 1);
1153 90 : std::vector<std::uint8_t> data(size);
1154 14655 : for(std::size_t idx(0); idx < size; ++idx)
1155 : {
1156 14625 : data[idx] = rand();
1157 : }
1158 30 : prinbee::in_event event;
1159 30 : event.set_request_id(prinbee::id_to_string(id));
1160 : {
1161 30 : prinbee::attachment a;
1162 30 : a.set_data(data.data(), size);
1163 30 : event.add_attachment(a);
1164 30 : }
1165 :
1166 30 : snapdev::timespec_ex const event_time(snapdev::now());
1167 30 : snapdev::timespec_ex pass_time(event_time);
1168 30 : CATCH_REQUIRE(j.add_event(event, pass_time));
1169 30 : CATCH_REQUIRE(event_time == pass_time);
1170 :
1171 30 : ids.push_back(id);
1172 30 : }
1173 :
1174 12 : for(int status(0); status < 3; ++status)
1175 : {
1176 9 : std::shuffle(ids.begin(), ids.end(), g);
1177 :
1178 99 : for(auto const & id : ids)
1179 : {
1180 90 : switch(status)
1181 : {
1182 30 : case 0:
1183 30 : CATCH_REQUIRE(j.event_forwarded(prinbee::id_to_string(id)));
1184 30 : break;
1185 :
1186 30 : case 1:
1187 30 : CATCH_REQUIRE(j.event_acknowledged(prinbee::id_to_string(id)));
1188 30 : break;
1189 :
1190 30 : case 2:
1191 30 : CATCH_REQUIRE(j.event_completed(prinbee::id_to_string(id)));
1192 30 : break;
1193 :
1194 0 : default:
1195 0 : CATCH_REQUIRE(!"unknown status");
1196 :
1197 : }
1198 : }
1199 : }
1200 3 : }
1201 :
1202 : // make sure the DELETE happened
1203 : //
1204 12 : for(int idx(0); idx < 3; ++idx)
1205 : {
1206 9 : std::string const filename(event_filename(path, idx));
1207 9 : CATCH_REQUIRE(access(filename.c_str(), R_OK) != 0);
1208 9 : }
1209 :
1210 : // just re-opening does not re-create files
1211 : {
1212 3 : prinbee::journal j(path);
1213 3 : CATCH_REQUIRE(j.empty());
1214 3 : }
1215 :
1216 : // make sure the files were not re-created
1217 : //
1218 12 : for(int idx(0); idx < 3; ++idx)
1219 : {
1220 9 : std::string const filename(event_filename(path, idx));
1221 9 : CATCH_REQUIRE(access(filename.c_str(), R_OK) != 0);
1222 9 : }
1223 : }
1224 1 : }
1225 5 : CATCH_END_SECTION()
1226 :
1227 6 : CATCH_START_SECTION("journal_event_status_sequence: verify the delete functionality when not empty")
1228 : {
1229 1 : std::random_device rd;
1230 1 : std::mt19937 g(rd());
1231 :
1232 4 : for(int sync(0); sync < 3; ++sync)
1233 : {
1234 3 : std::string const name("journal_truncate_delete-" + std::to_string(sync));
1235 3 : std::string const path(conf_path(name));
1236 :
1237 : {
1238 3 : advgetopt::conf_file::reset_conf_files();
1239 3 : prinbee::journal j(path);
1240 3 : CATCH_REQUIRE(j.set_file_management(prinbee::file_management_t::FILE_MANAGEMENT_DELETE));
1241 3 : CATCH_REQUIRE(j.set_maximum_events(5));
1242 3 : CATCH_REQUIRE(j.set_sync(static_cast<prinbee::sync_t>(sync)));
1243 3 : CATCH_REQUIRE(j.is_valid());
1244 :
1245 3 : std::vector<int> ids;
1246 33 : for(int id(1); id <= 10; ++id)
1247 : {
1248 30 : std::size_t const size(rand() % 1024 + 1);
1249 90 : std::vector<std::uint8_t> data(size);
1250 13957 : for(std::size_t idx(0); idx < size; ++idx)
1251 : {
1252 13927 : data[idx] = rand();
1253 : }
1254 30 : prinbee::in_event event;
1255 30 : event.set_request_id(prinbee::id_to_string(id));
1256 : {
1257 30 : prinbee::attachment a;
1258 30 : a.set_data(data.data(), size);
1259 30 : event.add_attachment(a);
1260 30 : }
1261 30 : snapdev::timespec_ex const event_time(snapdev::now());
1262 30 : snapdev::timespec_ex pass_time(event_time);
1263 30 : CATCH_REQUIRE(j.add_event(event, pass_time));
1264 30 : CATCH_REQUIRE(event_time == pass_time);
1265 :
1266 30 : if(rand() % 2 != 0)
1267 : {
1268 13 : ids.push_back(id);
1269 : }
1270 30 : }
1271 3 : if(ids.size() == 10)
1272 : {
1273 : // make sure at least one entry is out
1274 : //
1275 0 : ids.erase(ids.begin() + rand() % 10);
1276 : }
1277 :
1278 12 : for(int status(0); status < 3; ++status)
1279 : {
1280 9 : std::shuffle(ids.begin(), ids.end(), g);
1281 :
1282 48 : for(auto const & id : ids)
1283 : {
1284 39 : switch(status)
1285 : {
1286 13 : case 0:
1287 13 : CATCH_REQUIRE(j.event_forwarded(prinbee::id_to_string(id)));
1288 13 : break;
1289 :
1290 13 : case 1:
1291 13 : CATCH_REQUIRE(j.event_acknowledged(prinbee::id_to_string(id)));
1292 13 : break;
1293 :
1294 13 : case 2:
1295 13 : CATCH_REQUIRE(j.event_completed(prinbee::id_to_string(id)));
1296 13 : break;
1297 :
1298 0 : default:
1299 0 : CATCH_REQUIRE(!"unknown status");
1300 :
1301 : }
1302 : }
1303 : }
1304 3 : }
1305 :
1306 : {
1307 : // make sure the DELETE does not happen when not empty
1308 : //
1309 6 : for(int idx(0); idx < 3; ++idx)
1310 : {
1311 6 : std::string const filename(event_filename(path, idx));
1312 : //CATCH_REQUIRE(access(filename.c_str(), R_OK) != 0);
1313 :
1314 6 : struct stat s = {};
1315 6 : if(stat(filename.c_str(), &s) == 0)
1316 : {
1317 : // main header is 8 bytes (See event_journal_header_t)
1318 : //
1319 3 : CATCH_REQUIRE(s.st_size > 8);
1320 : }
1321 : else
1322 : {
1323 : // we (probably) reached the last file
1324 : //
1325 3 : int const e(errno);
1326 3 : CATCH_REQUIRE(e == ENOENT);
1327 :
1328 : // we at least needed 1 file to save the few entries
1329 : // created above, so idx should never be zero if it
1330 : // worked as expected
1331 : //
1332 3 : CATCH_REQUIRE(idx > 0);
1333 3 : break;
1334 : }
1335 6 : }
1336 : }
1337 3 : }
1338 1 : }
1339 5 : CATCH_END_SECTION()
1340 :
1341 6 : CATCH_START_SECTION("journal_event_status_sequence: verify the truncate functionality")
1342 : {
1343 1 : std::random_device rd;
1344 1 : std::mt19937 g(rd());
1345 :
1346 3 : std::string const path(conf_path("journal_truncate"));
1347 :
1348 4 : for(int sync(0); sync < 3; ++sync)
1349 : {
1350 : {
1351 3 : advgetopt::conf_file::reset_conf_files();
1352 3 : prinbee::journal j(path);
1353 3 : CATCH_REQUIRE(j.set_file_management(prinbee::file_management_t::FILE_MANAGEMENT_TRUNCATE));
1354 3 : CATCH_REQUIRE(j.set_maximum_events(5));
1355 3 : CATCH_REQUIRE(j.set_sync(static_cast<prinbee::sync_t>(sync)));
1356 3 : CATCH_REQUIRE(j.is_valid());
1357 :
1358 3 : std::vector<int> ids;
1359 33 : for(int id(1); id <= 10; ++id)
1360 : {
1361 30 : std::size_t const size(rand() % 1024 + 1);
1362 90 : std::vector<std::uint8_t> data(size);
1363 16234 : for(std::size_t idx(0); idx < size; ++idx)
1364 : {
1365 16204 : data[idx] = rand();
1366 : }
1367 30 : prinbee::in_event event;
1368 30 : event.set_request_id(prinbee::id_to_string(id));
1369 : {
1370 30 : prinbee::attachment a;
1371 30 : a.set_data(data.data(), size);
1372 30 : event.add_attachment(a);
1373 30 : }
1374 30 : snapdev::timespec_ex const event_time(snapdev::now());
1375 30 : snapdev::timespec_ex pass_time(event_time);
1376 30 : CATCH_REQUIRE(j.add_event(event, pass_time));
1377 30 : CATCH_REQUIRE(event_time == pass_time);
1378 :
1379 30 : ids.push_back(id);
1380 30 : }
1381 :
1382 12 : for(int status(0); status < 3; ++status)
1383 : {
1384 9 : std::shuffle(ids.begin(), ids.end(), g);
1385 :
1386 99 : for(auto const & id : ids)
1387 : {
1388 90 : switch(status)
1389 : {
1390 30 : case 0:
1391 30 : CATCH_REQUIRE(j.event_forwarded(prinbee::id_to_string(id)));
1392 30 : break;
1393 :
1394 30 : case 1:
1395 30 : CATCH_REQUIRE(j.event_acknowledged(prinbee::id_to_string(id)));
1396 30 : break;
1397 :
1398 30 : case 2:
1399 30 : CATCH_REQUIRE(j.event_completed(prinbee::id_to_string(id)));
1400 30 : break;
1401 :
1402 0 : default:
1403 0 : CATCH_REQUIRE(!"unknown status");
1404 :
1405 : }
1406 : }
1407 : }
1408 3 : }
1409 :
1410 : {
1411 : // make sure the TRUNCATE happened
1412 : //
1413 6 : for(int idx(0); idx < 3; ++idx)
1414 : {
1415 6 : std::string const filename(event_filename(path, idx));
1416 6 : struct stat s = {};
1417 6 : if(stat(filename.c_str(), &s) == 0)
1418 : {
1419 3 : CATCH_REQUIRE(s.st_size == 8); // main header is 8 bytes (See event_journal_header_t)
1420 : }
1421 : else
1422 : {
1423 : // we (probably) reached the last file
1424 : //
1425 3 : int const e(errno);
1426 3 : CATCH_REQUIRE(e == ENOENT);
1427 :
1428 : // we at least needed 1 file to save the few entries
1429 : // created above, so idx should never be zero if it
1430 : // worked as expected
1431 : //
1432 3 : CATCH_REQUIRE(idx > 0);
1433 3 : break;
1434 : }
1435 6 : }
1436 : }
1437 : }
1438 1 : }
1439 5 : CATCH_END_SECTION()
1440 4 : }
1441 :
1442 :
1443 2 : CATCH_TEST_CASE("journal_event_list", "[journal]")
1444 : {
1445 4 : CATCH_START_SECTION("journal_event_list: verify the unicity of the timestamp")
1446 : {
1447 1 : std::random_device rd;
1448 1 : std::mt19937 g(rd());
1449 :
1450 3 : std::string const name("journal_repeated_event_time");
1451 1 : std::string const path(conf_path(name));
1452 :
1453 1 : snapdev::timespec_ex const start_time(snapdev::now());
1454 1 : snapdev::timespec_ex event_time(start_time);
1455 1 : snapdev::timespec_ex pass_time(event_time);
1456 :
1457 : // we want the ids to be in a different order than the time
1458 : //
1459 1 : std::vector<int> ids;
1460 11 : for(int id(1); id <= 10; ++id)
1461 : {
1462 10 : ids.push_back(id);
1463 : }
1464 1 : std::shuffle(ids.begin(), ids.end(), g);
1465 :
1466 3 : std::vector<snapdev::timespec_ex> times(ids.size());
1467 : {
1468 1 : advgetopt::conf_file::reset_conf_files();
1469 1 : prinbee::journal j(path);
1470 1 : CATCH_REQUIRE(j.set_file_management(prinbee::file_management_t::FILE_MANAGEMENT_DELETE));
1471 1 : CATCH_REQUIRE(j.set_maximum_events(5));
1472 1 : CATCH_REQUIRE(j.is_valid());
1473 1 : CATCH_REQUIRE(j.empty());
1474 :
1475 11 : for(int r(0); r < 10; ++r)
1476 : {
1477 10 : std::size_t const size(rand() % 124 + 1);
1478 30 : std::vector<std::uint8_t> data(size);
1479 574 : for(std::size_t idx(0); idx < size; ++idx)
1480 : {
1481 564 : data[idx] = rand();
1482 : }
1483 10 : prinbee::in_event event;
1484 10 : event.set_request_id(prinbee::id_to_string(ids[r]));
1485 : {
1486 10 : prinbee::attachment a;
1487 10 : a.set_data(data.data(), size);
1488 10 : event.add_attachment(a);
1489 10 : }
1490 10 : CATCH_REQUIRE(j.add_event(event, pass_time));
1491 10 : CATCH_REQUIRE(event_time == pass_time);
1492 10 : CATCH_REQUIRE(j.size() == r + 1ULL);
1493 10 : CATCH_REQUIRE_FALSE(j.empty());
1494 10 : times[ids[r] - 1] = pass_time;
1495 :
1496 10 : ++event_time; // next time it will be incremented by one
1497 10 : }
1498 1 : }
1499 :
1500 : {
1501 1 : prinbee::journal j(path);
1502 1 : event_time = start_time;
1503 11 : for(int r(0); r < 10; ++r)
1504 : {
1505 10 : prinbee::out_event event;
1506 10 : CATCH_REQUIRE(j.next_event(event));
1507 10 : CATCH_REQUIRE(event_time == event.get_event_time());
1508 10 : CATCH_REQUIRE(prinbee::id_to_string(ids[r]) == event.get_request_id());
1509 10 : ++event_time;
1510 10 : }
1511 :
1512 : // make sure we reached the end
1513 : //
1514 : {
1515 1 : prinbee::out_event event;
1516 1 : CATCH_REQUIRE_FALSE(j.next_event(event));
1517 1 : }
1518 1 : }
1519 :
1520 : {
1521 1 : prinbee::journal j(path);
1522 11 : for(int r(0); r < 10; ++r)
1523 : {
1524 10 : prinbee::out_event event;
1525 10 : CATCH_REQUIRE(j.next_event(event, false));
1526 10 : CATCH_REQUIRE(times[r] == event.get_event_time());
1527 10 : CATCH_REQUIRE(prinbee::id_to_string(r + 1) == event.get_request_id());
1528 10 : }
1529 :
1530 : // make sure we reached the end
1531 : //
1532 : {
1533 1 : prinbee::out_event event;
1534 1 : CATCH_REQUIRE_FALSE(j.next_event(event, false));
1535 1 : }
1536 1 : }
1537 1 : }
1538 3 : CATCH_END_SECTION()
1539 :
1540 4 : CATCH_START_SECTION("journal_event_list: fill an event with files & direct data")
1541 : {
1542 1 : std::string const temp(SNAP_CATCH2_NAMESPACE::g_tmp_dir() + "/files_of_mixed_test");
1543 1 : CATCH_REQUIRE(snapdev::mkdir_p(temp) == 0);
1544 :
1545 : // we want a realpath (a.k.a. absolute path) and a relative path
1546 : //
1547 1 : std::string error_msg;
1548 1 : std::string const temp_absolute(snapdev::pathinfo::realpath(temp, error_msg));
1549 1 : std::string const cwd(snapdev::pathinfo::getcwd(error_msg));
1550 1 : std::string const temp_relative(snapdev::pathinfo::relative_path(cwd, temp_absolute));
1551 :
1552 1 : prinbee::attachment_copy_handling_t const mode[] =
1553 : {
1554 : prinbee::attachment_copy_handling_t::ATTACHMENT_COPY_HANDLING_SOFTLINK,
1555 : prinbee::attachment_copy_handling_t::ATTACHMENT_COPY_HANDLING_HARDLINK,
1556 : prinbee::attachment_copy_handling_t::ATTACHMENT_COPY_HANDLING_REFLINK,
1557 : prinbee::attachment_copy_handling_t::ATTACHMENT_COPY_HANDLING_FULL,
1558 : };
1559 :
1560 5 : for(auto const & handling : mode)
1561 : {
1562 24 : for(int count(0); count < 5; ++count)
1563 : {
1564 60 : std::string name("journal_event_with_mixed_data-");
1565 20 : name += std::to_string(count + 1);
1566 20 : name += '-';
1567 20 : name += std::to_string(static_cast<int>(handling));
1568 20 : std::string const path(conf_path(name));
1569 :
1570 20 : std::size_t const max(rand() % 100 + 150);
1571 60 : std::vector<prinbee::data_t> data(max);
1572 :
1573 : // create one event in a journal with many attachments
1574 : // some of which are direct others will be files
1575 : {
1576 20 : advgetopt::conf_file::reset_conf_files();
1577 20 : prinbee::journal j(path);
1578 20 : CATCH_REQUIRE(j.set_file_management(prinbee::file_management_t::FILE_MANAGEMENT_DELETE));
1579 20 : CATCH_REQUIRE(j.set_attachment_copy_handling(handling));
1580 20 : CATCH_REQUIRE(j.is_valid());
1581 20 : CATCH_REQUIRE(j.empty());
1582 :
1583 : // create the event with many attachments
1584 : //
1585 20 : prinbee::in_event event;
1586 20 : event.set_request_id(prinbee::id_to_string(count));
1587 :
1588 20 : std::uint16_t select(0);
1589 3918 : for(std::size_t r(0); r < max; ++r, select >>= 1)
1590 : {
1591 3898 : if((r % 16) == 0)
1592 : {
1593 256 : select = rand();
1594 : }
1595 :
1596 3898 : std::size_t const size(rand() % (20 * 1024) + 1);
1597 3898 : data[r].resize(size);
1598 40137297 : for(std::size_t idx(0); idx < size; ++idx)
1599 : {
1600 40133399 : data[r][idx] = rand();
1601 : }
1602 :
1603 3898 : prinbee::attachment a;
1604 3898 : if((select & 1) == 0)
1605 : {
1606 : // add as direct data
1607 : //
1608 1924 : a.set_data(data[r].data(), size);
1609 : }
1610 : else
1611 : {
1612 : // add as a file
1613 : //
1614 1974 : std::string filename((rand() & 1) == 0 ? temp_absolute : temp_relative);
1615 1974 : filename += "/in-";
1616 1974 : filename += std::to_string(count + 1);
1617 1974 : filename += '-';
1618 1974 : filename += std::to_string(r + 1);
1619 1974 : filename += ".data";
1620 : {
1621 1974 : std::ofstream out(filename);
1622 1974 : CATCH_REQUIRE(out.is_open());
1623 1974 : out.write(reinterpret_cast<char const *>(data[r].data()), size);
1624 1974 : }
1625 1974 : a.set_file(filename);
1626 1974 : }
1627 3898 : event.add_attachment(a);
1628 3898 : }
1629 :
1630 20 : snapdev::timespec_ex event_time(snapdev::now());
1631 20 : CATCH_REQUIRE(j.add_event(event, event_time));
1632 20 : CATCH_REQUIRE(j.size() == 1ULL);
1633 20 : CATCH_REQUIRE_FALSE(j.empty());
1634 20 : }
1635 :
1636 : // now reload that journal and see that we can retrieve all
1637 : // the attachments as we added above
1638 : {
1639 20 : prinbee::journal j(path);
1640 :
1641 20 : prinbee::out_event event;
1642 20 : CATCH_REQUIRE(j.next_event(event));
1643 20 : CATCH_REQUIRE(prinbee::id_to_string(count) == event.get_request_id());
1644 20 : CATCH_REQUIRE(max == event.get_attachment_size());
1645 :
1646 3918 : for(std::size_t r(0); r < max; ++r)
1647 : {
1648 3898 : prinbee::attachment const a(event.get_attachment(r));
1649 3898 : void * d(a.data());
1650 3898 : std::size_t sz(a.size());
1651 3898 : CATCH_REQUIRE(data[r].size() == sz);
1652 3898 : CATCH_REQUIRE(memcmp(d, data[r].data(), sz) == 0);
1653 3898 : }
1654 :
1655 : // make sure we reached the end
1656 20 : CATCH_REQUIRE_FALSE(j.next_event(event));
1657 20 : }
1658 20 : }
1659 : }
1660 1 : }
1661 3 : CATCH_END_SECTION()
1662 2 : }
1663 :
1664 :
1665 1 : CATCH_TEST_CASE("journal_event_files", "[journal]")
1666 : {
1667 3 : CATCH_START_SECTION("journal_event_files: reduce number of files with missing files")
1668 : {
1669 3 : std::string const path(conf_path("journal_event_files"));
1670 1 : advgetopt::conf_file::reset_conf_files();
1671 :
1672 : {
1673 1 : prinbee::journal j(path);
1674 1 : CATCH_REQUIRE(j.is_valid());
1675 1 : CATCH_REQUIRE(j.set_maximum_number_of_files(5));
1676 :
1677 : // 9 to 10 Kb of data per message so we should be able to add
1678 : // between 6 and 7 messages per file; i.e. 14 maximum then we
1679 : // are expecting an error on the add_event()
1680 : //
1681 1 : std::size_t const size(rand() % 1024 + 1);
1682 3 : std::vector<std::uint8_t> data(size);
1683 493 : for(std::size_t idx(0); idx < size; ++idx)
1684 : {
1685 492 : data[idx] = rand();
1686 : }
1687 1 : prinbee::in_event event;
1688 3 : event.set_request_id("id-1");
1689 : {
1690 1 : prinbee::attachment a;
1691 1 : a.set_data(data.data(), size);
1692 1 : event.add_attachment(a);
1693 1 : }
1694 1 : snapdev::timespec_ex event_time(snapdev::now());
1695 1 : CATCH_REQUIRE(j.add_event(event, event_time));
1696 :
1697 : // trying to reduce the number of files works fine when events are
1698 : // only in the very first file
1699 : //
1700 1 : CATCH_REQUIRE(j.set_maximum_number_of_files(prinbee::JOURNAL_MINIMUM_NUMBER_OF_FILES));
1701 1 : }
1702 :
1703 : {
1704 1 : prinbee::journal j(path);
1705 1 : CATCH_REQUIRE(j.is_valid());
1706 1 : CATCH_REQUIRE(j.get_file_management() == prinbee::file_management_t::FILE_MANAGEMENT_KEEP);
1707 1 : }
1708 1 : }
1709 2 : CATCH_END_SECTION()
1710 1 : }
1711 :
1712 :
1713 2 : CATCH_TEST_CASE("journal_attachement", "[journal][attachment]")
1714 : {
1715 4 : CATCH_START_SECTION("journal_attachment: attachment save_data() makes a copy")
1716 : {
1717 101 : for(int i(0); i < 100; ++i)
1718 : {
1719 100 : prinbee::attachment a;
1720 :
1721 100 : std::size_t sz(rand() & 1000 + 1);
1722 300 : std::vector<std::uint8_t> data(sz);
1723 50661 : for(std::size_t idx(0); idx < sz; ++idx)
1724 : {
1725 50561 : data[idx] = rand();
1726 : }
1727 100 : a.save_data(data.data(), sz);
1728 :
1729 : // keep a copy
1730 : //
1731 100 : std::vector<std::uint8_t> copy(data);
1732 :
1733 : // "mess up original"
1734 : //
1735 50661 : for(std::size_t idx(0); idx < sz; ++idx)
1736 : {
1737 : do
1738 : {
1739 50744 : data[idx] = rand();
1740 : }
1741 50744 : while(data[idx] == copy[idx]);
1742 : }
1743 :
1744 100 : CATCH_REQUIRE(static_cast<off_t>(sz) == a.size());
1745 :
1746 100 : void const * p(a.data());
1747 100 : std::uint8_t const * ptr(reinterpret_cast<std::uint8_t const *>(p));
1748 :
1749 50661 : for(std::size_t idx(0); idx < sz; ++idx)
1750 : {
1751 50561 : CATCH_REQUIRE(ptr[idx] == copy[idx]);
1752 : }
1753 100 : }
1754 : }
1755 3 : CATCH_END_SECTION()
1756 :
1757 4 : CATCH_START_SECTION("journal_attachment: attachment set_file() saves a filename and reads its data")
1758 : {
1759 3 : std::string const content("This is the file.\n");
1760 1 : std::string const path(SNAP_CATCH2_NAMESPACE::g_tmp_dir() + "/set_file-test-file.txt");
1761 : {
1762 1 : std::ofstream out(path);
1763 1 : CATCH_REQUIRE(out.is_open());
1764 1 : out << content;
1765 1 : }
1766 1 : prinbee::attachment a;
1767 1 : a.set_file(path);
1768 1 : CATCH_REQUIRE_FALSE(a.empty());
1769 1 : CATCH_REQUIRE(a.size() == static_cast<off_t>(content.length()));
1770 1 : CATCH_REQUIRE(a.is_file());
1771 1 : CATCH_REQUIRE(a.filename() == path);
1772 :
1773 : // the a.data() will read the file in memory inside the attachment
1774 : // object then we can compare and make sure it is equal to our input
1775 : //
1776 1 : void const * p(a.data());
1777 1 : char const * ptr(reinterpret_cast<char const *>(p));
1778 19 : for(std::size_t idx(0); idx < content.length(); ++idx)
1779 : {
1780 18 : CATCH_REQUIRE(ptr[idx] == content[idx]);
1781 : }
1782 1 : }
1783 3 : CATCH_END_SECTION()
1784 2 : }
1785 :
1786 :
1787 29 : CATCH_TEST_CASE("journal_errors", "[journal][error]")
1788 : {
1789 31 : CATCH_START_SECTION("journal_errors: trying to re-add the same event multiple times fails")
1790 : {
1791 3 : std::string const path(conf_path("journal_duplicate_event"));
1792 1 : advgetopt::conf_file::reset_conf_files();
1793 1 : prinbee::journal j(path);
1794 1 : CATCH_REQUIRE(j.is_valid());
1795 :
1796 1 : std::size_t const size(rand() % 10 * 1024 + 1);
1797 3 : std::vector<std::uint8_t> data(size);
1798 3074 : for(std::size_t idx(0); idx < size; ++idx)
1799 : {
1800 3073 : data[idx] = rand();
1801 : }
1802 1 : prinbee::in_event event;
1803 3 : event.set_request_id("id-123");
1804 : {
1805 1 : prinbee::attachment a;
1806 1 : a.set_data(data.data(), size);
1807 1 : event.add_attachment(a);
1808 1 : }
1809 1 : snapdev::timespec_ex event_time(snapdev::now());
1810 1 : CATCH_REQUIRE(j.add_event(event, event_time));
1811 :
1812 : // if we try again, it fails
1813 : //
1814 1 : event_time = snapdev::now();
1815 1 : CATCH_REQUIRE_FALSE(j.add_event(event, event_time));
1816 1 : }
1817 30 : CATCH_END_SECTION()
1818 :
1819 31 : CATCH_START_SECTION("journal_errors: attachment negative size (set_data)")
1820 : {
1821 1 : prinbee::attachment a;
1822 1 : std::uint8_t buf[256];
1823 :
1824 101 : for(int i(0); i < 100; ++i)
1825 : {
1826 100 : off_t size(0);
1827 200 : while(size >= 0)
1828 : {
1829 100 : size = -rand();
1830 : }
1831 300 : CATCH_REQUIRE_THROWS_MATCHES(
1832 : a.set_data(buf, size)
1833 : , prinbee::invalid_parameter
1834 : , Catch::Matchers::ExceptionMessage("prinbee_exception: attachment cannot have a negative size."));
1835 : }
1836 1 : }
1837 30 : CATCH_END_SECTION()
1838 :
1839 31 : CATCH_START_SECTION("journal_errors: attachment invalid size / pointer combo (set_data)")
1840 : {
1841 1 : prinbee::attachment a;
1842 :
1843 101 : for(int i(0); i < 100; ++i)
1844 : {
1845 100 : off_t size(0);
1846 200 : while(size <= 0)
1847 : {
1848 100 : size = rand();
1849 : }
1850 300 : CATCH_REQUIRE_THROWS_MATCHES(
1851 : a.set_data(nullptr, size)
1852 : , prinbee::invalid_parameter
1853 : , Catch::Matchers::ExceptionMessage("prinbee_exception: attachment with a size > 0 must have a non null data pointer."));
1854 : }
1855 1 : }
1856 30 : CATCH_END_SECTION()
1857 :
1858 31 : CATCH_START_SECTION("journal_errors: attachment negative size (save_data)")
1859 : {
1860 1 : prinbee::attachment a;
1861 1 : std::uint8_t buf[256];
1862 :
1863 101 : for(int i(0); i < 100; ++i)
1864 : {
1865 100 : off_t size(0);
1866 200 : while(size >= 0)
1867 : {
1868 100 : size = -rand();
1869 : }
1870 300 : CATCH_REQUIRE_THROWS_MATCHES(
1871 : a.save_data(buf, size)
1872 : , prinbee::invalid_parameter
1873 : , Catch::Matchers::ExceptionMessage("prinbee_exception: attachment cannot have a negative size."));
1874 : }
1875 1 : }
1876 30 : CATCH_END_SECTION()
1877 :
1878 31 : CATCH_START_SECTION("journal_errors: attachment invalid size / pointer combo (save_data)")
1879 : {
1880 1 : prinbee::attachment a;
1881 :
1882 101 : for(int i(0); i < 100; ++i)
1883 : {
1884 100 : off_t size(0);
1885 200 : while(size <= 0)
1886 : {
1887 100 : size = rand();
1888 : }
1889 300 : CATCH_REQUIRE_THROWS_MATCHES(
1890 : a.save_data(nullptr, size)
1891 : , prinbee::invalid_parameter
1892 : , Catch::Matchers::ExceptionMessage("prinbee_exception: attachment with a size > 0 must have a non null data pointer (2)."));
1893 : }
1894 1 : }
1895 30 : CATCH_END_SECTION()
1896 :
1897 31 : CATCH_START_SECTION("journal_errors: request_id too long")
1898 : {
1899 3 : std::string const path(conf_path("journal_large_event"));
1900 1 : advgetopt::conf_file::reset_conf_files();
1901 1 : prinbee::journal j(path);
1902 1 : CATCH_REQUIRE(j.is_valid());
1903 :
1904 1 : std::size_t const size(rand() % 10 * 1024 + 1);
1905 3 : std::vector<std::uint8_t> data(size);
1906 6146 : for(std::size_t idx(0); idx < size; ++idx)
1907 : {
1908 6145 : data[idx] = rand();
1909 : }
1910 1 : prinbee::in_event event;
1911 3 : event.set_request_id("for a request identifier too be way to long here it needs to be some two hundred and fifty six or way more characters which means this is a really long sentence to make it happen and well, since I have a lot of imagination that is really no issue at all, right?");
1912 : {
1913 1 : prinbee::attachment a;
1914 1 : a.set_data(data.data(), size);
1915 1 : event.add_attachment(a);
1916 1 : }
1917 1 : snapdev::timespec_ex event_time(snapdev::now());
1918 1 : CATCH_REQUIRE_FALSE(j.add_event(event, event_time));
1919 1 : }
1920 30 : CATCH_END_SECTION()
1921 :
1922 31 : CATCH_START_SECTION("journal_errors: invalid number of files")
1923 : {
1924 3 : std::string const path(conf_path("journal_out_of_range"));
1925 1 : advgetopt::conf_file::reset_conf_files();
1926 1 : prinbee::journal j(path);
1927 :
1928 3 : for(std::uint32_t count(0); count < prinbee::JOURNAL_MINIMUM_NUMBER_OF_FILES; ++count)
1929 : {
1930 2 : std::stringstream ss;
1931 : ss << "out_of_range: maximum number of files ("
1932 4 : << std::to_string(count)
1933 : << ") is out of range: ["
1934 4 : << std::to_string(prinbee::JOURNAL_MINIMUM_NUMBER_OF_FILES)
1935 : << ".."
1936 4 : << std::to_string(prinbee::JOURNAL_MAXIMUM_NUMBER_OF_FILES)
1937 8 : << "]";
1938 2 : CATCH_REQUIRE_THROWS_MATCHES(
1939 : j.set_maximum_number_of_files(count)
1940 : , prinbee::out_of_range
1941 : , Catch::Matchers::ExceptionMessage(ss.str()));
1942 2 : }
1943 100 : for(std::uint32_t count(prinbee::JOURNAL_MAXIMUM_NUMBER_OF_FILES + 1); count < prinbee::JOURNAL_MAXIMUM_NUMBER_OF_FILES + 100; ++count)
1944 : {
1945 99 : std::stringstream ss;
1946 : ss << "out_of_range: maximum number of files ("
1947 198 : << std::to_string(count)
1948 : << ") is out of range: ["
1949 198 : << std::to_string(prinbee::JOURNAL_MINIMUM_NUMBER_OF_FILES)
1950 : << ".."
1951 198 : << std::to_string(prinbee::JOURNAL_MAXIMUM_NUMBER_OF_FILES)
1952 396 : << "]";
1953 99 : CATCH_REQUIRE_THROWS_MATCHES(
1954 : j.set_maximum_number_of_files(count)
1955 : , prinbee::out_of_range
1956 : , Catch::Matchers::ExceptionMessage(ss.str()));
1957 99 : }
1958 1 : }
1959 30 : CATCH_END_SECTION()
1960 :
1961 31 : CATCH_START_SECTION("journal_errors: missing folder")
1962 : {
1963 3 : std::string const path(conf_path("journal_missing", true));
1964 1 : chmod(path.c_str(), 0); // remove permissions so the add_event() fails with EPERM
1965 1 : advgetopt::conf_file::reset_conf_files();
1966 1 : prinbee::journal j(path);
1967 1 : CATCH_REQUIRE(j.is_valid());
1968 :
1969 1 : std::size_t const size(rand() % 10 * 1024 + 1);
1970 3 : std::vector<std::uint8_t> data(size);
1971 3074 : for(std::size_t idx(0); idx < size; ++idx)
1972 : {
1973 3073 : data[idx] = rand();
1974 : }
1975 1 : prinbee::in_event event;
1976 3 : event.set_request_id("id-123");
1977 : {
1978 1 : prinbee::attachment a;
1979 1 : a.set_data(data.data(), size);
1980 1 : event.add_attachment(a);
1981 1 : }
1982 1 : snapdev::timespec_ex event_time(snapdev::now());
1983 1 : CATCH_REQUIRE_FALSE(j.add_event(event, event_time));
1984 1 : }
1985 30 : CATCH_END_SECTION()
1986 :
1987 31 : CATCH_START_SECTION("journal_errors: filled up journal (small size)")
1988 : {
1989 3 : std::string const path(conf_path("journal_filled"));
1990 1 : advgetopt::conf_file::reset_conf_files();
1991 1 : prinbee::journal j(path);
1992 1 : CATCH_REQUIRE(j.is_valid());
1993 :
1994 1 : j.set_maximum_file_size(prinbee::JOURNAL_MINIMUM_FILE_SIZE);
1995 1 : j.set_inline_attachment_size_threshold(prinbee::JOURNAL_INLINE_ATTACHMENT_SIZE_MAXIMUM_THRESHOLD);
1996 :
1997 : // 9 to 10 Kb of data per message so we should be able to add
1998 : // between 6 and 7 messages per file; i.e. 14 maximum then we
1999 : // are expecting an error on the add_event()
2000 : //
2001 1 : std::vector<std::uint8_t> data;
2002 1 : bool success(false);
2003 1 : int count(0);
2004 13 : for(; count < 15; ++count)
2005 : {
2006 13 : std::size_t const size(rand() % 1024 + 1024 * 9);
2007 13 : data.resize(size);
2008 128098 : for(std::size_t idx(0); idx < size; ++idx)
2009 : {
2010 128085 : data[idx] = rand();
2011 : }
2012 13 : prinbee::in_event event;
2013 13 : event.set_request_id("id-" + std::to_string(count));
2014 : {
2015 13 : prinbee::attachment a;
2016 13 : a.set_data(data.data(), size);
2017 13 : event.add_attachment(a);
2018 13 : };
2019 13 : snapdev::timespec_ex event_time(snapdev::now());
2020 13 : bool const r(j.add_event(event, event_time));
2021 13 : if(!r)
2022 : {
2023 1 : success = true;
2024 1 : break;
2025 : }
2026 13 : }
2027 1 : CATCH_REQUIRE(success);
2028 :
2029 : // mark a few as complete and attempt another insert, it should
2030 : // still fail
2031 : //
2032 3 : std::vector<int> ids(count);
2033 13 : for(int mark_complete(0); mark_complete < count; ++mark_complete)
2034 : {
2035 12 : ids[mark_complete] = mark_complete;
2036 : }
2037 1 : std::random_device rd;
2038 1 : std::mt19937 g(rd());
2039 1 : std::shuffle(ids.begin(), ids.end(), g);
2040 1 : int const complete_count(rand() % 3 + 1);
2041 3 : for(int idx(0); idx < complete_count; ++idx)
2042 : {
2043 2 : std::string const request_id("id-" + std::to_string(ids[idx]));
2044 2 : if((rand() & 1) == 0)
2045 : {
2046 1 : CATCH_REQUIRE(j.event_completed(request_id));
2047 : }
2048 : else
2049 : {
2050 1 : CATCH_REQUIRE(j.event_failed(request_id));
2051 : }
2052 2 : }
2053 :
2054 : {
2055 : // as is, it still overflows (because we are not compressing)
2056 : //
2057 : //std::size_t const size(rand() % 1024 + 1024 * 8);
2058 : //std::vector<std::uint8_t> data(size);
2059 : //for(std::size_t idx(0); idx < size; ++idx)
2060 : //{
2061 : // data[idx] = rand();
2062 : //}
2063 1 : prinbee::in_event event;
2064 3 : event.set_request_id("id-extra");
2065 : {
2066 1 : prinbee::attachment a;
2067 1 : a.set_data(data.data(), data.size());
2068 1 : event.add_attachment(a);
2069 1 : };
2070 1 : snapdev::timespec_ex event_time(snapdev::now());
2071 1 : CATCH_REQUIRE_FALSE(j.add_event(event, event_time));
2072 :
2073 : // however, if we turn on the "allow compression" flag, it works
2074 : //
2075 1 : CATCH_REQUIRE(j.set_compress_when_full(true));
2076 1 : CATCH_REQUIRE(j.add_event(event, event_time));
2077 1 : }
2078 1 : }
2079 30 : CATCH_END_SECTION()
2080 :
2081 31 : CATCH_START_SECTION("journal_errors: fail with invalid size as ID is not complete and data is missing")
2082 : {
2083 3 : std::string const name("journal_incomplete_id");
2084 1 : std::string const path(conf_path(name));
2085 :
2086 : // create a journal file with one valid event
2087 : {
2088 1 : advgetopt::conf_file::reset_conf_files();
2089 1 : prinbee::journal j(path);
2090 1 : CATCH_REQUIRE(j.is_valid());
2091 1 : CATCH_REQUIRE(j.empty());
2092 :
2093 1 : std::uint8_t data[20] = {};
2094 1 : prinbee::in_event event;
2095 3 : event.set_request_id("this-id");
2096 : {
2097 1 : prinbee::attachment a;
2098 1 : a.set_data(data, sizeof(data));
2099 1 : event.add_attachment(a);
2100 1 : }
2101 1 : snapdev::timespec_ex now(snapdev::now());
2102 1 : CATCH_REQUIRE(j.add_event(event, now));
2103 1 : CATCH_REQUIRE(j.size() == 1ULL);
2104 1 : CATCH_REQUIRE_FALSE(j.empty());
2105 1 : }
2106 :
2107 : // open that journal and add a broken header (invalid identifier)
2108 : {
2109 1 : std::string const filename(event_filename(path, 0));
2110 1 : std::ofstream out(filename, std::ios::app | std::ios::binary);
2111 : char data[1];
2112 1 : std::size_t const size(32 /* == sizeof(header) */
2113 : + 1 * sizeof(prinbee::attachment_offsets_t)
2114 : + sizeof("next-id") - 1
2115 : + sizeof(data));
2116 1 : CATCH_REQUIRE(size < 256ULL);
2117 1 : std::uint8_t const header[] = {
2118 : 'e', 'v', // f_magic
2119 : static_cast<std::uint8_t>(prinbee::status_t::STATUS_READY), // f_status
2120 : sizeof("next-id") - 1, // f_request_id_size
2121 : size, 0, 0, 0, // f_size
2122 : 0, 0, 0, 0, 0, 0, 0, 0, // f_time
2123 : 0, 0, 0, 0, 0, 0, 0, 0,
2124 : 1, // f_attachment_offsets
2125 : 0, 0, 0, 0, 0, 0, 0, // f_pad[7]
2126 : };
2127 1 : out.write(reinterpret_cast<char const *>(header), sizeof(header));
2128 1 : prinbee::attachment_offsets_t const offset(
2129 : sizeof(header)
2130 : + 1 * sizeof(prinbee::attachment_offsets_t) // itself
2131 : + sizeof("next-id") - 1);
2132 1 : out.write(reinterpret_cast<char const *>(&offset), sizeof(offset));
2133 1 : out.write("next", 4); // <-- only 4 bytes
2134 1 : }
2135 :
2136 : {
2137 1 : prinbee::journal j(path);
2138 1 : prinbee::out_event event;
2139 :
2140 : // we find the first valid event
2141 : //
2142 1 : CATCH_REQUIRE(j.next_event(event));
2143 1 : CATCH_REQUIRE("this-id" == event.get_request_id());
2144 :
2145 : // make sure we reached the end; the second event was invalid
2146 : //
2147 1 : CATCH_REQUIRE_FALSE(j.next_event(event));
2148 1 : }
2149 1 : }
2150 30 : CATCH_END_SECTION()
2151 :
2152 31 : CATCH_START_SECTION("journal_errors: invalid event date & time")
2153 : {
2154 3 : std::string const name("journal_wrong_time");
2155 1 : std::string const path(conf_path(name));
2156 :
2157 : // create a journal file with one valid event
2158 : {
2159 1 : advgetopt::conf_file::reset_conf_files();
2160 1 : prinbee::journal j(path);
2161 1 : CATCH_REQUIRE(j.is_valid());
2162 1 : CATCH_REQUIRE(j.empty());
2163 :
2164 1 : std::uint8_t data[20] = {};
2165 1 : prinbee::in_event event;
2166 3 : event.set_request_id("this-id");
2167 : {
2168 1 : prinbee::attachment a;
2169 1 : a.set_data(data, sizeof(data));
2170 1 : event.add_attachment(a);
2171 1 : }
2172 1 : snapdev::timespec_ex now(snapdev::now());
2173 1 : CATCH_REQUIRE(j.add_event(event, now));
2174 1 : CATCH_REQUIRE(j.size() == 1ULL);
2175 1 : CATCH_REQUIRE_FALSE(j.empty());
2176 :
2177 : // trying to add an event in the future fails
2178 : //
2179 1 : snapdev::timespec_ex soon(snapdev::now());
2180 1 : soon += snapdev::timespec_ex(100, 0); // 100 seconds in the future
2181 3 : event.set_request_id("future");
2182 1 : CATCH_REQUIRE_FALSE(j.add_event(event, soon));
2183 1 : }
2184 :
2185 : // open that journal and add a broken header (invalid date & time)
2186 : {
2187 1 : std::string const filename(event_filename(path, 0));
2188 1 : std::ofstream out(filename, std::ios::app | std::ios::binary);
2189 1 : snapdev::timespec_ex soon(snapdev::now());
2190 1 : soon += snapdev::timespec_ex(100, 0); // 100 seconds in the future
2191 1 : char data[32]; // content not used by the test, no need to initialized
2192 1 : std::size_t const size(32 /* == sizeof(header)*/
2193 : + 1 * sizeof(prinbee::attachment_offsets_t)
2194 : + sizeof("next-id") - 1
2195 : + sizeof(data));
2196 1 : CATCH_REQUIRE(size < 256ULL);
2197 1 : std::uint8_t const header[] = {
2198 : 'e', 'v', // f_magic
2199 : static_cast<std::uint8_t>(prinbee::status_t::STATUS_READY), // f_status
2200 : sizeof("next-id") - 1, // f_request_id_size
2201 : size, 0, 0, 0, // f_size
2202 1 : static_cast<std::uint8_t>(soon.tv_sec >> 0), // f_time
2203 1 : static_cast<std::uint8_t>(soon.tv_sec >> 8),
2204 1 : static_cast<std::uint8_t>(soon.tv_sec >> 16),
2205 1 : static_cast<std::uint8_t>(soon.tv_sec >> 24),
2206 1 : static_cast<std::uint8_t>(soon.tv_sec >> 32),
2207 1 : static_cast<std::uint8_t>(soon.tv_sec >> 40),
2208 1 : static_cast<std::uint8_t>(soon.tv_sec >> 48),
2209 1 : static_cast<std::uint8_t>(soon.tv_sec >> 56),
2210 1 : static_cast<std::uint8_t>(soon.tv_nsec >> 0),
2211 1 : static_cast<std::uint8_t>(soon.tv_nsec >> 8),
2212 1 : static_cast<std::uint8_t>(soon.tv_nsec >> 16),
2213 1 : static_cast<std::uint8_t>(soon.tv_nsec >> 24),
2214 1 : static_cast<std::uint8_t>(soon.tv_nsec >> 32),
2215 1 : static_cast<std::uint8_t>(soon.tv_nsec >> 40),
2216 1 : static_cast<std::uint8_t>(soon.tv_nsec >> 48),
2217 1 : static_cast<std::uint8_t>(soon.tv_nsec >> 56),
2218 : 1, // f_attachment_count
2219 : 0, 0, 0, 0, 0, 0, 0, // f_pad[7]
2220 1 : };
2221 1 : out.write(reinterpret_cast<char const *>(header), sizeof(header));
2222 1 : prinbee::attachment_offsets_t const offset(
2223 : sizeof(header)
2224 : + 1 * sizeof(prinbee::attachment_offsets_t) // itself
2225 : + sizeof("next-id") - 1);
2226 1 : out.write(reinterpret_cast<char const *>(&offset), sizeof(offset));
2227 1 : out.write("next-id", sizeof("next-id") - 1);
2228 1 : out.write(data, sizeof(data));
2229 1 : }
2230 :
2231 : {
2232 1 : prinbee::journal j(path);
2233 1 : prinbee::out_event event;
2234 :
2235 : // we find the first valid event
2236 : //
2237 1 : CATCH_REQUIRE(j.next_event(event));
2238 1 : CATCH_REQUIRE("this-id" == event.get_request_id());
2239 :
2240 : // make sure we reached the end; the second event was invalid
2241 : //
2242 1 : CATCH_REQUIRE_FALSE(j.next_event(event));
2243 1 : }
2244 1 : }
2245 30 : CATCH_END_SECTION()
2246 :
2247 31 : CATCH_START_SECTION("journal_errors: invalid end marker")
2248 : {
2249 : // to test the conversions, we need multiple cases so use a loop
2250 : //
2251 : struct marker
2252 : {
2253 : char a;
2254 : char b;
2255 : };
2256 1 : std::vector<marker> invalid_markers{
2257 : { 'n', 'g' },
2258 : { '\0', '@' }, // starts well, bad ending
2259 : { 0x03, 0x07 },
2260 : { 0x7F, static_cast<char>(0x97) },
2261 3 : };
2262 1 : int count(0);
2263 5 : for(auto const & bad_marker : invalid_markers)
2264 : {
2265 4 : ++count;
2266 12 : std::string name("journal_invalid_end_marker-");
2267 4 : name += std::to_string(count);
2268 4 : std::string const path(conf_path(name));
2269 :
2270 : // create a journal file with one valid event
2271 : {
2272 4 : advgetopt::conf_file::reset_conf_files();
2273 4 : prinbee::journal j(path);
2274 4 : CATCH_REQUIRE(j.is_valid());
2275 4 : CATCH_REQUIRE(j.empty());
2276 :
2277 4 : std::uint8_t data[20] = {};
2278 4 : prinbee::in_event event;
2279 12 : event.set_request_id("this-id");
2280 : {
2281 4 : prinbee::attachment a;
2282 4 : a.set_data(data, sizeof(data));
2283 4 : event.add_attachment(a);
2284 4 : }
2285 4 : snapdev::timespec_ex now(snapdev::now());
2286 4 : CATCH_REQUIRE(j.add_event(event, now));
2287 4 : CATCH_REQUIRE(j.size() == 1ULL);
2288 4 : CATCH_REQUIRE_FALSE(j.empty());
2289 4 : }
2290 :
2291 : // open that journal and add a broken end marker
2292 : // the header and data are otherwise valid
2293 : {
2294 4 : std::string const filename(event_filename(path, 0));
2295 4 : std::ofstream out(filename, std::ios::app | std::ios::binary);
2296 4 : char data[1];
2297 4 : std::size_t const size(32 /* == sizeof(header) */
2298 : + 1 * sizeof(prinbee::attachment_offsets_t)
2299 : + sizeof("next-id") - 1
2300 : + sizeof(data));
2301 4 : CATCH_REQUIRE(size < 256ULL);
2302 4 : std::uint8_t const header[] = {
2303 4 : static_cast<std::uint8_t>(bad_marker.a), // f_magic
2304 4 : static_cast<std::uint8_t>(bad_marker.b),
2305 : static_cast<std::uint8_t>(prinbee::status_t::STATUS_READY), // f_status
2306 : sizeof("next-id") - 1, // f_request_id_size
2307 : size, 0, 0, 0, // f_size
2308 : 0, 0, 0, 0, 0, 0, 0, 0, // f_time
2309 : 0, 0, 0, 0, 0, 0, 0, 0,
2310 : 1, // f_attachment_offsets
2311 : 0, 0, 0, 0, 0, 0, 0, // f_pad[7]
2312 4 : };
2313 4 : out.write(reinterpret_cast<char const *>(header), sizeof(header));
2314 4 : prinbee::attachment_offsets_t const offset(
2315 : sizeof(header)
2316 : + 1 * sizeof(prinbee::attachment_offsets_t) // itself
2317 : + sizeof("next-id") - 1);
2318 4 : out.write(reinterpret_cast<char const *>(&offset), sizeof(offset));
2319 4 : out.write("next-id", sizeof("next-id") - 1);
2320 4 : out.write(data, sizeof(data));
2321 4 : }
2322 :
2323 : {
2324 4 : prinbee::journal j(path);
2325 4 : prinbee::out_event event;
2326 :
2327 : // we find the first valid event
2328 : //
2329 4 : CATCH_REQUIRE(j.next_event(event));
2330 4 : CATCH_REQUIRE("this-id" == event.get_request_id());
2331 :
2332 : // make sure we reached the end; the second event was invalid
2333 : //
2334 4 : CATCH_REQUIRE_FALSE(j.next_event(event));
2335 4 : }
2336 4 : }
2337 1 : }
2338 30 : CATCH_END_SECTION()
2339 :
2340 31 : CATCH_START_SECTION("journal_errors: incomplete header")
2341 : {
2342 6 : for(int idx(0); idx < 5; ++idx)
2343 : {
2344 15 : std::string name("journal_incomplete_header-");
2345 5 : name += std::to_string(idx + 1);
2346 5 : std::string const path(conf_path(name));
2347 :
2348 : // create a journal file with one valid event
2349 : {
2350 5 : advgetopt::conf_file::reset_conf_files();
2351 5 : prinbee::journal j(path);
2352 5 : CATCH_REQUIRE(j.is_valid());
2353 5 : CATCH_REQUIRE(j.empty());
2354 :
2355 5 : std::uint8_t data[20] = {};
2356 5 : prinbee::in_event event;
2357 15 : event.set_request_id("this-id");
2358 : {
2359 5 : prinbee::attachment a;
2360 5 : a.set_data(data, sizeof(data));
2361 5 : event.add_attachment(a);
2362 5 : };
2363 5 : snapdev::timespec_ex now(snapdev::now());
2364 5 : CATCH_REQUIRE(j.add_event(event, now));
2365 5 : CATCH_REQUIRE(j.size() == 1ULL);
2366 5 : CATCH_REQUIRE_FALSE(j.empty());
2367 5 : }
2368 :
2369 : // create a broken header (too small by 1 or more bytes)
2370 : {
2371 5 : std::string const filename(event_filename(path, 0));
2372 5 : std::ofstream out(filename, std::ios::app | std::ios::binary);
2373 : char data[1];
2374 5 : std::size_t const size(32 /* == sizeof(header) */
2375 : + 1 * sizeof(prinbee::attachment_offsets_t)
2376 : + sizeof("next-id") - 1
2377 : + sizeof(data));
2378 5 : CATCH_REQUIRE(size < 256ULL);
2379 5 : std::uint8_t const header[] = {
2380 : 'e', 'v', // f_magic
2381 : static_cast<std::uint8_t>(prinbee::status_t::STATUS_READY), // f_status
2382 : sizeof("next-id") - 1, // f_request_id_size
2383 : size, 0, 0, 0, // f_size
2384 : 0, 0, 0, 0, 0, 0, 0, 0, // f_time
2385 : 0, 0, 0, 0, 0, 0, 0, 0,
2386 : 1, // f_attachment_offsets
2387 : 0, 0, 0, 0, 0, 0, 0, // f_pad[7]
2388 : };
2389 5 : std::size_t const bad_size(rand() % (sizeof(header) - 1) + 1);
2390 5 : out.write(reinterpret_cast<char const *>(header), bad_size);
2391 5 : }
2392 :
2393 : {
2394 5 : prinbee::journal j(path);
2395 5 : prinbee::out_event event;
2396 :
2397 : // we find the first valid event
2398 : //
2399 5 : CATCH_REQUIRE(j.next_event(event));
2400 5 : CATCH_REQUIRE("this-id" == event.get_request_id());
2401 :
2402 : // make sure we reached the end; the second event was invalid
2403 : // note: in this case we do not get an error message
2404 : //
2405 5 : CATCH_REQUIRE_FALSE(j.next_event(event));
2406 5 : }
2407 5 : }
2408 : }
2409 30 : CATCH_END_SECTION()
2410 :
2411 31 : CATCH_START_SECTION("journal_errors: invalid magic (start of file header magic tampered)")
2412 : {
2413 7 : for(int pos(0); pos < 6; ++pos)
2414 : {
2415 18 : std::string name("journal_invalid_magic-");
2416 6 : name += std::to_string(pos);
2417 6 : std::string const path(conf_path(name));
2418 :
2419 : // create a journal file with one valid event
2420 : // (without the event, it does not create the file)
2421 : {
2422 6 : advgetopt::conf_file::reset_conf_files();
2423 6 : prinbee::journal j(path);
2424 6 : CATCH_REQUIRE(j.is_valid());
2425 6 : CATCH_REQUIRE(j.empty());
2426 :
2427 6 : std::uint8_t data[20] = {};
2428 6 : prinbee::in_event event;
2429 18 : event.set_request_id("this-id");
2430 : {
2431 6 : prinbee::attachment a;
2432 6 : a.set_data(data, sizeof(data));
2433 6 : event.add_attachment(a);
2434 6 : }
2435 6 : snapdev::timespec_ex now(snapdev::now());
2436 6 : CATCH_REQUIRE(j.add_event(event, now));
2437 6 : CATCH_REQUIRE(j.size() == 1ULL);
2438 6 : CATCH_REQUIRE_FALSE(j.empty());
2439 6 : }
2440 :
2441 : // smash one of the header characters
2442 : {
2443 6 : std::string const filename(event_filename(path, 0));
2444 6 : std::fstream out(filename);
2445 6 : out.seekp(pos);
2446 6 : std::string header;
2447 6 : header += 'E';
2448 6 : header += 'V';
2449 6 : header += 'T';
2450 6 : header += 'J';
2451 6 : header += '\1';
2452 6 : header += '\0';
2453 : for(;;)
2454 : {
2455 6 : char c(static_cast<char>(rand()));
2456 6 : if(c != header[pos])
2457 : {
2458 6 : out.write(&c, 1);
2459 6 : break;
2460 : }
2461 0 : }
2462 6 : }
2463 :
2464 : {
2465 6 : prinbee::journal j(path);
2466 6 : prinbee::out_event event;
2467 :
2468 : // we find no events
2469 : //
2470 6 : CATCH_REQUIRE_FALSE(j.next_event(event));
2471 6 : }
2472 6 : }
2473 : }
2474 30 : CATCH_END_SECTION()
2475 :
2476 31 : CATCH_START_SECTION("journal_errors: short magic (start of file header)")
2477 : {
2478 9 : for(int size(0); size < 8; ++size)
2479 : {
2480 24 : std::string name("journal_short_magic-");
2481 8 : name += std::to_string(size);
2482 8 : std::string const path(conf_path(name));
2483 :
2484 : // create a journal file with one valid event
2485 : // (without the event, it does not create the file)
2486 : {
2487 8 : advgetopt::conf_file::reset_conf_files();
2488 8 : prinbee::journal j(path);
2489 8 : CATCH_REQUIRE(j.is_valid());
2490 8 : CATCH_REQUIRE(j.empty());
2491 :
2492 8 : std::uint8_t data[20] = {};
2493 8 : prinbee::in_event event;
2494 24 : event.set_request_id("this-id");
2495 : {
2496 8 : prinbee::attachment a;
2497 8 : a.set_data(data, sizeof(data));
2498 8 : event.add_attachment(a);
2499 8 : };
2500 8 : snapdev::timespec_ex now(snapdev::now());
2501 8 : CATCH_REQUIRE(j.add_event(event, now));
2502 8 : CATCH_REQUIRE(j.size() == 1ULL);
2503 8 : CATCH_REQUIRE_FALSE(j.empty());
2504 8 : }
2505 :
2506 : // truncate the header to `size` bytes
2507 : {
2508 8 : std::string const filename(event_filename(path, 0));
2509 8 : CATCH_REQUIRE(truncate(filename.c_str(), size) == 0);
2510 8 : }
2511 :
2512 : {
2513 8 : prinbee::journal j(path);
2514 8 : prinbee::out_event event;
2515 :
2516 : // we find no events
2517 : //
2518 8 : CATCH_REQUIRE_FALSE(j.next_event(event));
2519 8 : }
2520 8 : }
2521 : }
2522 30 : CATCH_END_SECTION()
2523 :
2524 31 : CATCH_START_SECTION("journal_errors: invalid out event statuses")
2525 : {
2526 257 : for(int idx(0); idx < 256; ++idx)
2527 : {
2528 256 : switch(static_cast<prinbee::status_t>(idx))
2529 : {
2530 6 : case prinbee::status_t::STATUS_UNKNOWN:
2531 : case prinbee::status_t::STATUS_READY:
2532 : case prinbee::status_t::STATUS_FORWARDED:
2533 : case prinbee::status_t::STATUS_ACKNOWLEDGED:
2534 : case prinbee::status_t::STATUS_COMPLETED:
2535 : case prinbee::status_t::STATUS_FAILED:
2536 6 : continue;
2537 :
2538 250 : default:
2539 250 : break;
2540 :
2541 : }
2542 :
2543 500 : prinbee::out_event event;
2544 750 : CATCH_REQUIRE_THROWS_MATCHES(
2545 : event.set_status(static_cast<prinbee::status_t>(idx))
2546 : , prinbee::invalid_parameter
2547 : , Catch::Matchers::ExceptionMessage(
2548 : "prinbee_exception: unsupported status number."));
2549 : }
2550 : }
2551 30 : CATCH_END_SECTION()
2552 :
2553 31 : CATCH_START_SECTION("journal_errors: can't reduce number of files in a filled up journal")
2554 : {
2555 3 : std::string const path(conf_path("journal_reduce_max_files"));
2556 1 : advgetopt::conf_file::reset_conf_files();
2557 1 : prinbee::journal j(path);
2558 1 : CATCH_REQUIRE(j.is_valid());
2559 1 : CATCH_REQUIRE(j.set_maximum_number_of_files(5));
2560 :
2561 1 : j.set_maximum_file_size(prinbee::JOURNAL_MINIMUM_FILE_SIZE);
2562 :
2563 : // 9 to 10 Kb of data per message so we should be able to add
2564 : // between 6 and 7 messages per file; i.e. 14 maximum then we
2565 : // are expecting an error on the add_event()
2566 : //
2567 1 : std::vector<std::uint8_t> data;
2568 1 : bool success(false);
2569 1 : int count(0);
2570 7641 : for(;; ++count)
2571 : {
2572 7642 : std::size_t const size(rand() % 1024 + 1024 * 9);
2573 7642 : data.resize(size);
2574 74357662 : for(std::size_t idx(0); idx < size; ++idx)
2575 : {
2576 74350020 : data[idx] = rand();
2577 : }
2578 7642 : prinbee::in_event event;
2579 7642 : event.set_request_id("id-" + std::to_string(count));
2580 : {
2581 7642 : prinbee::attachment a;
2582 7642 : a.set_data(data.data(), size);
2583 7642 : event.add_attachment(a);
2584 7642 : };
2585 7642 : snapdev::timespec_ex event_time(snapdev::now());
2586 7642 : bool const r(j.add_event(event, event_time));
2587 7642 : if(!r)
2588 : {
2589 1 : success = true;
2590 1 : break;
2591 : }
2592 15283 : }
2593 1 : CATCH_REQUIRE(success);
2594 :
2595 : // trying to reduce the number of files when full fails with
2596 : // an exception
2597 : //
2598 3 : CATCH_REQUIRE_THROWS_MATCHES(
2599 : j.set_maximum_number_of_files(prinbee::JOURNAL_MINIMUM_NUMBER_OF_FILES)
2600 : , prinbee::file_still_in_use
2601 : , Catch::Matchers::ExceptionMessage(
2602 : "prinbee_exception: it is not currently possible to reduce the maximum number of files when some of those over the new limit are still in use."));
2603 :
2604 : // mark all as complete and re-attempt the reduction
2605 : //
2606 7642 : for(int idx(0); idx < count; ++idx)
2607 : {
2608 7641 : std::string const request_id("id-" + std::to_string(idx));
2609 7641 : if((rand() & 1) == 0)
2610 : {
2611 3762 : CATCH_REQUIRE(j.event_completed(request_id));
2612 : }
2613 : else
2614 : {
2615 3879 : CATCH_REQUIRE(j.event_failed(request_id));
2616 : }
2617 7641 : }
2618 :
2619 1 : CATCH_REQUIRE(j.set_maximum_number_of_files(prinbee::JOURNAL_MINIMUM_NUMBER_OF_FILES));
2620 1 : }
2621 30 : CATCH_END_SECTION()
2622 :
2623 31 : CATCH_START_SECTION("journal_errors: source file missing")
2624 : {
2625 1 : prinbee::attachment a;
2626 1 : std::string const path(SNAP_CATCH2_NAMESPACE::g_tmp_dir() + "/this-does-not-exist.txt");
2627 3 : CATCH_REQUIRE_THROWS_MATCHES(
2628 : a.set_file(path)
2629 : , prinbee::file_not_found
2630 : , Catch::Matchers::ExceptionMessage(
2631 : std::string("prinbee_exception: file \"")
2632 : + path
2633 : + "\" not accessible: No such file or directory."));
2634 1 : }
2635 30 : CATCH_END_SECTION()
2636 :
2637 31 : CATCH_START_SECTION("journal_errors: file size mismatch")
2638 : {
2639 1 : prinbee::attachment a;
2640 1 : std::size_t real_size(0);
2641 1 : std::string const path(SNAP_CATCH2_NAMESPACE::g_tmp_dir() + "/some-file.txt");
2642 : {
2643 1 : std::ofstream out(path);
2644 1 : CATCH_REQUIRE(out.is_open());
2645 1 : out << "This small file.\n";
2646 1 : real_size = out.tellp();
2647 1 : }
2648 1 : CATCH_REQUIRE_THROWS_MATCHES(
2649 : a.set_file(path, 100)
2650 : , prinbee::invalid_parameter
2651 : , Catch::Matchers::ExceptionMessage(
2652 : "prinbee_exception: trying to save more data (100) than available in file attachment \""
2653 : + path
2654 : + "\" ("
2655 : + std::to_string(real_size)
2656 : + ")."));
2657 1 : }
2658 30 : CATCH_END_SECTION()
2659 :
2660 31 : CATCH_START_SECTION("journal_errors: delete attachment file then try to read the data")
2661 : {
2662 3 : std::string content("File about to be deleted.\n");
2663 1 : std::string const path(SNAP_CATCH2_NAMESPACE::g_tmp_dir() + "/set_file-unlink-file.txt");
2664 : {
2665 1 : std::ofstream out(path);
2666 1 : CATCH_REQUIRE(out.is_open());
2667 1 : out << content;
2668 1 : }
2669 1 : prinbee::attachment a;
2670 1 : a.set_file(path);
2671 1 : CATCH_REQUIRE_FALSE(a.empty());
2672 1 : CATCH_REQUIRE(a.size() == static_cast<off_t>(content.length()));
2673 1 : CATCH_REQUIRE(a.is_file());
2674 1 : CATCH_REQUIRE(a.filename() == path);
2675 :
2676 : // deleting the file before call a.data() means we get an error
2677 : //
2678 1 : CATCH_REQUIRE(unlink(path.c_str()) == 0);
2679 1 : CATCH_REQUIRE_THROWS_MATCHES(
2680 : a.data()
2681 : , prinbee::file_not_found
2682 : , Catch::Matchers::ExceptionMessage(
2683 : "prinbee_exception: file \""
2684 : + path
2685 : + "\" not found or permission denied."));
2686 1 : }
2687 30 : CATCH_END_SECTION()
2688 :
2689 31 : CATCH_START_SECTION("journal_errors: delete small attachment file before adding event to journal")
2690 : {
2691 3 : std::string const path(conf_path("journal_small_attachment"));
2692 1 : advgetopt::conf_file::reset_conf_files();
2693 1 : prinbee::journal j(path);
2694 :
2695 3 : std::string content("Another file about to be deleted.\n");
2696 1 : std::string const to_unlink(SNAP_CATCH2_NAMESPACE::g_tmp_dir() + "/set_file-add_event-unlink-file.txt");
2697 : {
2698 1 : std::ofstream out(to_unlink);
2699 1 : CATCH_REQUIRE(out.is_open());
2700 1 : out << content;
2701 1 : }
2702 1 : prinbee::attachment a;
2703 1 : a.set_file(to_unlink);
2704 1 : CATCH_REQUIRE_FALSE(a.empty());
2705 1 : CATCH_REQUIRE(a.size() == static_cast<off_t>(content.length()));
2706 1 : CATCH_REQUIRE(a.is_file());
2707 1 : CATCH_REQUIRE(a.filename() == to_unlink);
2708 :
2709 1 : prinbee::in_event event;
2710 3 : event.set_request_id("unlinked");
2711 1 : event.add_attachment(a);
2712 :
2713 : // deleting the file before calling j.add_event()
2714 : //
2715 1 : CATCH_REQUIRE(unlink(to_unlink.c_str()) == 0);
2716 :
2717 : // the add fails as a result
2718 : //
2719 1 : snapdev::timespec_ex event_time(snapdev::now());
2720 1 : CATCH_REQUIRE_FALSE(j.add_event(event, event_time));
2721 1 : }
2722 30 : CATCH_END_SECTION()
2723 :
2724 31 : CATCH_START_SECTION("journal_errors: delete large attachment file before adding event to journal")
2725 : {
2726 1 : prinbee::attachment_copy_handling_t const mode[] =
2727 : {
2728 : prinbee::attachment_copy_handling_t::ATTACHMENT_COPY_HANDLING_SOFTLINK,
2729 : prinbee::attachment_copy_handling_t::ATTACHMENT_COPY_HANDLING_HARDLINK,
2730 : prinbee::attachment_copy_handling_t::ATTACHMENT_COPY_HANDLING_REFLINK,
2731 : prinbee::attachment_copy_handling_t::ATTACHMENT_COPY_HANDLING_FULL,
2732 : };
2733 :
2734 5 : for(auto const & handling : mode)
2735 : {
2736 12 : std::string const path(conf_path("journal_large_attachment"));
2737 4 : advgetopt::conf_file::reset_conf_files();
2738 4 : prinbee::journal j(path);
2739 4 : j.set_attachment_copy_handling(handling);
2740 :
2741 : // create a large string so we go through the large file case
2742 : //
2743 4 : std::string const content(SNAP_CATCH2_NAMESPACE::random_string(
2744 : prinbee::JOURNAL_INLINE_ATTACHMENT_SIZE_DEFAULT_THRESHOLD,
2745 : prinbee::JOURNAL_INLINE_ATTACHMENT_SIZE_DEFAULT_THRESHOLD * 2,
2746 4 : SNAP_CATCH2_NAMESPACE::character_t::CHARACTER_ZUNICODE));
2747 4 : std::string const to_unlink(SNAP_CATCH2_NAMESPACE::g_tmp_dir() + "/set_file-add_event-unlink-file.txt");
2748 : {
2749 4 : std::ofstream out(to_unlink);
2750 4 : CATCH_REQUIRE(out.is_open());
2751 4 : out << content;
2752 4 : }
2753 4 : prinbee::attachment a;
2754 4 : a.set_file(to_unlink);
2755 4 : CATCH_REQUIRE_FALSE(a.empty());
2756 4 : CATCH_REQUIRE(a.size() == static_cast<off_t>(content.length()));
2757 4 : CATCH_REQUIRE(a.is_file());
2758 4 : CATCH_REQUIRE(a.filename() == to_unlink);
2759 :
2760 4 : prinbee::in_event event;
2761 12 : event.set_request_id("unlinked");
2762 4 : event.add_attachment(a);
2763 :
2764 : // deleting the file before calling j.add_event()
2765 : //
2766 4 : CATCH_REQUIRE(unlink(to_unlink.c_str()) == 0);
2767 :
2768 : // the add fails as a result
2769 : //
2770 4 : snapdev::timespec_ex event_time(snapdev::now());
2771 4 : if(handling == prinbee::attachment_copy_handling_t::ATTACHMENT_COPY_HANDLING_SOFTLINK)
2772 : {
2773 : // softlink does not require access to the original file so
2774 : // the test passes in this case (oops?)
2775 : //
2776 1 : CATCH_REQUIRE(j.add_event(event, event_time));
2777 : }
2778 : else
2779 : {
2780 3 : CATCH_REQUIRE_FALSE(j.add_event(event, event_time));
2781 : }
2782 4 : }
2783 : }
2784 30 : CATCH_END_SECTION()
2785 :
2786 31 : CATCH_START_SECTION("journal_errors: large attachment file destination is a directory")
2787 : {
2788 1 : prinbee::attachment_copy_handling_t const mode[] =
2789 : {
2790 : prinbee::attachment_copy_handling_t::ATTACHMENT_COPY_HANDLING_SOFTLINK,
2791 : prinbee::attachment_copy_handling_t::ATTACHMENT_COPY_HANDLING_HARDLINK,
2792 : prinbee::attachment_copy_handling_t::ATTACHMENT_COPY_HANDLING_REFLINK,
2793 : prinbee::attachment_copy_handling_t::ATTACHMENT_COPY_HANDLING_FULL,
2794 : };
2795 :
2796 1 : prinbee::attachment_offsets_t id(0);
2797 5 : for(auto const & handling : mode)
2798 : {
2799 12 : std::string const path(conf_path("journal_attachment_to_directory"));
2800 4 : advgetopt::conf_file::reset_conf_files();
2801 4 : prinbee::journal j(path);
2802 4 : j.set_attachment_copy_handling(handling);
2803 :
2804 : // create a large string so we go through the large file case
2805 : //
2806 4 : std::string const content(SNAP_CATCH2_NAMESPACE::random_string(
2807 : prinbee::JOURNAL_INLINE_ATTACHMENT_SIZE_DEFAULT_THRESHOLD,
2808 : prinbee::JOURNAL_INLINE_ATTACHMENT_SIZE_DEFAULT_THRESHOLD * 2,
2809 4 : SNAP_CATCH2_NAMESPACE::character_t::CHARACTER_ZUNICODE));
2810 4 : std::string const to_unlink(SNAP_CATCH2_NAMESPACE::g_tmp_dir() + "/set_file-add_event-unlink-file.txt");
2811 : {
2812 4 : std::ofstream out(to_unlink);
2813 4 : CATCH_REQUIRE(out.is_open());
2814 4 : out << content;
2815 4 : }
2816 4 : prinbee::attachment a;
2817 4 : a.set_file(to_unlink);
2818 4 : CATCH_REQUIRE_FALSE(a.empty());
2819 4 : CATCH_REQUIRE(a.size() == static_cast<off_t>(content.length()));
2820 4 : CATCH_REQUIRE(a.is_file());
2821 4 : CATCH_REQUIRE(a.filename() == to_unlink);
2822 :
2823 4 : prinbee::in_event event;
2824 12 : event.set_request_id("directory_as_destination");
2825 4 : event.add_attachment(a);
2826 :
2827 : // create a directory preventing creation of destination file
2828 : //
2829 : // note: we use the same directory so the sequence counter will
2830 : // continue to increase instead of using 1.bin each time
2831 : //
2832 4 : ++id;
2833 4 : std::string const dirname(path + "/" + std::to_string(id) + ".bin");
2834 12 : CATCH_REQUIRE(snapdev::mkdir_p(dirname.c_str()) == 0);
2835 :
2836 : // the add fails as a result
2837 : //
2838 4 : snapdev::timespec_ex event_time(snapdev::now());
2839 4 : CATCH_REQUIRE_FALSE(j.add_event(event, event_time));
2840 4 : }
2841 : }
2842 30 : CATCH_END_SECTION()
2843 :
2844 31 : CATCH_START_SECTION("journal_errors: large attachment buffer destination is a directory")
2845 : {
2846 3 : std::string const path(conf_path("journal_large_buffer_attachment_to_directory"));
2847 1 : advgetopt::conf_file::reset_conf_files();
2848 1 : prinbee::journal j(path);
2849 :
2850 : // create a large string so we go through the large file case
2851 : //
2852 1 : std::string const content(SNAP_CATCH2_NAMESPACE::random_string(
2853 : prinbee::JOURNAL_INLINE_ATTACHMENT_SIZE_DEFAULT_THRESHOLD,
2854 : prinbee::JOURNAL_INLINE_ATTACHMENT_SIZE_DEFAULT_THRESHOLD * 2,
2855 1 : SNAP_CATCH2_NAMESPACE::character_t::CHARACTER_ZUNICODE));
2856 1 : prinbee::attachment a;
2857 1 : a.set_data(const_cast<char *>(content.data()), content.size());
2858 1 : CATCH_REQUIRE_FALSE(a.empty());
2859 1 : CATCH_REQUIRE(a.size() == static_cast<off_t>(content.length()));
2860 1 : CATCH_REQUIRE_FALSE(a.is_file());
2861 1 : CATCH_REQUIRE(a.filename() == "");
2862 :
2863 1 : prinbee::in_event event;
2864 3 : event.set_request_id("directory_as_destination");
2865 1 : event.add_attachment(a);
2866 :
2867 : // create a directory preventing creation of destination file
2868 : //
2869 1 : std::string const dirname(path + "/1.bin");
2870 3 : CATCH_REQUIRE(snapdev::mkdir_p(dirname.c_str()) == 0);
2871 :
2872 : // the add fails as a result
2873 : //
2874 1 : snapdev::timespec_ex event_time(snapdev::now());
2875 1 : CATCH_REQUIRE_FALSE(j.add_event(event, event_time));
2876 1 : }
2877 30 : CATCH_END_SECTION()
2878 :
2879 31 : CATCH_START_SECTION("journal_errors: large attachment file shorten before added to journal in FULL copy mode")
2880 : {
2881 3 : std::string const path(conf_path("journal_shorten_large_attachment"));
2882 1 : advgetopt::conf_file::reset_conf_files();
2883 1 : prinbee::journal j(path);
2884 1 : j.set_attachment_copy_handling(prinbee::attachment_copy_handling_t::ATTACHMENT_COPY_HANDLING_FULL);
2885 :
2886 : // create a large string so we go through the large file case
2887 : //
2888 1 : std::string const content(SNAP_CATCH2_NAMESPACE::random_string(
2889 : prinbee::JOURNAL_INLINE_ATTACHMENT_SIZE_DEFAULT_THRESHOLD,
2890 : prinbee::JOURNAL_INLINE_ATTACHMENT_SIZE_DEFAULT_THRESHOLD * 2,
2891 1 : SNAP_CATCH2_NAMESPACE::character_t::CHARACTER_ZUNICODE));
2892 1 : std::string const to_unlink(SNAP_CATCH2_NAMESPACE::g_tmp_dir() + "/set_file-add_event-unlink-file.txt");
2893 : {
2894 1 : std::ofstream out(to_unlink);
2895 1 : CATCH_REQUIRE(out.is_open());
2896 1 : out << content;
2897 1 : }
2898 1 : prinbee::attachment a;
2899 1 : a.set_file(to_unlink);
2900 1 : CATCH_REQUIRE_FALSE(a.empty());
2901 1 : CATCH_REQUIRE(a.size() == static_cast<off_t>(content.length()));
2902 1 : CATCH_REQUIRE(a.is_file());
2903 1 : CATCH_REQUIRE(a.filename() == to_unlink);
2904 :
2905 1 : prinbee::in_event event;
2906 3 : event.set_request_id("shorten");
2907 1 : event.add_attachment(a);
2908 :
2909 : // shortening the file before calling j.add_event()
2910 : //
2911 1 : CATCH_REQUIRE(truncate(to_unlink.c_str(), content.length() / 2) == 0);
2912 :
2913 : // the add fails as a result
2914 : //
2915 1 : snapdev::timespec_ex event_time(snapdev::now());
2916 1 : CATCH_REQUIRE_FALSE(j.add_event(event, event_time));
2917 1 : }
2918 30 : CATCH_END_SECTION()
2919 :
2920 31 : CATCH_START_SECTION("journal_errors: special file cannot be used")
2921 : {
2922 1 : prinbee::attachment a;
2923 7 : CATCH_REQUIRE_THROWS_MATCHES(
2924 : a.set_file("/dev/null")
2925 : , prinbee::invalid_parameter
2926 : , Catch::Matchers::ExceptionMessage(
2927 : "prinbee_exception: file \"/dev/null\" does not represent a regular file."));
2928 1 : }
2929 30 : CATCH_END_SECTION()
2930 :
2931 31 : CATCH_START_SECTION("journal_errors: directories cannot be used")
2932 : {
2933 1 : prinbee::attachment a;
2934 7 : CATCH_REQUIRE_THROWS_MATCHES(
2935 : a.set_file("/usr/bin")
2936 : , prinbee::invalid_parameter
2937 : , Catch::Matchers::ExceptionMessage(
2938 : "prinbee_exception: file \"/usr/bin\" does not represent a regular file."));
2939 1 : }
2940 30 : CATCH_END_SECTION()
2941 :
2942 31 : CATCH_START_SECTION("journal_errors: add too many attachments (in)")
2943 : {
2944 : // create a journal
2945 : //
2946 3 : std::string const path(conf_path("journal_events"));
2947 1 : advgetopt::conf_file::reset_conf_files();
2948 1 : prinbee::journal j(path);
2949 1 : CATCH_REQUIRE(j.is_valid());
2950 1 : prinbee::in_event event;
2951 :
2952 : // add the maximum number of attachments
2953 : //
2954 256 : for(std::size_t count(0); count < prinbee::MAXIMUM_ATTACHMENT_COUNT; ++count)
2955 : {
2956 32895 : for(std::size_t id(count); id < prinbee::MAXIMUM_ATTACHMENT_COUNT; ++id)
2957 : {
2958 97920 : CATCH_REQUIRE_THROWS_MATCHES(
2959 : event.get_attachment(id)
2960 : , prinbee::out_of_range
2961 : , Catch::Matchers::ExceptionMessage(
2962 : "out_of_range: identifier out of range retrieving attachment from in_event."));
2963 : }
2964 :
2965 255 : prinbee::attachment a;
2966 255 : std::size_t const size(rand() % 25 + 1);
2967 765 : std::vector<std::uint8_t> data(size);
2968 3573 : for(std::size_t idx(0); idx < size; ++idx)
2969 : {
2970 3318 : data[idx] = static_cast<std::uint8_t>(rand());
2971 : }
2972 255 : a.set_data(data.data(), size);
2973 255 : event.add_attachment(a);
2974 255 : }
2975 :
2976 : // try to add one more attachment, that must fail
2977 : //
2978 : {
2979 1 : prinbee::attachment a;
2980 1 : std::size_t const size(rand() % 25 + 1);
2981 3 : std::vector<std::uint8_t> data(size);
2982 25 : for(std::size_t idx(0); idx < size; ++idx)
2983 : {
2984 24 : data[idx] = static_cast<std::uint8_t>(rand());
2985 : }
2986 1 : a.set_data(data.data(), size);
2987 :
2988 3 : CATCH_REQUIRE_THROWS_MATCHES(
2989 : event.add_attachment(a)
2990 : , prinbee::full
2991 : , Catch::Matchers::ExceptionMessage(
2992 : "prinbee_exception: attachment table is full, this attachment cannot be added (in_event)."));
2993 1 : }
2994 1 : }
2995 30 : CATCH_END_SECTION()
2996 :
2997 31 : CATCH_START_SECTION("journal_errors: add too many attachments (out)")
2998 : {
2999 : // create a journal
3000 : //
3001 3 : std::string const path(conf_path("journal_events"));
3002 1 : advgetopt::conf_file::reset_conf_files();
3003 1 : prinbee::journal j(path);
3004 1 : CATCH_REQUIRE(j.is_valid());
3005 1 : prinbee::out_event event;
3006 :
3007 : // add the maximum number of attachments
3008 : //
3009 256 : for(std::size_t count(0); count < prinbee::MAXIMUM_ATTACHMENT_COUNT; ++count)
3010 : {
3011 32895 : for(std::size_t id(count); id < prinbee::MAXIMUM_ATTACHMENT_COUNT; ++id)
3012 : {
3013 97920 : CATCH_REQUIRE_THROWS_MATCHES(
3014 : event.get_attachment(id)
3015 : , prinbee::out_of_range
3016 : , Catch::Matchers::ExceptionMessage(
3017 : "out_of_range: identifier out of range retrieving attachment from out_event."));
3018 : }
3019 :
3020 255 : prinbee::attachment a;
3021 255 : std::size_t const size(rand() % 25 + 1);
3022 765 : std::vector<std::uint8_t> data(size);
3023 3700 : for(std::size_t idx(0); idx < size; ++idx)
3024 : {
3025 3445 : data[idx] = static_cast<std::uint8_t>(rand());
3026 : }
3027 255 : a.set_data(data.data(), size);
3028 255 : event.add_attachment(a);
3029 255 : }
3030 :
3031 : // try to add one more attachment, that must fail
3032 : //
3033 : {
3034 1 : prinbee::attachment a;
3035 1 : std::size_t const size(rand() % 25 + 1);
3036 3 : std::vector<std::uint8_t> data(size);
3037 22 : for(std::size_t idx(0); idx < size; ++idx)
3038 : {
3039 21 : data[idx] = static_cast<std::uint8_t>(rand());
3040 : }
3041 1 : a.set_data(data.data(), size);
3042 :
3043 3 : CATCH_REQUIRE_THROWS_MATCHES(
3044 : event.add_attachment(a)
3045 : , prinbee::full
3046 : , Catch::Matchers::ExceptionMessage(
3047 : "prinbee_exception: attachment table is full, this attachment cannot be added (out_event)."));
3048 1 : }
3049 1 : }
3050 30 : CATCH_END_SECTION()
3051 29 : }
3052 :
3053 :
3054 : // vim: ts=4 sw=4 et
|