Files
vlibc/tests/test_syslog.c
T

234 lines
7.2 KiB
C

/*
* 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) */