Line data Source code
1 : // Copyright (c) 2006-2025 Made to Order Software Corp. All Rights Reserved
2 : //
3 : // https://snapwebsites.org/project/snaplogger
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 2 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 along
17 : // with this program; if not, write to the Free Software Foundation, Inc.,
18 : // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 :
20 : // self
21 : //
22 : #include "catch_main.h"
23 :
24 :
25 : // snaplogger
26 : //
27 : #include <snaplogger/buffer_appender.h>
28 : #include <snaplogger/exception.h>
29 : #include <snaplogger/format.h>
30 : #include <snaplogger/logger.h>
31 : #include <snaplogger/map_diagnostic.h>
32 : #include <snaplogger/message.h>
33 : #include <snaplogger/severity.h>
34 : #include <snaplogger/version.h>
35 :
36 :
37 : // advgetopt
38 : //
39 : #include <advgetopt/conf_file.h>
40 : #include <advgetopt/validator_integer.h>
41 :
42 :
43 : // snapdev
44 : //
45 : #include <snapdev/enum_class_math.h>
46 :
47 :
48 : // C
49 : //
50 : #include <unistd.h>
51 : #include <netdb.h>
52 : #include <sys/param.h>
53 :
54 :
55 :
56 :
57 4 : CATCH_TEST_CASE("severity", "[severity]")
58 : {
59 4 : CATCH_START_SECTION("severity: Create Severity")
60 : {
61 1 : snaplogger::severity_t const err_plus_one(static_cast<snaplogger::severity_t>(static_cast<int>(snaplogger::severity_t::SEVERITY_ERROR) + 1));
62 :
63 : // user severity by name
64 : {
65 1 : snaplogger::severity::pointer_t s(std::make_shared<snaplogger::severity>(err_plus_one, "error"));
66 :
67 5 : CATCH_REQUIRE_THROWS_MATCHES(
68 : snaplogger::add_severity(s)
69 : , snaplogger::duplicate_error
70 : , Catch::Matchers::ExceptionMessage(
71 : "logger_error: a system severity (error) cannot be replaced (same name)."));
72 1 : }
73 :
74 : // system severity by name
75 : {
76 1 : snaplogger::severity::pointer_t s(std::make_shared<snaplogger::severity>(err_plus_one, "error", true));
77 :
78 5 : CATCH_REQUIRE_THROWS_MATCHES(
79 : snaplogger::add_severity(s)
80 : , snaplogger::duplicate_error
81 : , Catch::Matchers::ExceptionMessage(
82 : "logger_error: a system severity (error) cannot be replaced (same name)."));
83 1 : }
84 :
85 : // user severity by severity
86 : {
87 1 : snaplogger::severity::pointer_t s(std::make_shared<snaplogger::severity>(snaplogger::severity_t::SEVERITY_ERROR, "bad-error"));
88 :
89 3 : CATCH_REQUIRE_THROWS_MATCHES(
90 : snaplogger::add_severity(s)
91 : , snaplogger::duplicate_error
92 : , Catch::Matchers::ExceptionMessage(
93 : "logger_error: a system severity ("
94 : + std::to_string(static_cast<int>(snaplogger::severity_t::SEVERITY_ERROR))
95 : + ") cannot be replaced (same severity level: "
96 : + std::to_string(static_cast<int>(snaplogger::severity_t::SEVERITY_ERROR))
97 : + ")."));
98 1 : }
99 :
100 : // user severity by severity
101 : {
102 1 : snaplogger::severity::pointer_t s(std::make_shared<snaplogger::severity>(snaplogger::severity_t::SEVERITY_ERROR, "bad-error", true));
103 :
104 3 : CATCH_REQUIRE_THROWS_MATCHES(
105 : snaplogger::add_severity(s)
106 : , snaplogger::duplicate_error
107 : , Catch::Matchers::ExceptionMessage(
108 : "logger_error: a system severity ("
109 : + std::to_string(static_cast<int>(snaplogger::severity_t::SEVERITY_ERROR))
110 : + ") cannot be replaced (same severity level: "
111 : + std::to_string(static_cast<int>(snaplogger::severity_t::SEVERITY_ERROR))
112 : + ")."));
113 1 : }
114 :
115 : // actually create a valid severity
116 : {
117 3 : CATCH_REQUIRE(snaplogger::get_severity("bad-error") == nullptr);
118 3 : CATCH_REQUIRE(snaplogger::get_severity("big-error") == nullptr);
119 :
120 1 : snaplogger::severity_t const level(static_cast<snaplogger::severity_t>(205));
121 1 : snaplogger::severity::pointer_t s(std::make_shared<snaplogger::severity>(level, "bad-error"));
122 :
123 1 : CATCH_REQUIRE(s->get_severity() == level);
124 1 : CATCH_REQUIRE(s->get_name() == "bad-error");
125 :
126 1 : CATCH_REQUIRE(s->get_all_names().size() == 1);
127 1 : CATCH_REQUIRE(s->get_all_names()[0] == "bad-error");
128 :
129 3 : CATCH_REQUIRE(snaplogger::get_severity("bad-error") == nullptr);
130 3 : CATCH_REQUIRE(snaplogger::get_severity("big-error") == nullptr);
131 :
132 : // duplicates are not allowed
133 : //
134 7 : CATCH_REQUIRE_THROWS_MATCHES(
135 : s->add_alias("bad-error")
136 : , snaplogger::duplicate_error
137 : , Catch::Matchers::ExceptionMessage(
138 : "logger_error: severity \""
139 : "bad-error"
140 : "\" already has an alias \""
141 : "bad-error"
142 : "\"."));
143 :
144 1 : snaplogger::add_severity(s);
145 :
146 3 : CATCH_REQUIRE(snaplogger::get_severity("bad-error") == s);
147 3 : CATCH_REQUIRE(snaplogger::get_severity("big-error") == nullptr);
148 :
149 3 : s->add_alias("big-error");
150 :
151 : // duplicates are not allowed
152 : //
153 7 : CATCH_REQUIRE_THROWS_MATCHES(
154 : s->add_alias("big-error")
155 : , snaplogger::duplicate_error
156 : , Catch::Matchers::ExceptionMessage(
157 : "logger_error: severity \""
158 : "bad-error"
159 : "\" already has an alias \""
160 : "big-error"
161 : "\"."));
162 :
163 1 : CATCH_REQUIRE(s->get_all_names().size() == 2);
164 1 : CATCH_REQUIRE(s->get_all_names()[0] == "bad-error");
165 1 : CATCH_REQUIRE(s->get_all_names()[1] == "big-error");
166 :
167 1 : CATCH_REQUIRE(s->get_description() == "bad-error");
168 :
169 3 : s->set_description("bad error");
170 1 : CATCH_REQUIRE(s->get_description() == "bad error");
171 :
172 1 : s->set_description(std::string());
173 1 : CATCH_REQUIRE(s->get_description() == "bad-error");
174 :
175 3 : CATCH_REQUIRE(snaplogger::get_severity("bad-error") == s);
176 3 : CATCH_REQUIRE(snaplogger::get_severity("big-error") == s);
177 1 : CATCH_REQUIRE(snaplogger::get_severity(level) == s);
178 :
179 3 : s->set_styles("orange");
180 1 : CATCH_REQUIRE(s->get_styles() == "orange");
181 :
182 1 : snaplogger::severity_t const level_plus_one(static_cast<snaplogger::severity_t>(static_cast<int>(level) + 1));
183 1 : CATCH_REQUIRE(snaplogger::get_severity(level_plus_one) == nullptr);
184 :
185 1 : snaplogger::message msg(::snaplogger::severity_t::SEVERITY_ERROR);
186 3 : CATCH_REQUIRE(snaplogger::get_severity(msg, "bad-error") == s);
187 3 : CATCH_REQUIRE(snaplogger::get_severity(msg, "big-error") == s);
188 1 : }
189 :
190 : // verify that the "<name>"_sev syntax works as expected
191 : {
192 1 : snaplogger::severity_t const level(static_cast<snaplogger::severity_t>(25));
193 1 : snaplogger::severity::pointer_t s(std::make_shared<snaplogger::severity>(level, "remark"));
194 :
195 1 : snaplogger::add_severity(s);
196 :
197 1 : CATCH_REQUIRE(s->get_severity() == level);
198 1 : CATCH_REQUIRE(s->get_name() == "remark");
199 :
200 : #if SNAPDEV_CHECK_GCC_VERSION(7, 5, 0)
201 1 : snaplogger::severity::pointer_t r("remark"_sev);
202 1 : CATCH_REQUIRE(r == s);
203 :
204 1 : CATCH_REQUIRE(r->get_severity() == level);
205 1 : CATCH_REQUIRE(r->get_name() == "remark");
206 : #endif
207 1 : }
208 : }
209 4 : CATCH_END_SECTION()
210 :
211 4 : CATCH_START_SECTION("severity: Print Severity")
212 : {
213 : struct level_and_name_t
214 : {
215 : ::snaplogger::severity_t f_level = ::snaplogger::severity_t::SEVERITY_ERROR;
216 : std::string f_name = std::string();
217 : };
218 :
219 1 : std::vector<level_and_name_t> level_and_name =
220 : {
221 : { ::snaplogger::severity_t::SEVERITY_ALL, "all" },
222 : { ::snaplogger::severity_t::SEVERITY_TRACE, "trace" },
223 : { ::snaplogger::severity_t::SEVERITY_NOISY, "noisy" },
224 : { ::snaplogger::severity_t::SEVERITY_DEBUG, "debug" },
225 : { ::snaplogger::severity_t::SEVERITY_NOTICE, "notice" },
226 : { ::snaplogger::severity_t::SEVERITY_UNIMPORTANT, "unimportant" },
227 : { ::snaplogger::severity_t::SEVERITY_VERBOSE, "verbose" },
228 : { ::snaplogger::severity_t::SEVERITY_CONFIGURATION, "configuration" },
229 : { ::snaplogger::severity_t::SEVERITY_CONFIGURATION_WARNING, "configuration-warning" },
230 : { ::snaplogger::severity_t::SEVERITY_INFORMATION, "information" },
231 : { ::snaplogger::severity_t::SEVERITY_IMPORTANT, "important" },
232 : { ::snaplogger::severity_t::SEVERITY_MINOR, "minor" },
233 : { ::snaplogger::severity_t::SEVERITY_DEPRECATED, "deprecated" },
234 : { ::snaplogger::severity_t::SEVERITY_WARNING, "warning" },
235 : { ::snaplogger::severity_t::SEVERITY_MAJOR, "major" },
236 : { ::snaplogger::severity_t::SEVERITY_RECOVERABLE_ERROR, "recoverable-error" },
237 : { ::snaplogger::severity_t::SEVERITY_ERROR, "error" },
238 : { ::snaplogger::severity_t::SEVERITY_NOISY_ERROR, "noisy-error" },
239 : { ::snaplogger::severity_t::SEVERITY_SEVERE, "severe" },
240 : { ::snaplogger::severity_t::SEVERITY_EXCEPTION, "exception" },
241 : { ::snaplogger::severity_t::SEVERITY_CRITICAL, "critical" },
242 : { ::snaplogger::severity_t::SEVERITY_ALERT, "alert" },
243 : { ::snaplogger::severity_t::SEVERITY_EMERGENCY, "emergency" },
244 : { ::snaplogger::severity_t::SEVERITY_FATAL, "fatal" },
245 : { ::snaplogger::severity_t::SEVERITY_OFF, "off" },
246 29 : };
247 :
248 26 : for(auto const & ln : level_and_name)
249 : {
250 25 : std::stringstream buffer;
251 25 : buffer << ln.f_level;
252 25 : CATCH_REQUIRE(buffer.str() == ln.f_name);
253 25 : }
254 :
255 : {
256 1 : std::stringstream buffer;
257 1 : buffer << static_cast<::snaplogger::severity_t>(254);
258 1 : CATCH_REQUIRE(buffer.str() == "(unknown severity: 254)");
259 1 : }
260 1 : }
261 4 : CATCH_END_SECTION()
262 :
263 4 : CATCH_START_SECTION("severity: Severity by Level or Name")
264 : {
265 1 : snaplogger::severity_by_severity_t severities(snaplogger::get_severities_by_severity());
266 1 : snaplogger::severity_by_name_t names(snaplogger::get_severities_by_name());
267 :
268 : // this is not true, there are more names than severity levels
269 : //
270 : //CATCH_REQUIRE(severities.size() == names.size());
271 :
272 : // this does not hold true, that is, the map is properly sorted, but for
273 : // entries with an alias, the s->get_name() returns the base name, not the
274 : // alias, and thus, the order may be skewed for all aliases
275 : //
276 : // std::string previous_name;
277 : // for(auto const & s : names)
278 : // {
279 : // CATCH_REQUIRE(s.second->get_name() > previous_name);
280 : // previous_name = s.second->get_name();
281 : // }
282 :
283 1 : snaplogger::severity_t previous_severity(snaplogger::severity_t::SEVERITY_ALL - 1);
284 29 : for(auto const & s : severities)
285 : {
286 28 : CATCH_REQUIRE(s.second->get_severity() > previous_severity);
287 28 : previous_severity = s.second->get_severity();
288 : }
289 1 : }
290 4 : CATCH_END_SECTION()
291 :
292 4 : CATCH_START_SECTION("severity: severity.ini file matches")
293 : {
294 : // whenever I make an edit to the list of severity levels, I have to
295 : // keep the severity.ini up to date or the system won't be able to
296 : // load the file properly; this test verifies the one found in the
297 : // source tree against the severity levels defined in the header
298 : //
299 1 : std::string severity_ini_filename(SNAP_CATCH2_NAMESPACE::g_source_dir());
300 1 : severity_ini_filename += "/conf/severity.ini";
301 1 : advgetopt::conf_file_setup setup(severity_ini_filename);
302 1 : CATCH_REQUIRE(setup.is_valid());
303 :
304 1 : advgetopt::conf_file::pointer_t severity_ini(advgetopt::conf_file::get_conf_file(setup));
305 1 : CATCH_REQUIRE(severity_ini != nullptr);
306 :
307 1 : std::set<std::string> found;
308 :
309 1 : advgetopt::conf_file::sections_t sections(severity_ini->get_sections());
310 27 : for(auto const & s : sections)
311 : {
312 26 : snaplogger::severity::pointer_t severity(snaplogger::get_severity(s));
313 :
314 : // the default severity.ini file only defines system severities
315 : //
316 26 : CATCH_REQUIRE(severity->is_system());
317 :
318 : // make sure the entry includes a severity=... value
319 : //
320 26 : std::string const level_name(s + "::severity");
321 26 : CATCH_REQUIRE(severity_ini->has_parameter(level_name));
322 :
323 26 : std::string const level(severity_ini->get_parameter(level_name));
324 :
325 : // the level must be a valid integer
326 : //
327 26 : std::int64_t value(0);
328 26 : CATCH_REQUIRE(advgetopt::validator_integer::convert_string(level, value));
329 :
330 : // the .ini level must match the internal (library) level
331 : //
332 26 : CATCH_REQUIRE(severity->get_severity() == static_cast<snaplogger::severity_t>(value));
333 :
334 26 : found.insert(s);
335 :
336 : // severities may have aliases which needs to be added to `found`
337 : //
338 26 : std::string const aliases_name(s + "::aliases");
339 26 : if(severity_ini->has_parameter(aliases_name))
340 : {
341 17 : std::string const aliases(severity_ini->get_parameter(aliases_name));
342 17 : advgetopt::string_list_t names;
343 51 : advgetopt::split_string(aliases, names, {","});
344 35 : for(auto const & n : names)
345 : {
346 18 : found.insert(n);
347 : }
348 17 : }
349 :
350 : // verify mismatches in other parameters if defined
351 : //
352 26 : std::string const description_name(s + "::description");
353 26 : if(severity_ini->has_parameter(description_name))
354 : {
355 25 : std::string const description(severity_ini->get_parameter(description_name));
356 25 : CATCH_REQUIRE(severity->get_description() == description);
357 25 : }
358 :
359 26 : std::string const styles_name(s + "::styles");
360 26 : if(severity_ini->has_parameter(styles_name))
361 : {
362 17 : std::string const styles(severity_ini->get_parameter(styles_name));
363 17 : CATCH_REQUIRE(severity->get_styles() == styles);
364 17 : }
365 :
366 26 : std::string const default_name(s + "::default");
367 26 : if(severity_ini->has_parameter(default_name))
368 : {
369 1 : std::string const default_value(severity_ini->get_parameter(default_name));
370 1 : bool const valid_default(advgetopt::is_true(default_value) || advgetopt::is_false(default_value));
371 1 : CATCH_REQUIRE(valid_default);
372 1 : }
373 26 : }
374 :
375 : // make sure all the parameters are known
376 : // (for our file, that's best in case we misspell something)
377 : //
378 1 : std::string default_severity;
379 87 : for(auto const & p : severity_ini->get_parameters())
380 : {
381 86 : advgetopt::string_list_t names;
382 258 : advgetopt::split_string(p.first, names, {"::"});
383 :
384 : // first of all, we do not support global definition in the
385 : // severity.ini file so we should only have names within a
386 : // section and no sub-section thus two names exactly
387 : //
388 86 : CATCH_REQUIRE(names.size() == 2);
389 :
390 86 : if(names[1] == "default")
391 : {
392 1 : if(advgetopt::is_true(p.second))
393 : {
394 : // there can be only one default
395 : //
396 1 : if(!default_severity.empty())
397 : {
398 : std::cerr
399 : << "--- found more than one default: \""
400 : << default_severity
401 : << "\" and \""
402 0 : << names[0]
403 0 : << "\"\n";
404 : }
405 1 : CATCH_REQUIRE(default_severity.empty());
406 :
407 1 : default_severity = names[0];
408 : }
409 : }
410 85 : else if(names[1] != "aliases"
411 68 : && names[1] != "description"
412 43 : && names[1] != "severity"
413 153 : && names[1] != "styles")
414 : {
415 0 : std::cerr << "--- found unknown parameter \"" << names[1] << "\"\n";
416 0 : CATCH_REQUIRE(!"parameter not known");
417 : }
418 87 : }
419 :
420 : // further, make sure that all system severities defined in the
421 : // library are found in the .ini file
422 : //
423 : // with aliases, this is not 100% true, maybe we should check
424 : // with the by_severity() list instead...
425 : //
426 47 : for(auto const & s : snaplogger::get_severities_by_name())
427 : {
428 46 : if(s.second->is_system())
429 : {
430 43 : bool const contained(found.contains(s.first));
431 43 : if(!contained)
432 : {
433 : std::cout
434 : << "--- system severity \""
435 0 : << s.first
436 : << "\" is not defined in \""
437 : << severity_ini_filename
438 0 : << "\" file.\n";
439 : }
440 43 : CATCH_REQUIRE(contained);
441 : }
442 1 : }
443 1 : }
444 4 : CATCH_END_SECTION()
445 7 : }
446 :
447 :
448 2 : CATCH_TEST_CASE("severity_error", "[severity][error]")
449 : {
450 2 : CATCH_START_SECTION("severity: too small")
451 : {
452 6 : CATCH_REQUIRE_THROWS_MATCHES(
453 : snaplogger::severity(snaplogger::severity_t::SEVERITY_MIN - 1, "TOO_SMALL")
454 : , snaplogger::invalid_severity
455 : , Catch::Matchers::ExceptionMessage(
456 : "logger_error: the severity level cannot be "
457 : + std::to_string(static_cast<int>(snaplogger::severity_t::SEVERITY_MIN - 1))
458 : + ". The possible range is ["
459 : + std::to_string(static_cast<int>(snaplogger::severity_t::SEVERITY_MIN))
460 : + ".."
461 : + std::to_string(static_cast<int>(snaplogger::severity_t::SEVERITY_MAX))
462 : + "]."));
463 : }
464 2 : CATCH_END_SECTION()
465 :
466 2 : CATCH_START_SECTION("severity: too large")
467 : {
468 6 : CATCH_REQUIRE_THROWS_MATCHES(
469 : snaplogger::severity(snaplogger::severity_t::SEVERITY_MAX + 1, "TOO_LARGE")
470 : , snaplogger::invalid_severity
471 : , Catch::Matchers::ExceptionMessage(
472 : "logger_error: the severity level cannot be "
473 : + std::to_string(static_cast<int>(snaplogger::severity_t::SEVERITY_MAX + 1))
474 : + ". The possible range is ["
475 : + std::to_string(static_cast<int>(snaplogger::severity_t::SEVERITY_MIN))
476 : + ".."
477 : + std::to_string(static_cast<int>(snaplogger::severity_t::SEVERITY_MAX))
478 : + "]."));
479 : }
480 2 : CATCH_END_SECTION()
481 2 : }
482 :
483 :
484 :
485 : // vim: ts=4 sw=4 et
|