/* SPDX-License-Identifier: LGPL-2.1-only */ /* * The Rin Library – library "conformance" tests * * Copyright (C) 2015-2021 Gediminas Jakutis */ #define RIN_NEED_GETTID #include #include #include #include #include "test.h" #include "rin/diagnostic.h" #include "rin/compat.h" struct static_args { const char * const func_name; int line_num; const char * const file_name; enum rin_diag_outstream chan; }; 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, struct static_args args); static const int default_line_num = 911; static const char default_file_name[] = "good_time.c"; 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 struct static_args args = {__func__, default_line_num, default_file_name, rin_diag_err}; static const char *in[] = {"", "test", "test number is %zu", "%zu tests ran"}; static const char *expected[] = {"error:good_time.c:err_test:911:\n", "error:good_time.c:err_test:911:test\n", "error:good_time.c:err_test:911:test number is 3\n", "error:good_time.c:err_test:911:4 tests ran\n"}; for (i = 0; i < arrlen(in); ++i) { ret = test_default_channels(in[i], expected[i], i, args); } return ret; } static int warn_test(void) { size_t i; int ret; static const struct static_args args = {__func__, default_line_num, default_file_name, rin_diag_warn}; static const char *in[] = {"", "test", "test number is %zu", "%zu tests ran"}; static const char *expected[] = {"warning:good_time.c:warn_test:911:\n", "warning:good_time.c:warn_test:911:test\n", "warning:good_time.c:warn_test:911:test number is 3\n", "warning:good_time.c:warn_test:911:4 tests ran\n"}; for (i = 0; i < arrlen(in); ++i) { ret = test_default_channels(in[i], expected[i], i, args); } return ret; } static int fixme_test(void) { size_t i; int ret; static const struct static_args args = {__func__, default_line_num, default_file_name, rin_diag_fixme}; static const char *in[] = {"", "test", "test number is %zu", "%zu tests ran"}; static const char *expected[] = {"fixme:good_time.c:fixme_test:911:\n", "fixme:good_time.c:fixme_test:911:test\n", "fixme:good_time.c:fixme_test:911:test number is 3\n", "fixme:good_time.c:fixme_test:911:4 tests ran\n"}; for (i = 0; i < arrlen(in); ++i) { ret = test_default_channels(in[i], expected[i], i, args); } return ret; } static int info_test(void) { size_t i; int ret; static const struct static_args args = {__func__, default_line_num, default_file_name, rin_diag_info}; static const char *in[] = {"", "test", "test number is %zu", "%zu tests ran"}; static const char *expected[] = {"info:good_time.c:info_test:911:\n", "info:good_time.c:info_test:911:test\n", "info:good_time.c:info_test:911:test number is 3\n", "info:good_time.c:info_test:911:4 tests ran\n"}; for (i = 0; i < arrlen(in); ++i) { ret = test_default_channels(in[i], expected[i], i, args); } return ret; } static int format_test(void) { char expbuf[64]; size_t i; enum rin_diag_outstream j; int ret; static const char *format[] = {NULL, "", "C:F:t:mn", "CCCC"}; static const char *expected[][4] = { {"error:good_time.c:format_test:911:\n", "", "error:format_test:%08lx:message is 3\n", "errorerrorerrorerror"}, {"warning:good_time.c:format_test:911:\n", "", "warning:format_test:%08lx:message is 3\n", "warningwarningwarningwarning"}, {"fixme:good_time.c:format_test:911:\n", "", "fixme:format_test:%08lx:message is 3\n", "fixmefixmefixmefixme"}, {"info:good_time.c:format_test:911:\n", "", "info:format_test:%08lx:message is 3\n", "infoinfoinfoinfo"} }; static const char *msg[] = {"", "", "message is %zu", ""}; struct static_args args = {__func__, default_line_num, default_file_name, 0}; /* `ninja test` seems to be intercepting stdout, so we cannot test the default outstream */ for (j = rin_diag_err; j <= rin_diag_info; ++j) { args.chan = j; for (i = 0; i < arrlen(format); ++i) { rin_diag_format(j, format[i]); snprintf(expbuf, sizeof(expbuf), expected[j][i], (long int) gettid()); ret = test_default_channels(msg[i], expbuf, i, args); } } 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, struct static_args args) { char buf[64] = {0}; int ret = 0; void (*func[])(const char * const, int, const char * const, const char *format, ...) = {__rin_err, __rin_warn, __rin_fixme, __rin_info}; FILE *capstream; if (!(capstream = tmpfile())) { ret = -1; goto fail; } rin_diag_set_outstream(args.chan, capstream); func[args.chan](args.func_name, args.line_num, args.file_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; }