t/unit-tests: update clar to 206accb

Update clar from: - 1516124 (Merge pull request #97 from pks-t/pks-whitespace-fixes, 2024-08-15). To: - 206accb (Merge pull request #108 from pks-t/pks-uclibc-without-wchar, 2024-10-21) This update includes a bunch of fixes and improvements that we have discussed in Git when initial support for clar was merged: - There is a ".editorconfig" file now. - Compatibility with Windows has been improved so that the clar compiles on this platform without an issue. This has been tested with Cygwin, MinGW and Microsoft Visual Studio. - clar now uses CMake. This does not impact us at all as we wire up the clar into our own build infrastructure anyway. This conversion was done such that we can easily run CI jobs against Windows. - Allocation failures are now checked for consistently. - We now define feature test macros in "clar.c", which fixes compilation on some platforms that didn't previously pull in non-standard functions like lstat(3p) or strdup(3p). This was reported by a user of OpenSUSE Leap. - We stop using `struct timezone`, which is undefined behaviour nowadays and results in a compilation error on some platforms. - We now use the combination of mktemp(3) and mkdir(3) on SunOS, same as we do on NonStop. - We now support uClibc without support for <wchar.h>. The most important bits here are the improved platform compatibility with Windows, OpenSUSE, SunOS and uClibc. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Taylor Blau <me@ttaylorr.com>

Patrick Steinhardt committed Oct 21, 2024 at 12:56 UTC 7d5f18a901a7b2c2fd4722b0dbc915b97e61787b
11 files changed +189 -124
t/unit-tests/clar/.editorconfig new
+13
@@ -0,0 +1,13 @@
1 +root = true
2 +
3 +[*]
4 +charset = utf-8
5 +insert_final_newline = true
6 +
7 +[*.{c,h}]
8 +indent_style = tab
9 +tab_width = 8
10 +
11 +[CMakeLists.txt]
12 +indent_style = tab
13 +tab_width = 8
t/unit-tests/clar/.github/workflows/ci.yml
+16 -4
@@ -10,14 +10,26 @@ jobs:
10 build:
11 strategy:
12 matrix:
13 - os: [ ubuntu-latest, macos-latest ]
13 + platform:
14 + - os: ubuntu-latest
15 + generator: Unix Makefiles
16 + - os: macos-latest
17 + generator: Unix Makefiles
18 + - os: windows-latest
19 + generator: Visual Studio 17 2022
20 + - os: windows-latest
21 + generator: MSYS Makefiles
22 + - os: windows-latest
23 + generator: MinGW Makefiles
24
15 - runs-on: ${{ matrix.os }}
25 + runs-on: ${{ matrix.platform.os }}
26
27 steps:
28 - name: Check out
29 uses: actions/checkout@v2
30 - name: Build
31 run: |
22 - cd test
23 - make
32 + mkdir build
33 + cd build
34 + cmake .. -G "${{matrix.platform.generator}}"
35 + cmake --build .
t/unit-tests/clar/.gitignore new
+1
@@ -0,0 +1 @@
1 +/build/
t/unit-tests/clar/CMakeLists.txt new
+28
@@ -0,0 +1,28 @@
1 +cmake_minimum_required(VERSION 3.16..3.29)
2 +
3 +project(clar LANGUAGES C)
4 +
5 +option(BUILD_TESTS "Build test executable" ON)
6 +
7 +add_library(clar INTERFACE)
8 +target_sources(clar INTERFACE
9 + clar.c
10 + clar.h
11 + clar/fixtures.h
12 + clar/fs.h
13 + clar/print.h
14 + clar/sandbox.h
15 + clar/summary.h
16 +)
17 +set_target_properties(clar PROPERTIES
18 + C_STANDARD 90
19 + C_STANDARD_REQUIRED ON
20 + C_EXTENSIONS OFF
21 +)
22 +
23 +if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
24 + include(CTest)
25 + if(BUILD_TESTING)
26 + add_subdirectory(test)
27 + endif()
28 +endif()
t/unit-tests/clar/clar.c
+71 -56
@@ -4,7 +4,12 @@
4 * This file is part of clar, distributed under the ISC license.
5 * For full terms see the included COPYING file.
6 */
7 -#include <assert.h>
7 +
8 +#define _BSD_SOURCE
9 +#define _DARWIN_C_SOURCE
10 +#define _DEFAULT_SOURCE
11 +
12 +#include <errno.h>
13 #include <setjmp.h>
14 #include <stdlib.h>
15 #include <stdio.h>
@@ -13,11 +18,22 @@
18 #include <stdarg.h>
19 #include <wchar.h>
20 #include <time.h>
21 +#include <inttypes.h>
22
23 /* required for sandboxing */
24 #include <sys/types.h>
25 #include <sys/stat.h>
26
27 +#if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_WCHAR__)
28 + /*
29 + * uClibc can optionally be built without wchar support, in which case
30 + * the installed <wchar.h> is a stub that only defines the `whar_t`
31 + * type but none of the functions typically declared by it.
32 + */
33 +#else
34 +# define CLAR_HAVE_WCHAR
35 +#endif
36 +
37 #ifdef _WIN32
38 # define WIN32_LEAN_AND_MEAN
39 # include <windows.h>
@@ -28,6 +44,9 @@
44
45 # ifndef stat
46 # define stat(path, st) _stat(path, st)
47 + typedef struct _stat STAT_T;
48 +# else
49 + typedef struct stat STAT_T;
50 # endif
51 # ifndef mkdir
52 # define mkdir(path, mode) _mkdir(path)
@@ -60,30 +79,11 @@
79 # else
80 # define p_snprintf snprintf
81 # endif
63 -
64 -# ifndef PRIuZ
65 -# define PRIuZ "Iu"
66 -# endif
67 -# ifndef PRIxZ
68 -# define PRIxZ "Ix"
69 -# endif
70 -
71 -# if defined(_MSC_VER) || (defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR))
72 - typedef struct stat STAT_T;
73 -# else
74 - typedef struct _stat STAT_T;
75 -# endif
82 #else
83 # include <sys/wait.h> /* waitpid(2) */
84 # include <unistd.h>
85 # define _MAIN_CC
86 # define p_snprintf snprintf
81 -# ifndef PRIuZ
82 -# define PRIuZ "zu"
83 -# endif
84 -# ifndef PRIxZ
85 -# define PRIxZ "zx"
86 -# endif
87 typedef struct stat STAT_T;
88 #endif
89
@@ -102,7 +102,7 @@ fixture_path(const char *base, const char *fixture_name);
102 struct clar_error {
103 const char *file;
104 const char *function;
105 - size_t line_number;
105 + uintmax_t line_number;
106 const char *error_msg;
107 char *description;
108
@@ -195,11 +195,12 @@ static void clar_print_shutdown(int test_count, int suite_count, int error_count
195 static void clar_print_error(int num, const struct clar_report *report, const struct clar_error *error);
196 static void clar_print_ontest(const char *suite_name, const char *test_name, int test_number, enum cl_test_status failed);
197 static void clar_print_onsuite(const char *suite_name, int suite_index);
198 +static void clar_print_onabortv(const char *msg, va_list argp);
199 static void clar_print_onabort(const char *msg, ...);
200
201 /* From clar_sandbox.c */
202 static void clar_unsandbox(void);
202 -static int clar_sandbox(void);
203 +static void clar_sandbox(void);
204
205 /* From summary.h */
206 static struct clar_summary *clar_summary_init(const char *filename);
@@ -218,6 +219,15 @@ static int clar_summary_shutdown(struct clar_summary *fp);
219 _clar.trace_payload); \
220 } while (0)
221
222 +static void clar_abort(const char *msg, ...)
223 +{
224 + va_list argp;
225 + va_start(argp, msg);
226 + clar_print_onabortv(msg, argp);
227 + va_end(argp);
228 + exit(-1);
229 +}
230 +
231 void cl_trace_register(cl_trace_cb *cb, void *payload)
232 {
233 _clar.pfn_trace_cb = cb;
@@ -271,9 +281,7 @@ static double clar_time_diff(clar_time *start, clar_time *end)
281
282 static void clar_time_now(clar_time *out)
283 {
274 - struct timezone tz;
275 -
276 - gettimeofday(out, &tz);
284 + gettimeofday(out, NULL);
285 }
286
287 static double clar_time_diff(clar_time *start, clar_time *end)
@@ -386,7 +394,8 @@ clar_run_suite(const struct clar_suite *suite, const char *filter)
394
395 _clar.active_test = test[i].name;
396
389 - report = calloc(1, sizeof(struct clar_report));
397 + if ((report = calloc(1, sizeof(*report))) == NULL)
398 + clar_abort("Failed to allocate report.\n");
399 report->suite = _clar.active_suite;
400 report->test = _clar.active_test;
401 report->test_number = _clar.tests_ran;
@@ -479,9 +488,10 @@ clar_parse_args(int argc, char **argv)
488
489 switch (action) {
490 case 's': {
482 - struct clar_explicit *explicit =
483 - calloc(1, sizeof(struct clar_explicit));
484 - assert(explicit);
491 + struct clar_explicit *explicit;
492 +
493 + if ((explicit = calloc(1, sizeof(*explicit))) == NULL)
494 + clar_abort("Failed to allocate explicit test.\n");
495
496 explicit->suite_idx = j;
497 explicit->filter = argument;
@@ -505,10 +515,8 @@ clar_parse_args(int argc, char **argv)
515 }
516 }
517
508 - if (!found) {
509 - clar_print_onabort("No suite matching '%s' found.\n", argument);
510 - exit(-1);
511 - }
518 + if (!found)
519 + clar_abort("No suite matching '%s' found.\n", argument);
520 break;
521 }
522
@@ -540,11 +548,17 @@ clar_parse_args(int argc, char **argv)
548 case 'r':
549 _clar.write_summary = 1;
550 free(_clar.summary_filename);
543 - _clar.summary_filename = *(argument + 2) ? strdup(argument + 2) : NULL;
551 + if (*(argument + 2)) {
552 + if ((_clar.summary_filename = strdup(argument + 2)) == NULL)
553 + clar_abort("Failed to allocate summary filename.\n");
554 + } else {
555 + _clar.summary_filename = NULL;
556 + }
557 break;
558
559 default:
547 - assert(!"Unexpected commandline argument!");
560 + clar_abort("Unexpected commandline argument '%s'.\n",
561 + argument[1]);
562 }
563 }
564 }
@@ -566,22 +580,18 @@ clar_test_init(int argc, char **argv)
580 if (!_clar.summary_filename &&
581 (summary_env = getenv("CLAR_SUMMARY")) != NULL) {
582 _clar.write_summary = 1;
569 - _clar.summary_filename = strdup(summary_env);
583 + if ((_clar.summary_filename = strdup(summary_env)) == NULL)
584 + clar_abort("Failed to allocate summary filename.\n");
585 }
586
587 if (_clar.write_summary && !_clar.summary_filename)
573 - _clar.summary_filename = strdup("summary.xml");
588 + if ((_clar.summary_filename = strdup("summary.xml")) == NULL)
589 + clar_abort("Failed to allocate summary filename.\n");
590
575 - if (_clar.write_summary &&
576 - !(_clar.summary = clar_summary_init(_clar.summary_filename))) {
577 - clar_print_onabort("Failed to open the summary file\n");
578 - exit(-1);
579 - }
591 + if (_clar.write_summary)
592 + _clar.summary = clar_summary_init(_clar.summary_filename);
593
581 - if (clar_sandbox() < 0) {
582 - clar_print_onabort("Failed to sandbox the test runner.\n");
583 - exit(-1);
584 - }
594 + clar_sandbox();
595 }
596
597 int
@@ -615,10 +625,9 @@ clar_test_shutdown(void)
625
626 clar_unsandbox();
627
618 - if (_clar.write_summary && clar_summary_shutdown(_clar.summary) < 0) {
619 - clar_print_onabort("Failed to write the summary file\n");
620 - exit(-1);
621 - }
628 + if (_clar.write_summary && clar_summary_shutdown(_clar.summary) < 0)
629 + clar_abort("Failed to write the summary file '%s: %s.\n",
630 + _clar.summary_filename, strerror(errno));
631
632 for (explicit = _clar.explicit; explicit; explicit = explicit_next) {
633 explicit_next = explicit->next;
@@ -649,7 +658,7 @@ static void abort_test(void)
658 {
659 if (!_clar.trampoline_enabled) {
660 clar_print_onabort(
652 - "Fatal error: a cleanup method raised an exception.");
661 + "Fatal error: a cleanup method raised an exception.\n");
662 clar_report_errors(_clar.last_report);
663 exit(-1);
664 }
@@ -673,7 +682,10 @@ void clar__fail(
682 const char *description,
683 int should_abort)
684 {
676 - struct clar_error *error = calloc(1, sizeof(struct clar_error));
685 + struct clar_error *error;
686 +
687 + if ((error = calloc(1, sizeof(*error))) == NULL)
688 + clar_abort("Failed to allocate error.\n");
689
690 if (_clar.last_report->errors == NULL)
691 _clar.last_report->errors = error;
@@ -688,8 +700,9 @@ void clar__fail(
700 error->line_number = line;
701 error->error_msg = error_msg;
702
691 - if (description != NULL)
692 - error->description = strdup(description);
703 + if (description != NULL &&
704 + (error->description = strdup(description)) == NULL)
705 + clar_abort("Failed to allocate description.\n");
706
707 _clar.total_errors++;
708 _clar.last_report->status = CL_TEST_FAILURE;
@@ -763,6 +776,7 @@ void clar__assert_equal(
776 }
777 }
778 }
779 +#ifdef CLAR_HAVE_WCHAR
780 else if (!strcmp("%ls", fmt)) {
781 const wchar_t *wcs1 = va_arg(args, const wchar_t *);
782 const wchar_t *wcs2 = va_arg(args, const wchar_t *);
@@ -798,8 +812,9 @@ void clar__assert_equal(
812 }
813 }
814 }
801 - else if (!strcmp("%"PRIuZ, fmt) || !strcmp("%"PRIxZ, fmt)) {
802 - size_t sz1 = va_arg(args, size_t), sz2 = va_arg(args, size_t);
815 +#endif /* CLAR_HAVE_WCHAR */
816 + else if (!strcmp("%"PRIuMAX, fmt) || !strcmp("%"PRIxMAX, fmt)) {
817 + uintmax_t sz1 = va_arg(args, uintmax_t), sz2 = va_arg(args, uintmax_t);
818 is_equal = (sz1 == sz2);
819 if (!is_equal) {
820 int offset = p_snprintf(buf, sizeof(buf), fmt, sz1);
t/unit-tests/clar/clar/print.h
+8 -3
@@ -21,7 +21,7 @@ static void clar_print_clap_error(int num, const struct clar_report *report, con
21 {
22 printf(" %d) Failure:\n", num);
23
24 - printf("%s::%s [%s:%"PRIuZ"]\n",
24 + printf("%s::%s [%s:%"PRIuMAX"]\n",
25 report->suite,
26 report->test,
27 error->file,
@@ -136,7 +136,7 @@ static void clar_print_tap_ontest(const char *suite_name, const char *test_name,
136
137 printf(" at:\n");
138 printf(" file: '"); print_escaped(error->file); printf("'\n");
139 - printf(" line: %" PRIuZ "\n", error->line_number);
139 + printf(" line: %" PRIuMAX "\n", error->line_number);
140 printf(" function: '%s'\n", error->function);
141 printf(" ---\n");
142
@@ -202,10 +202,15 @@ static void clar_print_onsuite(const char *suite_name, int suite_index)
202 PRINT(onsuite, suite_name, suite_index);
203 }
204
205 +static void clar_print_onabortv(const char *msg, va_list argp)
206 +{
207 + PRINT(onabort, msg, argp);
208 +}
209 +
210 static void clar_print_onabort(const char *msg, ...)
211 {
212 va_list argp;
213 va_start(argp, msg);
209 - PRINT(onabort, msg, argp);
214 + clar_print_onabortv(msg, argp);
215 va_end(argp);
216 }
t/unit-tests/clar/clar/sandbox.h
+8 -9
@@ -122,14 +122,14 @@ static int build_sandbox_path(void)
122
123 if (mkdir(_clar_path, 0700) != 0)
124 return -1;
125 -#elif defined(__TANDEM)
126 - if (mktemp(_clar_path) == NULL)
125 +#elif defined(_WIN32)
126 + if (_mktemp_s(_clar_path, sizeof(_clar_path)) != 0)
127 return -1;
128
129 if (mkdir(_clar_path, 0700) != 0)
130 return -1;
131 -#elif defined(_WIN32)
132 - if (_mktemp_s(_clar_path, sizeof(_clar_path)) != 0)
131 +#elif defined(__sun) || defined(__TANDEM)
132 + if (mktemp(_clar_path) == NULL)
133 return -1;
134
135 if (mkdir(_clar_path, 0700) != 0)
@@ -142,15 +142,14 @@ static int build_sandbox_path(void)
142 return 0;
143 }
144
145 -static int clar_sandbox(void)
145 +static void clar_sandbox(void)
146 {
147 if (_clar_path[0] == '\0' && build_sandbox_path() < 0)
148 - return -1;
148 + clar_abort("Failed to build sandbox path.\n");
149
150 if (chdir(_clar_path) != 0)
151 - return -1;
152 -
153 - return 0;
151 + clar_abort("Failed to change into sandbox directory '%s': %s.\n",
152 + _clar_path, strerror(errno));
153 }
154
155 const char *clar_sandbox_path(void)
t/unit-tests/clar/clar/summary.h
+5 -9
@@ -66,16 +66,12 @@ struct clar_summary *clar_summary_init(const char *filename)
66 struct clar_summary *summary;
67 FILE *fp;
68
69 - if ((fp = fopen(filename, "w")) == NULL) {
70 - perror("fopen");
71 - return NULL;
72 - }
69 + if ((fp = fopen(filename, "w")) == NULL)
70 + clar_abort("Failed to open the summary file '%s': %s.\n",
71 + filename, strerror(errno));
72
74 - if ((summary = malloc(sizeof(struct clar_summary))) == NULL) {
75 - perror("malloc");
76 - fclose(fp);
77 - return NULL;
78 - }
73 + if ((summary = malloc(sizeof(struct clar_summary))) == NULL)
74 + clar_abort("Failed to allocate summary.\n");
75
76 summary->filename = filename;
77 summary->fp = fp;
t/unit-tests/clar/test/.gitignore deleted
-4
@@ -1,4 +0,0 @@
1 -clar.suite
2 -.clarcache
3 -clar_test
4 -*.o
t/unit-tests/clar/test/CMakeLists.txt new
+39
@@ -0,0 +1,39 @@
1 +find_package(Python COMPONENTS Interpreter REQUIRED)
2 +
3 +add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/clar.suite"
4 + COMMAND "${Python_EXECUTABLE}" "${CMAKE_SOURCE_DIR}/generate.py" --output "${CMAKE_CURRENT_BINARY_DIR}"
5 + DEPENDS main.c sample.c clar_test.h
6 + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
7 +)
8 +
9 +add_executable(clar_test)
10 +set_target_properties(clar_test PROPERTIES
11 + C_STANDARD 90
12 + C_STANDARD_REQUIRED ON
13 + C_EXTENSIONS OFF
14 +)
15 +
16 +# MSVC generates all kinds of warnings. We may want to fix these in the future
17 +# and then unconditionally treat warnings as errors.
18 +if(NOT MSVC)
19 + set_target_properties(clar_test PROPERTIES
20 + COMPILE_WARNING_AS_ERROR ON
21 + )
22 +endif()
23 +
24 +target_sources(clar_test PRIVATE
25 + main.c
26 + sample.c
27 + "${CMAKE_CURRENT_BINARY_DIR}/clar.suite"
28 +)
29 +target_compile_definitions(clar_test PRIVATE
30 + CLAR_FIXTURE_PATH="${CMAKE_CURRENT_SOURCE_DIR}/resources/"
31 +)
32 +target_compile_options(clar_test PRIVATE
33 + $<IF:$<CXX_COMPILER_ID:MSVC>,/W4,-Wall>
34 +)
35 +target_include_directories(clar_test PRIVATE
36 + "${CMAKE_SOURCE_DIR}"
37 + "${CMAKE_CURRENT_BINARY_DIR}"
38 +)
39 +target_link_libraries(clar_test clar)
t/unit-tests/clar/test/Makefile deleted
-39
@@ -1,39 +0,0 @@
1 -#
2 -# Copyright (c) Vicent Marti. All rights reserved.
3 -#
4 -# This file is part of clar, distributed under the ISC license.
5 -# For full terms see the included COPYING file.
6 -#
7 -
8 -#
9 -# Set up the path to the clar sources and to the fixtures directory
10 -#
11 -# The fixture path needs to be an absolute path so it can be used
12 -# even after we have chdir'ed into the test directory while testing.
13 -#
14 -CURRENT_MAKEFILE := $(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST))
15 -TEST_DIRECTORY := $(abspath $(dir $(CURRENT_MAKEFILE)))
16 -CLAR_PATH := $(dir $(TEST_DIRECTORY))
17 -CLAR_FIXTURE_PATH := $(TEST_DIRECTORY)/resources/
18 -
19 -CFLAGS=-g -I.. -I. -Wall -DCLAR_FIXTURE_PATH=\"$(CLAR_FIXTURE_PATH)\"
20 -
21 -.PHONY: clean
22 -
23 -# list the objects that go into our test
24 -objects = main.o sample.o
25 -
26 -# build the test executable itself
27 -clar_test: $(objects) clar_test.h clar.suite $(CLAR_PATH)clar.c
28 - $(CC) $(CFLAGS) -o $@ "$(CLAR_PATH)clar.c" $(objects)
29 -
30 -# test object files depend on clar macros
31 -$(objects) : $(CLAR_PATH)clar.h
32 -
33 -# build the clar.suite file of test metadata
34 -clar.suite:
35 - python "$(CLAR_PATH)generate.py" .
36 -
37 -# remove all generated files
38 -clean:
39 - $(RM) -rf *.o clar.suite .clarcache clar_test clar_test.dSYM