/* $Id$ * * Test suite for error handling routines. * * Copyright 2004, 2005, 2006 * by Internet Systems Consortium, Inc. ("ISC") * Copyright 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, * 2003 by The Internet Software Consortium and Rich Salz * * This code is derived from software contributed to the Internet Software * Consortium by Rich Salz. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #define END (char *) 0 /* Test function type. */ typedef void (*test_function_t)(void); /* * Fork and execute the provided function, connecting stdout and stderr to a * pipe. Captures the output into the provided buffer and returns the exit * status as a waitpid status value. */ static int run_test(test_function_t function, char *buf, size_t buflen) { int fds[2]; pid_t child; ssize_t count, status; int rval; /* Flush stdout before we start to avoid odd forking issues. */ fflush(stdout); /* Set up the pipe and call the function, collecting its output. */ if (pipe(fds) == -1) sysdie("can't create pipe"); child = fork(); if (child == (pid_t) -1) { sysdie("can't fork"); } else if (child == 0) { /* In child. Set up our stdout and stderr. */ close(fds[0]); if (dup2(fds[1], 1) == -1) _exit(255); if (dup2(fds[1], 2) == -1) _exit(255); /* Now, run the function and exit successfully if it returns. */ (*function)(); fflush(stdout); _exit(0); } else { /* * In the parent; close the extra file descriptor, read the output if * any, and then collect the exit status. */ close(fds[1]); count = 0; do { status = read(fds[0], buf + count, buflen - count - 1); if (status > 0) count += status; } while (status > 0); buf[count < 0 ? 0 : count] = '\0'; if (waitpid(child, &rval, 0) == (pid_t) -1) sysdie("waitpid failed"); } return rval; } /* * Test functions. */ static void test1(void) { warn("warning"); } static void test2(void) { die("fatal"); } static void test3(void) { errno = EPERM; syswarn("permissions"); } static void test4(void) { errno = EACCES; sysdie("fatal access"); } static void test5(void) { message_program_name = "test5"; warn("warning"); } static void test6(void) { message_program_name = "test6"; die("fatal"); } static void test7(void) { message_program_name = "test7"; errno = EPERM; syswarn("perms %d", 7); } static void test8(void) { message_program_name = "test8"; errno = EACCES; sysdie("%st%s", "fa", "al"); } static int return10(void) { return 10; } static void test9(void) { message_fatal_cleanup = return10; die("fatal"); } static void test10(void) { message_program_name = 0; message_fatal_cleanup = return10; errno = EPERM; sysdie("fatal perm"); } static void test11(void) { message_program_name = "test11"; message_fatal_cleanup = return10; errno = EPERM; fputs("1st ", stdout); sysdie("fatal"); } static void log_msg(int len, const char *format, va_list args, int error) { fprintf(stderr, "%d %d ", len, error); vfprintf(stderr, format, args); fprintf(stderr, "\n"); } static void test12(void) { message_handlers_warn(1, log_msg); warn("warning"); } static void test13(void) { message_handlers_die(1, log_msg); die("fatal"); } static void test14(void) { message_handlers_warn(2, log_msg, log_msg); errno = EPERM; syswarn("warning"); } static void test15(void) { message_handlers_die(2, log_msg, log_msg); message_fatal_cleanup = return10; errno = EPERM; sysdie("fatal"); } static void test16(void) { message_handlers_warn(2, message_log_stderr, log_msg); message_program_name = "test16"; errno = EPERM; syswarn("warning"); } static void test17(void) { notice("notice"); } static void test18(void) { message_program_name = "test18"; notice("notice"); } static void test19(void) { debug("debug"); } static void test20(void) { message_handlers_notice(1, log_msg); notice("foo"); } static void test21(void) { message_handlers_debug(1, message_log_stdout); message_program_name = "test23"; debug("baz"); } static void test22(void) { message_handlers_die(0); die("hi mom!"); } static void test23(void) { message_handlers_warn(0); warn("this is a test"); } static void test24(void) { notice("first"); message_handlers_notice(0); notice("second"); message_handlers_notice(1, message_log_stdout); notice("third"); } /* * Given the test number, intended exit status and message, and the function * to run, print ok or not ok. */ static void test_error(int n, int status, const char *output, test_function_t function) { int real_status; char buf[256]; int succeeded = 1; real_status = run_test(function, buf, sizeof(buf)); if (!WIFEXITED(real_status) || status != WEXITSTATUS(real_status)) { printf(" unexpected exit status %d\n", real_status); succeeded = 0; } if (strcmp(output, buf)) { printf(" unexpected output: %s", buf); printf(" expected output: %s", output); succeeded = 0; } printf("%sok %d\n", succeeded ? "" : "not ", n); } /* * Given the test number, intended status, intended message sans the appended * strerror output, errno, and the function to run, print ok or not ok. */ static void test_strerror(int n, int status, const char *output, int error, test_function_t function) { char *full_output; full_output = concat(output, ": ", strerror(error), "\n", END); test_error(n, status, full_output, function); free(full_output); } /* * Run the tests. */ int main(void) { char buff[32]; test_init(24); test_error(1, 0, "warning\n", test1); test_error(2, 1, "fatal\n", test2); test_strerror(3, 0, "permissions", EPERM, test3); test_strerror(4, 1, "fatal access", EACCES, test4); test_error(5, 0, "test5: warning\n", test5); test_error(6, 1, "test6: fatal\n", test6); test_strerror(7, 0, "test7: perms 7", EPERM, test7); test_strerror(8, 1, "test8: fatal", EACCES, test8); test_error(9, 10, "fatal\n", test9); test_strerror(10, 10, "fatal perm", EPERM, test10); test_strerror(11, 10, "1st test11: fatal", EPERM, test11); test_error(12, 0, "7 0 warning\n", test12); test_error(13, 1, "5 0 fatal\n", test13); sprintf(buff, "%d", EPERM); test_error(14, 0, concat("7 ", buff, " warning\n7 ", buff, " warning\n", END), test14); test_error(15, 10, concat("5 ", buff, " fatal\n5 ", buff, " fatal\n", END), test15); test_error(16, 0, concat("test16: warning: ", strerror(EPERM), "\n7 ", buff, " warning\n", END), test16); test_error(17, 0, "notice\n", test17); test_error(18, 0, "test18: notice\n", test18); test_error(19, 0, "", test19); test_error(20, 0, "3 0 foo\n", test20); test_error(21, 0, "test23: baz\n", test21); /* Make sure that it's possible to turn off a message type entirely. */ test_error(22, 1, "", test22); test_error(23, 0, "", test23); test_error(24, 0, "first\nthird\n", test24); return 0; }