diff options
| author | Mosder <git@mosder.eu> | 2026-09-17 15:51:23 +0200 |
|---|---|---|
| committer | Mosder <git@mosder.eu> | 2026-09-17 15:51:23 +0200 |
| commit | 5a7dcb86485f050aeeb77eef3313a6b92a2adde4 (patch) | |
| tree | 8da7de37d80b36f054bfc6d5dba284dda609c435 | |
| parent | de163fe7ee64e63d36dd581db16c776831cbead0 (diff) | |
feat: changed output tests and suppression to use file descriptors for more portability
| -rw-r--r-- | include/moslib/safe.h | 2 | ||||
| -rw-r--r-- | include/moslib/tester.h | 30 | ||||
| -rw-r--r-- | src/safe.c | 8 | ||||
| -rw-r--r-- | src/tester.c | 71 | ||||
| -rw-r--r-- | tests/safe_tests.c | 12 | ||||
| -rw-r--r-- | tests/string_tests.c | 2 |
6 files changed, 68 insertions, 57 deletions
diff --git a/include/moslib/safe.h b/include/moslib/safe.h index c855cea..7e9d817 100644 --- a/include/moslib/safe.h +++ b/include/moslib/safe.h @@ -42,6 +42,7 @@ typedef struct { #define mos_safe_dup2(fd, fd2) mos_safe_dup2_fn(fd, fd2, mos_err_info) #define mos_safe_fork() mos_safe_fork_fn(mos_err_info) #define mos_safe_pipe(fd) mos_safe_pipe_fn(fd, mos_err_info) +#define mos_safe_mkstemp(path) mos_safe_mkstemp_fn(path, mos_err_info) // Function prototypes for macros extern void *mos_safe_malloc_fn(size_t size, MosSafeErrInfo info); @@ -53,5 +54,6 @@ extern int mos_safe_dup_fn(int fd, MosSafeErrInfo info); extern int mos_safe_dup2_fn(int fd, int fd2, MosSafeErrInfo info); extern pid_t mos_safe_fork_fn(MosSafeErrInfo info); extern int mos_safe_pipe_fn(int fd[2], MosSafeErrInfo info); +extern int mos_safe_mkstemp_fn(char *path, MosSafeErrInfo info); #endif // MOSLIB_SAFE_H diff --git a/include/moslib/tester.h b/include/moslib/tester.h index 4c77d73..f62645f 100644 --- a/include/moslib/tester.h +++ b/include/moslib/tester.h @@ -47,21 +47,21 @@ // // You can also test if a function writes correct output. // You need to create a helper test and pass it into test_assert_out in main test. -// Pass the helper test, output stream, and expected output to it. +// Pass the helper test, output stream's fd, and expected output to it. // Example: // TEST_HELPER(helper_test) { // printf("output"); // } // TEST(main_test) { -// test_assert_out(helper_test, stdout, "output"); +// test_assert_out(helper_test, STDOUT_FILENO, "output"); // } // // If your functions have output and you want to suppress them for the sake of tests, // you can do so using suppress_output and unsupress_outputs. // Example: // TEST(error_test) { -// suppress_output(stdout); -// suppress_output(stderr); +// suppress_output(STDOUT_FILENO); +// suppress_output(STDERR_FILENO); // int code = fun_that_fails_with_output(); // unsuppress_outputs(); // test_assert(code == 0, "function didn't fail"); @@ -168,20 +168,20 @@ extern void mos_test_assert(int expression, const char *fail_message); // function // function that is supposed to write output // -// stream -// stream to write the output to +// fd +// file descriptor of stream to write the output to // // output // expected output of the function -#define mos_test_assert_out(function, stream, output) \ - mos_test_assert_out_fn(function, &(stream), output, #function, #stream) +#define mos_test_assert_out(function, fd, output) \ + mos_test_assert_out_fn(function, fd, output, #function, #fd) // Suppress given output // // Arguments: -// out -// outpout to suppress -#define mos_suppress_output(out) mos_suppress_output_fn(&(out)) +// fd +// file descriptor of output to suppress +#define mos_suppress_output(fd) mos_suppress_output_fn(fd) // Unuppress previously suppressed outputs // Will unsupress all of the outputs that were suppressed @@ -218,13 +218,9 @@ extern void mos_free_tester(MosTester *tester); extern MosTestGroup *mos_add_test_group_fn(MosTester *tester, const char *group_name); extern void mos_test_assert_exit_fn(MosTestFn function, int code, const char *name); extern void mos_test_assert_out_fn( - MosTestFn function, - FILE **stream, - const char *output, - const char *fn_name, - const char *stream_name + MosTestFn function, int fd, const char *output, const char *fn_name, const char *fd_name ); -extern void mos_suppress_output_fn(FILE **out); +extern void mos_suppress_output_fn(int fd); extern void mos_add_test_fn(MosTestGroup *group, MosTestFn test, const char *name); #endif // MOSLIB_TESTER_H @@ -88,3 +88,11 @@ int mos_safe_pipe_fn(int fd[2], MosSafeErrInfo info) { ERR_MESS("failed to pipe"); exit(EXIT_FAILURE); } + +int mos_safe_mkstemp_fn(char *path, MosSafeErrInfo info) { + int fd = mkstemp(path); + if (fd != -1) + return fd; + ERR_MESS("failed to make temporary file"); + exit(EXIT_FAILURE); +} diff --git a/src/tester.c b/src/tester.c index e789871..2ab0c87 100644 --- a/src/tester.c +++ b/src/tester.c @@ -19,11 +19,11 @@ #define RENDER_WIDTH 80 static int failed = 0; -static FILE *dev_null = NULL; +static int dev_null = -1; typedef struct { - FILE ***outs; - FILE **ogs; + int *outs; + int *ogs; size_t n_outs; size_t out_cap; } Outputs; @@ -140,62 +140,67 @@ void mos_test_assert_exit_fn(MosTestFn function, int code, const char *name) { } void mos_test_assert_out_fn( - MosTestFn function, - FILE **stream, - const char *output, - const char *fn_name, - const char *stream_name + MosTestFn function, int fd, const char *output, const char *fn_name, const char *fd_name ) { size_t n = strlen(output); char *out = mos_safe_calloc(n + 2, 1); - FILE *og = *stream; - fflush(*stream); - *stream = mos_safe_tmpfile(); + int tmp = mos_safe_mkstemp(".tmp"); + unlink(".tmp"); + + int og = mos_safe_dup(fd); + fflush(NULL); + mos_safe_dup2(tmp, fd); function(); - fflush(*stream); - rewind(*stream); - fread(out, 1, n + 1, *stream); - fclose(*stream); - *stream = og; + fflush(NULL); + lseek(fd, 0, SEEK_SET); + read(fd, out, n + 1); + close(tmp); + + mos_safe_dup2(og, fd); + close(og); if (strcmp(out, output)) { failed = 1; print_color(RED, "ASSERT FAILED: "); - printf("function %s outputed to %s:\n", fn_name, stream_name); + printf("function %s outputed to %s:\n", fn_name, fd_name); printf(" %s (capped at %zu chars)\n", out, n + 1); printf(" expected: %s\n", output); } free(out); } -void mos_suppress_output_fn(FILE **out) { +void mos_suppress_output_fn(int fd) { if (!suppressed.outs || suppressed.n_outs >= suppressed.out_cap) { while (suppressed.n_outs >= suppressed.out_cap) suppressed.out_cap *= 2; - suppressed.outs = mos_safe_realloc(suppressed.outs, suppressed.out_cap * sizeof(FILE **)); - suppressed.ogs = mos_safe_realloc(suppressed.ogs, suppressed.out_cap * sizeof(FILE *)); + suppressed.outs = mos_safe_realloc(suppressed.outs, suppressed.out_cap * sizeof(int)); + suppressed.ogs = mos_safe_realloc(suppressed.ogs, suppressed.out_cap * sizeof(int)); } - suppressed.outs[suppressed.n_outs] = out; - suppressed.ogs[suppressed.n_outs++] = *out; + suppressed.outs[suppressed.n_outs] = fd; + suppressed.ogs[suppressed.n_outs++] = mos_safe_dup(fd); - if (!dev_null) - dev_null = mos_safe_tmpfile(); + if (dev_null == -1) { + dev_null = mos_safe_mkstemp(".dev_null"); + unlink(".dev_null"); + } - fflush(*out); - *out = dev_null; + fflush(NULL); + mos_safe_dup2(dev_null, fd); } void mos_unsuppress_outputs(void) { if (!suppressed.outs || !suppressed.ogs) return; - if (dev_null) - fflush(dev_null); + if (dev_null != -1) + fflush(NULL); - for (size_t i = suppressed.n_outs; i-- > 0;) - *suppressed.outs[i] = suppressed.ogs[i]; + for (size_t i = suppressed.n_outs; i-- > 0;) { + mos_safe_dup2(suppressed.ogs[i], suppressed.outs[i]); + close(suppressed.ogs[i]); + } suppressed.n_outs = 0; } @@ -275,9 +280,9 @@ void mos_free_tester(MosTester *tester) { free(tester); } mos_unsuppress_outputs(); - if (dev_null) { - fclose(dev_null); - dev_null = NULL; + if (dev_null != -1) { + close(dev_null); + dev_null = -1; } if (suppressed.outs) { free(suppressed.outs); diff --git a/tests/safe_tests.c b/tests/safe_tests.c index b50b073..3c25dd5 100644 --- a/tests/safe_tests.c +++ b/tests/safe_tests.c @@ -13,7 +13,7 @@ TEST(test_safe_malloc) { void *p = safe_malloc(1024); test_assert(p != NULL, "Failed to malloc 1KiB"); - suppress_output(stderr); + suppress_output(STDERR_FILENO); test_assert_exit(malloc_exit, EXIT_FAILURE); unsuppress_outputs(); } @@ -26,7 +26,7 @@ TEST(test_safe_calloc) { void *p = safe_calloc(16, 16); test_assert(p != NULL, "Failed to calloc 16 items of 16 bytes"); - suppress_output(stderr); + suppress_output(STDERR_FILENO); test_assert_exit(calloc_exit, EXIT_FAILURE); unsuppress_outputs(); } @@ -44,7 +44,7 @@ TEST(test_safe_realloc) { p = safe_realloc(p, 4096); test_assert(p != NULL, "Failed to realloc 1KiB to 4KiB"); - suppress_output(stderr); + suppress_output(STDERR_FILENO); test_assert_exit(realloc_exit, EXIT_FAILURE); unsuppress_outputs(); } @@ -57,7 +57,7 @@ TEST(test_safe_fopen) { FILE *fp = safe_fopen(__FILE__, "r"); test_assert(fp != NULL, "Failed to fopen current file"); - suppress_output(stderr); + suppress_output(STDERR_FILENO); test_assert_exit(fopen_exit, EXIT_FAILURE); unsuppress_outputs(); } @@ -76,7 +76,7 @@ TEST(test_safe_dup) { test_assert(fd != -1, "Failed to dup STDOUT_FILENO"); close(fd); - suppress_output(stderr); + suppress_output(STDERR_FILENO); test_assert_exit(dup_exit, EXIT_FAILURE); unsuppress_outputs(); } @@ -96,7 +96,7 @@ TEST(test_safe_dup2) { test_assert(res != -1, "Failed to restore STDOUT_FILENO"); close(fd); - suppress_output(stderr); + suppress_output(STDERR_FILENO); test_assert_exit(dup2_exit, EXIT_FAILURE); unsuppress_outputs(); } diff --git a/tests/string_tests.c b/tests/string_tests.c index eaa9498..8ca0dcd 100644 --- a/tests/string_tests.c +++ b/tests/string_tests.c @@ -41,7 +41,7 @@ TEST(ss_print_helper) { } TEST(printing) { - test_assert_out(ss_print_helper, stdout, "test"); + test_assert_out(ss_print_helper, STDOUT_FILENO, "test"); StringSlice ss = ss_from_str("slice"); char buf[16]; |
