234 lines
7.2 KiB
C
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) */
|