feat(syslog): openlog/syslog/closelog/vsyslog/setlogmask
This commit is contained in:
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
* vlibc — syslog.h test (todo 36).
|
||||
*
|
||||
* Coverage (what is observable without a syslog daemon or a /dev/log
|
||||
* reader):
|
||||
*
|
||||
* 1. mask semantics: setlogmask returns the PREVIOUS mask, starts from the
|
||||
* default LOG_UPTO(LOG_DEBUG) == 0xff, and a 0 mask suppresses every
|
||||
* severity (only the return value and the no-crash property are
|
||||
* observable here — see 4);
|
||||
* 2. basic call sequence openlog/syslog/closelog with %d and %m in the
|
||||
* format, plus a priority carrying an out-of-range high facility bit
|
||||
* (masked away to the openlog facility — the defined failure);
|
||||
* 3. every severity 0..7 goes through the mask-and-format path once;
|
||||
* 4. LOG_PERROR: with the option set, the composed line — including the
|
||||
* PID under LOG_PID and the strerror text of %m — is written to
|
||||
* standard error. The evidence run captures stderr (2>&1) and greps
|
||||
* for "<code>vlibc-test[pid]: ..." to confirm the wire format;
|
||||
* 5. repeated openlog/syslog/closelog cycles: each openlog(LOG_NDELAY)
|
||||
* opens the socket and each closelog closes it again (rc-level check —
|
||||
* a leaked descriptor would surface as a failing openlog only after
|
||||
* thousands of cycles, so the assertion is deliberately weak);
|
||||
* 6. -f mode: setlogmask(0) then syslog(LOG_DEBUG, ...) — suppressed, and
|
||||
* must not crash even though the message was filtered.
|
||||
*
|
||||
* No host headers: <syslog.h> is vlibc's own (-Iinclude shadows the system
|
||||
* one), errno comes from vlibc's <errno.h>, and every diagnostic goes
|
||||
* through the raw SYS_write helpers from <vlibc/internal/test.h>. Compiled
|
||||
* with -DVLIBC_LEVEL=2 so the whole (entirely L2) header is exercised.
|
||||
*
|
||||
* Not part of the library proper; compiled manually for this todo (the
|
||||
* tests/ + make check wiring is owned by a later todo).
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <syslog.h>
|
||||
|
||||
#include <vlibc/internal/test.h>
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/* The default mask the first setlogmask call must report back. */
|
||||
#define TEST_DEFAULT_MASK LOG_UPTO(LOG_DEBUG)
|
||||
|
||||
/*
|
||||
* This test must run FIRST: it observes the initial mask (0xff) that no
|
||||
* earlier setlogmask call has disturbed, then walks it down and restores it.
|
||||
*/
|
||||
static int
|
||||
test_mask_roundtrip(void)
|
||||
{
|
||||
/* Initial default: everything logged (LOG_UPTO(LOG_DEBUG) == 0xff). */
|
||||
TEST_ASSERT_EQ(setlogmask(LOG_MASK(LOG_ERR)), TEST_DEFAULT_MASK);
|
||||
|
||||
/* LOG_MASK(LOG_ERR) is 1<<3 == 8; the previous 0xff comes back. */
|
||||
TEST_ASSERT_EQ(setlogmask(LOG_MASK(LOG_INFO)), LOG_MASK(LOG_ERR));
|
||||
|
||||
/* A zero mask means "log nothing", and returns the previous mask. */
|
||||
TEST_ASSERT_EQ(setlogmask(0), LOG_MASK(LOG_INFO));
|
||||
|
||||
/* Restore the all-open default for every later test. */
|
||||
TEST_ASSERT_EQ(setlogmask(TEST_DEFAULT_MASK), 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
test_basic_sequence(void)
|
||||
{
|
||||
setlogmask(TEST_DEFAULT_MASK);
|
||||
openlog("vlibc-test", LOG_PID | LOG_NDELAY, LOG_USER);
|
||||
|
||||
/* Plain numeric conversion through the vsnprintf core. */
|
||||
syslog(LOG_INFO, "hello %d", 42);
|
||||
|
||||
/* %m expands to the strerror text of the current errno. */
|
||||
errno = ENOENT;
|
||||
syslog(LOG_ERR, "io failure: %m");
|
||||
|
||||
/* A facility bit above LOG_FACMASK is masked away (defined failure). */
|
||||
syslog(LOG_ERR | (1 << 12), "high facility bit masked");
|
||||
syslog(LOG_ERR | (1 << 20) | LOG_INFO, "very high facility bit masked");
|
||||
|
||||
closelog();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
test_every_severity(void)
|
||||
{
|
||||
int sev;
|
||||
|
||||
setlogmask(TEST_DEFAULT_MASK);
|
||||
openlog("vlibc-test", LOG_NDELAY, LOG_USER);
|
||||
for (sev = LOG_EMERG; sev <= LOG_DEBUG; sev++)
|
||||
{
|
||||
syslog(sev, "severity %d", sev);
|
||||
}
|
||||
closelog();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The wire-format path: under LOG_PERROR the composed line is written raw to
|
||||
* standard error. Nothing is asserted here (the test harness cannot capture
|
||||
* fd 2); the evidence run redirects stderr and greps for the expected text.
|
||||
*/
|
||||
static int
|
||||
test_perror_output(void)
|
||||
{
|
||||
setlogmask(TEST_DEFAULT_MASK);
|
||||
errno = ENOENT;
|
||||
openlog("vlibc-test", LOG_PERROR | LOG_PID | LOG_NDELAY, LOG_USER);
|
||||
syslog(LOG_ERR, "perror-check %d %m", 7);
|
||||
closelog();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
test_open_close_cycles(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
setlogmask(TEST_DEFAULT_MASK);
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
openlog("cycle", LOG_PID | LOG_NDELAY, LOG_USER);
|
||||
syslog(LOG_WARNING, "cycle %d", i);
|
||||
closelog();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Failure-mode complement of the mask tests: with the mask at 0 even the
|
||||
* most severe message is filtered before any formatting or output — the
|
||||
* observable contract is the return value and the absence of a crash.
|
||||
*/
|
||||
static int
|
||||
test_mask_zero_suppresses(void)
|
||||
{
|
||||
TEST_ASSERT_EQ(setlogmask(0), TEST_DEFAULT_MASK);
|
||||
openlog("vlibc-test", LOG_PERROR | LOG_NDELAY, LOG_USER);
|
||||
syslog(LOG_EMERG, "must be suppressed");
|
||||
syslog(LOG_DEBUG, "must be suppressed too");
|
||||
closelog();
|
||||
setlogmask(TEST_DEFAULT_MASK);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct vlibc_test tests[] = {
|
||||
{"mask-roundtrip", test_mask_roundtrip},
|
||||
{"basic-sequence", test_basic_sequence},
|
||||
{"every-severity", test_every_severity},
|
||||
{"perror-output", test_perror_output},
|
||||
{"open-close-cycles", test_open_close_cycles},
|
||||
{"mask-zero-suppresses", test_mask_zero_suppresses},
|
||||
};
|
||||
|
||||
/*
|
||||
* Own main (not TEST_MAIN): supports the -f failure mode. Everything else
|
||||
* follows the TEST_MAIN contract — same output shape, 0 on all-pass.
|
||||
*/
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
const size_t count = sizeof tests / sizeof tests[0];
|
||||
size_t passed = 0;
|
||||
size_t i;
|
||||
|
||||
if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'f' && argv[1][2] == '\0')
|
||||
{
|
||||
/* The failure QA: mask 0 must suppress even LOG_DEBUG without harm. */
|
||||
int before = vlibc_test_failures;
|
||||
|
||||
vlibc_test_say(1, "RUN mask-zero-debug: ");
|
||||
TEST_ASSERT_EQ(setlogmask(0), TEST_DEFAULT_MASK);
|
||||
syslog(LOG_DEBUG, "suppressed by zero mask");
|
||||
setlogmask(TEST_DEFAULT_MASK);
|
||||
if (vlibc_test_failures == before)
|
||||
{
|
||||
vlibc_test_say(1, "PASS\n");
|
||||
vlibc_test_say(1, "SUMMARY: 1/1 passed, 0 assertion failure(s)\n");
|
||||
return 0;
|
||||
}
|
||||
vlibc_test_say(1, "FAIL\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
int before = vlibc_test_failures;
|
||||
|
||||
vlibc_test_say(1, "RUN ");
|
||||
vlibc_test_say(1, tests[i].name);
|
||||
vlibc_test_say(1, ": ");
|
||||
if (tests[i].run() == 0 && vlibc_test_failures == before)
|
||||
{
|
||||
vlibc_test_say(1, "PASS\n");
|
||||
passed++;
|
||||
}
|
||||
else
|
||||
{
|
||||
vlibc_test_say(1, "FAIL\n");
|
||||
}
|
||||
}
|
||||
|
||||
vlibc_test_say(1, "SUMMARY: ");
|
||||
vlibc_test_say_dec(1, (unsigned long)passed);
|
||||
vlibc_test_say(1, "/");
|
||||
vlibc_test_say_dec(1, (unsigned long)count);
|
||||
vlibc_test_say(1, " passed, ");
|
||||
vlibc_test_say_dec(1, (unsigned long)vlibc_test_failures);
|
||||
vlibc_test_say(1, " assertion failure(s)\n");
|
||||
|
||||
return passed == count ? 0 : 1;
|
||||
}
|
||||
|
||||
#else /* !VLIBC_LEVEL_GE(2) */
|
||||
|
||||
/*
|
||||
* Level 1: <syslog.h> is entirely level 2 (XSI), so there is nothing to
|
||||
* run. Keep the TU compilable at any configured profile.
|
||||
*/
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
vlibc_test_say(1, "SKIP: <syslog.h> is level 2, not available here\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
Reference in New Issue
Block a user