/* * The Rin Library – library "conformance" tests * * Copyright (C) 2015-2017 Gediminas Jakutis * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; version 2.1 * of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #define RIN_NEED_GETTID #include #include #include #include #include "test.h" #include "rin/diagnostic.h" #include "rin/compat.h" static int err_test(void); static int warn_test(void); static int fixme_test(void); static int info_test(void); static int format_test(void); static char *visible_newlines(const char * const in); static int test_default_channels(const char * const in, const char * const expected, size_t i, const char *func_name, enum rin_diag_outstream chan); int diagnostic_test(char *testname) { static const struct test tests[] = { {"err", err_test}, {"warn", warn_test}, {"fixme", fixme_test}, {"info", info_test}, {"format", format_test}}; size_t i; for (i = 0; i < arrlen(tests); ++i) { if (!strcmp(testname, tests[i].name)) { return tests[i].testfunc(); } } return EXIT_FAILURE; } static int err_test(void) { size_t i; int ret; static const char *in[] = {"", "test", "test number is %zu", "%zu tests ran"}; static const char *expected[] = {"error:err_test:\n", "error:err_test:test\n", "error:err_test:test number is 3\n", "error:err_test:4 tests ran\n"}; for (i = 0; i < arrlen(in); ++i) { ret = test_default_channels(in[i], expected[i], i, __func__, rin_diag_err); } return ret; } static int warn_test(void) { size_t i; int ret; static const char *in[] = {"", "test", "test number is %zu", "%zu tests ran"}; static const char *expected[] = {"warning:warn_test:\n", "warning:warn_test:test\n", "warning:warn_test:test number is 3\n", "warning:warn_test:4 tests ran\n"}; for (i = 0; i < arrlen(in); ++i) { ret = test_default_channels(in[i], expected[i], i, __func__, rin_diag_warn); } return ret; } static int fixme_test(void) { size_t i; int ret; static const char *in[] = {"", "test", "test number is %zu", "%zu tests ran"}; static const char *expected[] = {"fixme:fixme_test:\n", "fixme:fixme_test:test\n", "fixme:fixme_test:test number is 3\n", "fixme:fixme_test:4 tests ran\n"}; for (i = 0; i < arrlen(in); ++i) { ret = test_default_channels(in[i], expected[i], i, __func__, rin_diag_fixme); } return ret; } static int info_test(void) { size_t i; int ret; static const char *in[] = {"", "test", "test number is %zu", "%zu tests ran"}; static const char *expected[] = {"info:info_test:\n", "info:info_test:test\n", "info:info_test:test number is 3\n", "info:info_test:4 tests ran\n"}; for (i = 0; i < arrlen(in); ++i) { ret = test_default_channels(in[i], expected[i], i, __func__, rin_diag_info); } return ret; } static int format_test(void) { char expbuf[64]; size_t i; int ret; static const char *format[] = {NULL, "", "C:F:t:mn", "CCCC"}; static const char *expected_err[] = {"error:format_test:\n", "", "error:format_test:%08lx:message is 3\n", "errorerrorerrorerror"}; static const char *expected_warn[] = {"warning:format_test:\n", "", "warning:format_test:%08lx:message is 3\n", "warningwarningwarningwarning"}; static const char *expected_fixme[] = {"fixme:format_test:\n", "", "fixme:format_test:%08lx:message is 3\n", "fixmefixmefixmefixme"}; static const char *expected_info[] = {"info:format_test:\n", "", "info:format_test:%08lx:message is 3\n", "infoinfoinfoinfo"}; static const char *msg[] = {"", "", "message is %zu"}; /* `ninja test` seems to be intercepting stdout, so we cannot test the default outstream */ for (i = 0; i < arrlen(format); ++i) { rin_diag_format(rin_diag_err, format[i]); rin_diag_format(rin_diag_warn, format[i]); rin_diag_format(rin_diag_fixme, format[i]); rin_diag_format(rin_diag_info, format[i]); snprintf(expbuf, sizeof(expbuf), expected_err[i], (long int) gettid()); ret = test_default_channels(msg[i], expbuf, i, __func__, rin_diag_err); snprintf(expbuf, sizeof(expbuf), expected_warn[i], (long int) gettid()); ret = test_default_channels(msg[i], expbuf, i, __func__, rin_diag_warn); snprintf(expbuf, sizeof(expbuf), expected_fixme[i], (long int) gettid()); ret = test_default_channels(msg[i], expbuf, i, __func__, rin_diag_fixme); snprintf(expbuf, sizeof(expbuf), expected_info[i], (long int) gettid()); ret = test_default_channels(msg[i], expbuf, i, __func__, rin_diag_info); } return ret; } static char *visible_newlines(const char * const in) { char *ret; const char *newline; const char *readptr; char *writeptr; readptr = in; ret = malloc(strlen(readptr) * 2 + 1); writeptr = ret; newline = strchr(readptr, '\n'); while (newline) { /* write chunk until the next newline */ memcpy(writeptr, readptr, newline - readptr); writeptr += newline - readptr; /* advance writing position */ readptr = newline; ++readptr; /* skip the newline */ /* toss in replacement */ memcpy(writeptr, "\\n", 2); writeptr += 2; newline = strchr(readptr, '\n'); } /* write all that remains */ strcpy(writeptr, readptr); return ret; } static int test_default_channels(const char * const in, const char * const expected, size_t i, const char *func_name, enum rin_diag_outstream chan) { char buf[64] = {0}; int ret = 0; void (*func[])(const char *, const char *, ...) = {__rin_err, __rin_warn, __rin_fixme, __rin_info}; FILE *capstream; if (!(capstream = tmpfile())) { ret = -1; goto fail; } rin_diag_set_outstream(chan, capstream); func[chan](func_name, in, i + 1); rewind(capstream); if (!(fgets(buf, sizeof(buf), capstream)) && strlen(expected)) { ret = -1; goto fail; } fail: if (capstream) { fclose(capstream); } if (ret == -1) { ret = ok(1, "%s: unexpected test program failure on iteration %zu", __func__, i); } else { char *tmp[2]; ret = strncmp(buf, expected, sizeof(buf)); tmp[0] = visible_newlines(buf); tmp[1] = visible_newlines(expected); ret = ok(ret, "%s: expected: \"%s\", got: \"%s\"", __func__, tmp[1], tmp[0]); free(tmp[0]); free(tmp[1]); } return ret; }